オープンクラス
- 標準クラスにmethod追加できる
require 'test/unit'
def to_alphanumeric(s)
s.gsub /[^\w\s]/, '' # [a-zA-Z0-9_空白]以外
end
class ToAlphanumericTest < Test::Unit::TestCase
def test_steps_non_alphanumeric_characters
assert_equal '3 the Magic Number', to_alphanumeric('#3, the ***Magic, Number*?')
end
end
require 'test/unit'
class String
def to_alphanumeric
gsub /[^\w\s]/, ''
end
end
class ToAlphanumericTest < Test::Unit::TestCase
def test_steps_non_alphanumeric_characters
assert_equal '3 the Magic Number', '#3, the ***Magic, Number*?'.to_alphanumeric
end
end
- method検索
[].methods.grep /^re/
# [:repeated_permutation, :repeated_combination, :reject, :reverse_each, :replace, :reverse, :reverse!, :reject!, :reduce, :remove_instance_variable, :respond_to?]
"".methods.grep /^re/
[:replace, :reverse, :reverse!, :remove_instance_variable, :respond_to?]
Objectとinstance変数
- objectのinstance変数はobjectにある(@vはそれぞれのobjectにある)
- objectのmethodはclassにある(my_methodはMyClassにある)
- instance変数は値が代入された時に初めて出現する
class MyClass
def my_method(v)
@v = v
end
end
obj1 = MyClass.new.my_method(5)
obj2 = MyClass.new.my_method(7)
obj3 = MyClass.new
obj3.instance_variables
# []
obj3.my_method
obj3.instance_variables
# [:@v]
オブジェクトはinstance変数とclassへのリンクで構成されている
オブジェクトのmethodはclassに住んでいる。classから見ればinstance methodと呼ばれる
classはClassクラスのオブジェクト。クラス名は単なる定数。
ClassはModuleのサブクラス
- module: methodをまとめたもの
- class: newしたり、superclassで階層構造を作れる
moduleやclassの名前がディレクトリ、定数がファイルのような感じ
module MyModule
MyConstant = 'out 定数'
class MyClass
MyConstant = 'innner 定数'
end
end
MyModule::MyConstant
# "out 定数"
MyModule::MyClass::MyConstant
# "inner 定数"
require, load method
require: ファイルを一度だけ読み込む
load: 呼び出すたびにファイルを実行する
require './a.rb'
# loadは定数が変数を汚染する、防ぐためにtrueオプション
load('a.rb', true)
classはBasicObjectまでの継承チェーンを持っている
method探索
- レシーバーのクラスに入り、メソッドを見つけるまで継承チェーンを上る
- レシーバ:
obj.my_method
の場合obj
がレシーバ - 継承チェーン: クラスをsuperclassに向かってたどる
class MyClass
def my_method
@v = 1
end
end
class SubClass < MyClass
end
obj = SubClass.new
obj.my_method
SubClass.ancestors
# SubClass, MyClass, Object, Kernel, BasicObject]
classにmoduleをinclude, prepend
- include: includeするclassの真上に入る
- prepend: prependするclassの真下に入る
module M1
def my_method
@v = 1
end
end
class C
include M1
end
class C1
prepend M1
end
class D < C
end
D.ancestors
# [D, C, M1, Object, Kernel, BasicObject]
class E < C1
end
E.ancestors
# [E, M1, C1, Object, Kernel, BasicObject]
moduleがすでにチェーンに含まれていたら2回目は無視する
- M3にM1がprepend [M1, M3]
- M3にM2がinclude [M1, M3, M2]
- M2にM1がincludeだが、M1はすでにチェーンにあるため無視
module M1; end
module M2
include M1
end
module M3
prepend M1
include M2
end
M3.ancestors
# [M1, M3, M2]
Kernel
- どこからでも呼び出せるprint methodはObject classがKernel moduleをincludesしているから
- Kernelにmethodを追加すればどこからでも使えるようになる
require 'awesome_print'
local_time = { :city => "Roma", :now => Time.now }
ap local_time, :indent => 2
# {
# :city => "Roma",
# :now => 2017-03-29 21:32:45 +0900
# }
# awesome_printがKernelにap methodを追加している
methodを呼び出すときはレシーバがselfになる
- コードはcurrent object内部(self)で実行される
- methodを呼び出すときはmethodのレシーバがself
- その時点から全てのinstance変数はselfのinstance変数になり、レシーバを明示しないmethod呼び出しは全てselfに対する呼び出しになる
- 最後にmethodのレシーバになったobjectを追いかけると流れがわかりやすい
class MyClass
def testing_self
@var = 10
my_method
self
puts @var
end
def my_method
@var = @var + 1
end
end
obj = MyClass.new
obj.testing_self
# #<MyClass:0x007f83542e7cd0 @var=11>
# testing_selfを呼び出すとレシーバであるobjがselfになる
# そのためinstance変数@varはobjのinstance変数となる
# レシーバを明示していないmy_methodの呼び出しもobjに対するものとなる
# my_method時もselfはobjなので@varはobjのinstance変数のまま
# よって、@varは+1される
トップレベル
- methodを呼び出してない、または呼び出したmethodが全て戻った時のselfはmain
self # main
self.class # Object
Refinements
- クラスのコードにパッチを当てて、通常のメソッド探索をオーバーライドする感じ
- Refinementsはusingを呼び出したところから、ファイルやモジュールの定義が終わるところまでの限られた部分でのみ有効になる
module StringExtensions
refine String do
def to_alphanumeric
gsub(/[^\w\s]/, '')
end
end
end
"my *1st* refinement!".to_alphanumeric
# NoMethodError
using StringExtensions
"my *1st* refinement!".to_alphanumeric
# "my 1st refinement!"
module StringExtensions
refine String do
def reverse
"esrever"
end
end
end
module StringStuff
using StringExtensions
"my *1st* refinement!".reverse
# "esrever"
end
"my *1st* refinement!".reverse
# "!tnemenifer *ts1* ym"
Documentのprintを実行したい場合
- どちらかのprintの名前を変える
- includeの順番を変える
module Printable
def print
puts 1
end
def prepare_cover
puts 2
end
end
module Document
def print_to_screen
prepare_cover # 2 Printableで発見
format_for_screen # 3 Documentで発見
print # 1
end
def format_for_screen
puts 3
end
def print
puts 4
end
end
class Book
include Document # Bookの真上に入る
include Printable # Bookの真上に入る
puts 5
end
# Book > Printable > Document > Object > Kernel > Basic Object
b = Book.new
b.print_to_screen
# 5 2 3 1
Book.ancestors
# [Book, Printable, Document, Object, Kernel,
BasicObject]
b.method(:print).owner
# Printable
Ctrl + L
pry画面クリア