内容
Ruby技術者認定試験のGold資格取得のための振り返り記事になります。
前提
Ruby3.1系
学習教材
- REx
- ruby-association
- CTC
- まとめ記事
目次
objectクラス関連
module関連
self関連
探索順
enumeratorクラス関連
procオブジェクト
例外処理関連
気になるメソッド
演算子関連
日付関連
正規表現関連
制御構造関連
パーセント記法
rubyオプション一覧
その他
Objectクラス関連
-
Objectクラスにメソッドを定義すると特異クラスでもそのメソッドを利用することができるclass Object CONST = "1" def const_succ CONST.succ! end end class Child1 const_succ # "2"になる class << self const_succ # 特異クラス内のメソッドも実行されるため、"3"になる end end class Child2 const_succ # "4"になる def initialize const_succ end end Child1.new # "4"のまま Child2.new # "5"になる p Object::CONST # => "5"
singleton_class
-
レシーバの特異クラスを返す。また、特異クラスが存在しなければ新しく作成する
- レシーバが
Integer、Float、Symbolの場合にTypeErrorが発生する
Object.new.singleton_class # => #<Class:#<Object:0xb7ce1e24>> String.singleton_class # => #<Class:String> nil.singleton_class # => NilClass true.singleton_class # => TrueClass false.singleton_class # => FalseClass 1.singleton_class # `singleton_class': can't define singleton (TypeError)- 特異クラスで
selfを参照するとレシーバのオブジェクトを返す
class Hoge def self._singleton class << Hoge self end end end p Hoge._singleton # => #<Class:Hoge> - レシーバが
method
- オブジェクトに対してメソッド名を引数として
methodを呼び出すと、当該メソッドをオブジェクト化したMethodオブジェクトを作って返す- メソッド名は
StringまたはSymbolで指定 - 定義されていないメソッド名を引数として与えると
NameErrorが発生する
arr = [1, 2, 3] arr_pop = arr.method(:pop) arr_pop.call # => 3 arr # => [1, 2] arr_pop.call # => 2 arr_pop.call # => 1 arr # => [] - メソッド名は
methods
- そのオブジェクトに対して呼び出せる
publicおよびprotectedメソッド名の一覧を配列で返す-
privateメソッドは含まれない - 引数が
falseの場合、Object#singleton_methods(false)と挙動は同じになり、特異メソッド名の一覧を返す- 引数を指定しない場合、デフォルトで
trueが指定される
- 引数を指定しない場合、デフォルトで
class Parent private; def private_parent() end protected; def protected_parent() end public; def public_parent() end end class Foo < Parent private; def private_foo() end protected; def protected_foo() end public; def public_foo() end end obj = Foo.new class << obj private; def private_singleton() end protected; def protected_singleton() end public; def public_singleton() end end p obj.methods(false) => [:public_singleton, :protected_singleton] # Objectクラスのインスタンスメソッドは除外する p obj.methods(true) - Object.instance_methods(true) # => [:public_singleton, :protected_singleton, :protected_foo, :public_foo, :protected_parent, :public_parent] -
instance_methods
- そのクラス/モジュールで定義されている
publicおよびprotectedメソッド名の一覧を配列で返す- 引数に
falseを指定するとそのモジュールで定義されているメソッドのみ返す- 引数を指定しない場合、デフォルトで
trueが指定される
- 引数を指定しない場合、デフォルトで
class Foo private; def private_foo() end protected; def protected_foo() end public; def public_foo() end end # あるクラスのインスタンスメソッドの一覧を得る p Foo.instance_methods(false) # => [:protected_foo, :public_foo] p Foo.public_instance_methods(false) # => [:public_foo] p Foo.private_instance_methods(false) # => [:private_foo] p Foo.protected_instance_methods(false) # => [:protected_foo] class Bar < Foo private; def private_foo2() end protected; def protected_foo2() end public; def public_foo2() end end # Object のインスタンスメソッドは一覧から排除している前提( - Object.instance_methods(false)) p Bar.instance_methods(false) # => [:protected_foo2, :public_foo2] p Bar.instance_methods(true) # => [:protected_foo2, :public_foo2, :protected_foo, :public_foo] - 引数に
initialize
- ユーザ定義クラスのオブジェクト初期化メソッド
-
publicなどでアクセス修飾子をつけたとしても、privateから変わることはないclass C public def initialize end end p C.new.private_methods.include? :initialize # => true
-
instance_eval
- オブジェクトの特異クラスにインスタンスメソッドを定義することができる
-
instance_evalにブロックを渡した場合のネスト状態class C CONST = "Hello, world" class << self CONST = "Hello, world!" end end module M C.instance_eval do CONST = "Hello, world!!" # instance_eval内でCONST定数を定義しないとNameErrorとなる def awesome_method CONST end end end p C.awesome_method # => "Hello, world!!" -
instance_evalに文字列を渡した場合のネスト状態class C CONST = "Hello, world" class << self # instance_eval内に定数が定義されていなければ、この定数が呼ばれる CONST = "Hello, world!" end end module M C.instance_eval(<<-CODE) CONST = "Hello, world!!" def awesome_method CONST end CODE end p C.awesome_method # => "Hello, world!!"
-
class_eval
- ブロックをあたかもクラス定義やモジュール定義の中にあるかのように実行することができる
- エイリアスメソッド:
module_eval -
class_evalにブロックを渡した場合のネスト状態class C CONST = "Hello, world" end module M CONST = "Hello, world!!" # モジュールMにCONST定数の定義がない場合、NameErrorとなる C.class_eval do def awesome_method CONST end end end p C.new.awesome_method # => "Hello, world!!" -
class_evalに文字列を渡した場合のネスト状態class C # クラスCにCONST定数の定義がない場合、モジュールMのCONST定数が参照される CONST = "Hello, world" end module M CONST = "Hello, world!!" C.class_eval(<<-CODE) def awesome_method CONST end CODE end p C.new.awesome_method # => "Hello, world"
- エイリアスメソッド:
instance_variable_set
- オブジェクトのインスタンス変数「
var」に値「value」を設定する- 第1引数に
var、第2引数にvalueを設定- 引数が正しく設定されていないと
ArgumentErrorが発生する
- 引数が正しく設定されていないと
- インスタンス変数が定義されていなければ新たに定義される
obj = Object.new p obj.instance_variable_set("@foo", 1) # => 1 p obj.instance_variable_set(:@foo, 2) # => 2 p obj.instance_variable_set(:@hoge) # => instance_variable_set': wrong number of arguments (given 1, expected 2) (ArgumentError) - 第1引数に
instance_variable_get
- オブジェクトのインスタンス変数の値を取得して返す
- インスタンス変数が定義されていなければ
nilを返す - インスタンス変数名は文字列かシンボルで指定
obj = Object.new p obj.instance_variable_set("@foo", 2) # => 2 p obj.instance_variable_get(:@foo) # => 2 p obj.instance_variable_get("@bar") # => nil - インスタンス変数が定義されていなければ
method_missing
- 継続チェーンを辿った末にメソッドが見つからなかった場合に呼び出される
module M def method_missing(id, *args) puts "M#method_missing" end end class A include M def method_missing(id, *args) puts "A#method_missing" end end class B < A # このmethod_missingメソッドをコメントアウトするとAクラスのmethod_missingが呼ばれる def method_missing(id, *args) puts "B#method_missing" end end obj = B.new obj.dummy_method # => B#method_missing
freeze
- オブジェクトを凍結する
- 破壊的な操作をすると
FrozenErrorが発生する- 非破壊的な操作はできる
- オブジェクトの代入はできる
# 破壊的な操作をした場合 a = ['001', '002', '003'].freeze a.map!{|id| id << 'hoge'} # => `map!': can't modify frozen Array: ["001", "002", "003"] (FrozenError) a.push('add') # => `push': can't modify frozen Array: ["001hoge", "002hoge", "003hoge"] (FrozenError) # 配列内の要素をfreezeしていないため、変更可能 b = ['001', '002', '003'].freeze b.map{|id| id << 'hoge'} # => ["001hoge", "002hoge", "003hoge"] # 非破壊的な操作はできる c = ['001', '002', '003'].freeze c.map{|id| id << 'hoge'} # => ["001hoge", "002hoge", "003hoge"] - 破壊的な操作をすると
respond_to?
- レシーバが引数で指定した
publicメソッドを持っていれば、真を返す- 第二引数に
trueを設定すると、privateメソッドとprotectedメソッドを確認の対象に含めることができる(デフォルトはfalse)
class Hoge def hoge ;end private def hogehoge ;end end hoge = Hoge.new hoge.respond_to?(:hoge) # => true hoge.respond_to?(:hogehoge) # => false hoge.respond_to?(:hogehoge, true) # => true - 第二引数に
define_method
- インスタンスメソッドを動的に定義できる
- 第一引数にメソッド名、第二引数に処理を記述する(ブロックで渡すことも可能)
define_method(:add) do |x, y| x + y end p add(1, 2) # => 3 # 第二引数に既存のメソッドを設定した場合 class Foo def foo() p "foo" end define_method(:bar, instance_method(:foo)) end Foo.new.bar # => "foo"
clone
- オブジェクトをコピーする
- 凍結状態・汚染状態・特異メソッドなどの情報も含めた完全な複製を作成
- 同様にオブジェクトをコピーする
dupメソッドは、凍結状態・汚染状態・特異メソッドはコピーしない
- 同様にオブジェクトをコピーする
obj = Object.new def obj.hello puts "Hi!" end copy = obj.clone copy.hello # => Hi! dup = obj.dup dup.hello # undefined method `hello' for #<Object:0x000000010855dd78> (NoMethodError) - 凍結状態・汚染状態・特異メソッドなどの情報も含めた完全な複製を作成
send
- オブジェクトのメソッドを
argsを引数にして呼び出し、メソッド結果を返す- 第一引数にメソッド名をシンボルで指定
- 第二引数に呼び出すメソッドに渡す引数
-
sendメソッドが再定義された場合に備えて別名__send__が用意されており、ライブラリではこちらを使っている ※1(そのため、__send__は再定義すべきではない)Rubyの標準ライブラリや多くの
gemでは、sendメソッドが特定の用途で再定義されていることがあります。sendはオブジェクトに対してメソッドを動的に呼び出すメソッドであり、一般的には安全な使い方が期待されますが、一部のコードでsendが上書きされている場合、予期しない結果が生じる可能性があります。そこで、Rubyは標準で__send__という別名を提供しています。__send__は常にsendと同じ動作をしますが、再定義されていない限りはそのままの動作をするため、安全にメソッドを呼び出すことができます。一般的なコードでは、通常はsendを使用しますが、特殊なケースやライブラリでsendが再定義されている可能性がある場合、代わりに__send__を使用することで安全性を確保できます。p -365.send(:abs) # => 365 p "ruby".send(:sub,/./,"R") # => "Ruby" p -365.__send__(:abs) # => 365 p "ruby".__send__(:sub,/./,"R") # => "Ruby"
class
- レシーバのクラスを返す
p "ruby".class # => String p 100.class # => Integer p ARGV.class # => Array p self.class # => Object p Class.class # => Class p Kernel.class # => Module class Hoge def hoge p self.class # => Hoge end def self.hoge p self.class # => Class end end module Hoge def self.hoge p self.class # => Module end end
public_send
-
publicメソッドのみを対象にメソッドの実行結果を返す- 第一引数にはメソッド名、第二引数には引数を指定する
-
protectedメソッドやprivateメソッドに対して実行した場合にNoMethodErrorが発生する - ※
private_sendやprotected_sendというメソッドは存在しない(引っ掛け問題で出題されました)
class Foo def foo(val) p val end private def bar(val) p val end def baz p 'baz' end end Foo.new.public_send(:foo, 'foo') # => "foo" Foo.new.public_send(:bar, 'bar') # => `public_send': private method `bar' called for #<Foo:0x0000000106f5dc30> (NoMethodError) # ただし、抜け道はある(参照先のメソッド名はシンボルでは動作しないため、文字列で渡す必要あり) Foo.new.public_send(:instance_eval, 'baz') # => "baz" # privateメソッドを参照する場合はsendメソッドを使用する Foo.new.send(:bar, 'bar') # => "bar"
Module関連
-
moduleのスーパークラスはObjectクラス-
同名のモジュールを別々に定義したとしてもテーブルを参照して値を取得することができる
- 同名の定数やメソッドを定義している場合、後で定義したモジュール側の内容に上書きされる
module Hoge HOGE = 'hoge' def hoge 'hoge' end def hoge2 'hoge2' end end module Hoge HOGE = 'hogehoge' def hoge 'hogehoge' end end class C include Hoge end C.new.hoge # => "hogehoge" C.new.hoge2 # => "hoge2" C::HOGE # => "hogehoge" -
モジュールをスーパークラスとして定義することはできない
- ※クラスを継承する場合、スーパークラスはクラスである必要がある
module Hoge;end class C < Hoge;end # superclass must be an instance of Class (given an instance of Module) (TypeError)
-
ancestors
-
クラス、モジュールのスーパークラスとミックスインしているモジュールを優先順位順に配列に格納して返す
module Foo end class Bar include Foo end class Baz < Bar p ancestors # => [Baz, Bar, Foo, Object, PP::ObjectMixin, Kernel, BasicObject] p included_modules # => [Foo, PP::ObjectMixin, Kernel] p superclass # => Bar end
included_modules
-
selfにミックスインしているモジュールの配列を返すmodule Mixin end module Outer include Mixin end Mixin.included_modules # => [] Outer.included_modules # => [Mixin]
include
- 引数で指定したモジュールの定数、メソッド、モジュール変数(モジュールで定義されたクラス変数)を引き継ぐ(ミックスイン)ことができる
-
メソッド探索順は
selfの後に追加されるmodule M def foo super puts "M#foo" end end class C2 def foo puts "C2#foo" end end class C < C2 def foo super puts "C#foo" end include M end C.ancestors # => [C, M, C2, Object, PP::ObjectMixin, Kernel, BasicObject] C.new.foo # => # C2#foo # M#foo # C#foo -
複数モジュールを指定した場合、左側から右側の順でメソッド探索される
module M end; module L end; module N end; class Hoge include M, L, N end Hoge.ancestors # => [Hoge, M, L, N, Object, PP::ObjectMixin, Kernel, BasicObject] -
1つずつ定義した場合、下から順に探索される
module M end; module L end; module N end; class Hoge include M include L include N end Hoge.ancestors # => [Hoge, N, L, M, Object, PP::ObjectMixin, Kernel, BasicObject]
-
prepend
-
引数で指定したモジュールを
selfの継承チェーンの先頭に「追加する」ことで、selfの定数、メソッド、モジュール変数を上書きするmodule M def foo super puts "M#foo" end end class C2 def foo puts "C2#foo" end end class C < C2 def foo super puts "C#foo" end prepend M end C.ancestors # => [M, C, C2, Object, PP::ObjectMixin, Kernel, BasicObject] C.new.foo # => C2#foo # C#foo # M#foo-
複数モジュールを指定した場合、左側から右側の順でメソッド探索される
module M end; module L end; module N end; class Hoge prepend M, L, N end Hoge.ancestors # => [M, L, N, Hoge, Object, PP::ObjectMixin, Kernel, BasicObject] -
1つずつ定義した場合、下から順に探索される
module M end; module L end; module N end; class Hoge prepend M prepend L prepend N end Hoge.ancestors # => [N, L, M, Hoge, Object, PP::ObjectMixin, Kernel, BasicObject]
-
extend
- 引数で指定したモジュールのインスタンスメソッドを
selfの特異メソッドとして追加するmodule Foo def a 'ok Foo' end end module Bar def b 'ok Bar' end end obj = Object.new obj.extend Foo, Bar p obj.a # => "ok Foo" p obj.b # => "ok Bar" class Klass include Foo extend Bar end p Klass.new.a # => "ok Foo" p Klass.b # => "ok Bar"- extend の機能は、「特異クラスに対す
include」と言い替えることもできる# obj.extend Foo, Bar とほぼ同じ class << obj include Foo, Bar end
- extend の機能は、「特異クラスに対す
included
-
includeしたモジュールのインスタンスメソッドをクラスのインスタンスメソッドだけではなく、クラスメソッドとして使用することができるmodule Mod def self.included(base) # 引数 base には includeしたクラス Car が格納される base.extend ClassMethods end module ClassMethods def run #クラスメソッドとして使いたい puts "GO!!" end end def stop # インスタンスメソッドとして使いたい puts "STOP!!" end end class Car include Mod car = Car.new car.stop # => STOP!! Car.run # => GO!! end
append_features
-
includeの実態で、オーバーライドするとincludeした際の挙動が変わるmodule M def self.append_features(klass) super # 記述しないとモジュールMがincludeされない p klass.ancestors end end class C include M end p C.ancestors # => [C, M, Object, PP::ObjectMixin, Kernel, BasicObject] # superがない場合 => [C, Object, PP::ObjectMixin, Kernel, BasicObject]
Module.nesting
-
このメソッドを呼び出した時点のクラス/モジュールのネストの状態を配列に入れて返す
module A p Module.nesting # => [A] class B p Module.nesting # => [A::B, A] end end-
xxx_evalでの渡し方の違いによる挙動の変化について- ブロックで渡した場合、クラス・モジュールのスコープに入らない
- テキストコードを与えた場合、class・moduleキーワード等でオープンしたとき相当の処理がされるため、スコープに入る
class C p Module.nesting # => [C] end C.class_eval do p Module.nesting # => [] end C.class_eval(<<~CODE) p Module.nesting # => [C] CODE C.instance_eval(<<~CODE) p Module.nesting # => [#<Class:C>] CODE
-
module_function
- メソッドをモジュール関数にする
- モジュール関数とは、
privateメソッドであると同時に特異メソッドでもあるようなメソッドを指す - 引数が与えられた場合、引数で指定されたメソッドをモジュール関数にする
- 引数なしの場合、今後このモジュール定義文内で新しく定義されるメソッドをすべてモジュール関数にする
module M def foo p "foo" end def bar p "bar" end module_function :foo end M.foo # => "foo" M.bar # <main>': undefined method `bar' for M:Module (NoMethodError) # 引数なしの場合 module M def foo p "foo" end module_function def bar p "bar" end end M.foo # undefined method `foo' for M:Module (NoMethodError) M.bar # => "bar" - クラス内で
includeして使用する場合module M def foo p "foo" end module_function :foo end class C include M def bar foo end end c = C.new c.bar # => "foo" # privateメソッドのため、オブジェクトからは呼ぶとエラーになる c.foo # private method `foo' called for #<C:0x0000000104766c68> (NoMethodError) - モジュール関数に別名を付ける場合、先に別名を定義してからモジュール関数にする必要がある
# モジュール関数の別名は定義できないため、エラーになる module M def foo p "foo" end module_function :foo alias bar foo end M.foo # => "foo" M.bar # => undefined method `bar' for Foo:Module (NoMethodError) # モジュール関数に別名を付ける場合 module M def foo p "foo" end alias bar foo module_function :foo, :bar end M.foo # => "foo" M.bar # => "foo"
- モジュール関数とは、
self関連
現在のメソッドの実行主体(レシーバ)を指す
-
self呼び出し一覧
p self # => main # mainはトップレベル(Objectクラス)に存在するインスタンス
class Hoge
p self # => Hoge
# メソッド内のself
def hoge
p self
end
# クラスメソッド内のself
def self.hoge
p self
end
# 特異メソッド内のself
class << Hoge
def foo
p self
end
def bar
baz
end
private
# プライベートメソッド内のself
def baz
p self
end
end
end
Hoge.new.hoge # => <Hoge:0x000000010090eb00>
Hoge.hoge # => Hoge
Hoge.foo # => Hoge
Hoge.bar # => Hoge
# 名前空間
module Zoo
class Cat
def where_self
self
end
end
end
Zoo::Cat.new.where_self # => #<Zoo::Cat:0x007f8c02bf4f10>
-
クラスメソッド内の
selfのクラスはClassクラスclass Identity def self.this_object self end def this_object self end end p Identity.this_object.class # => Class p Identity.new.this_object.class # => Identity p Identity.this_object # => Identity p Identity.new.this_object.class # => #<Identity:0x00000001010198f0> -
Mixin先のクラスメソッドは参照できないmodule Bar def self.bar p self end def bar p self end end class Baz include Bar end Baz.bar # `<main>': undefined method `bar' for Baz:Class (NoMethodError) # 通常メソッドは参照可能 Baz.new.bar # => #<Baz:0x0000000104682978>
const_get
-
selfに定義された定数の値を返すmodule Bar BAR = 1 end class Object include Bar end # include されたモジュールに定義された定数を見付ける p Object.const_get(:BAR) # => 1 class Baz include Bar end # Object以外でも同様 p Baz.const_get(:BAR) # => 1 # 定義されていない定数 p Baz.const_get(:NOT_DEFINED) # => raise NameError # 第二引数に false を指定するとスーパークラスや include したモジュールで定義された定数は対象外になる p Baz.const_get(:BAR, false) # const_get': uninitialized constant Baz::BAR (NameError) p Bar.const_get(:BAR, false) # => 1 # 完全修飾名を指定すると include や自分自身へ定義されていない場合でも参照できる p Class.const_get("Bar::BAR") # => 1
class_variable_get
- クラス/モジュールに定義されているクラス変数の値を返す
- クラス変数は
StringまたはSymbolを指定する - クラス変数が定義されていない場合、
NameErrorが発生する
class Fred @@foo = 99 end def Fred.foo class_variable_get(:@@foo) end p Fred.foo # => 99 - クラス変数は
private
-
privateメソッドの呼び出し時に明示的にレシーバを指定することはできない。ただし、selfをレシーバとして指定することはできる-
private以降で定義されたメソッドはそのクラス、またはサブクラス内でのみ使用することができる
class A def foo self.bar end private def bar "baz" end def self.bar "quux" end end puts A.new.foo # => "baz" puts A.bar # => "quux" # 明示的にレシーバを指定しているため、エラーになる puts A.new.bar # NoMethodError: private method `bar' called for xxx -
-
親クラスに定義されている
privateメソッドは継承先からも呼び出ことができる- 逆に子クラスに定義されているprivateメソッドは親クラスから呼び出すことができない
class Parent private def hoge 'hoge' end end class Child < Parent def test hoge end end Child.new.test # => "hoge"
undef_method
-
引数(
StringまたはSymbol)に指定したインスタンスメソッドを未定義にする- スーパークラスのメソッドには影響しないが、サブクラスでそのメソッドが「未定義」となっているため、スーパークラスに同名メソッドが存在しても参照できず、
NoMethodErrorになる-
undef:上記と機能は同様なのだが、引数に識別子もしくSymbolを指定する
-
class M1 def foo ;end def self.moo undef foo end end M1.instance_methods(false) # => [:foo] M1.moo M1.instance_methods(false) # => [] class M2 def foo ;end def self.moo undef_method :foo end end M2.instance_methods(false) # => [:foo] M2.moo M2.instance_methods(false) # => [] class M def foo ;end end class M3 < M def foo; end def self.moo undef_method :foo end end M3.instance_methods(false) # => [:foo] M3.moo M3.instance_methods(false) # => [] M.instance_methods(false) # => [:foo]-
aliasでメソッド名を別名に変更した場合の挙動class Foo def foo "foo" end end class Bar < Foo def foo super + "bar" end alias bar foo undef foo end puts Bar.new.bar # => foobar
- スーパークラスのメソッドには影響しないが、サブクラスでそのメソッドが「未定義」となっているため、スーパークラスに同名メソッドが存在しても参照できず、
remove_method
- 引数に指定したインスタンスメソッドを削除する
- スーパークラスの同名メソッドには影響しない
- 指定したメソッドが定義されていない場合、
NameErrorが発生する
class C def foo p 'C#foo' end end class C2 < C def foo p 'C2#foo' end def bar; end remove_method :foo, :bar end p C2.new.foo # => "C#foo" p C2.new.bar # => undefined method `bar' for #<C2:xxx> (NoMethodError)
alias_method
- 既存のメソッドに対して別名を付ける
- グローバル変数やクラスメソッドに対して使用することはできない
module Kernel alias_method :hoge, :puts hoge 'hoge' # => hoge end-
alias:alias_method同様、既存のメソッドに対して別名を付ける- メソッド名に文字列は使えない
alias new_method old_method # 識別子 alias :new_method :old_method # シンボル alias $new_global_val $old_global_val # グローバル変数も可能
refinement
- 既存のクラスやモジュールを拡張・修正することができる
-
using:refinementの引数で指定したモジュールで定義された拡張を有効にする- メソッド内で呼び出した場合、
RuntimeErrorが発生する
class C def m1 400 end end module M refine C do def m1 100 end end end class C using M # puts C.new => スコープ内で呼び出した場合、 100 を返す end # using で Refinementを有効化しているが、スコープ外のため、 クラスCの m1 メソッドを参照している puts C.new.m1 # 400 - メソッド内で呼び出した場合、
-
refineは無名モジュールを作成するため、ブロック内のselfは無名モジュールになる- 無名モジュール:
Module.newで作成したモジュールを指す
class C def self.m1 200 end end module R refine C do def self.m1 self # => <Module:0x000000010311d3d0> 100 end end end using R puts C.m1 # => 200-
Refinementでクラスメソッドを再定義したい場合はsingleton_classを使うclass C def self.m1 'C.m1' end end module M refine C.singleton_class do def m1 self # => C 'C.m1 in M' end end end using M puts C.m1 # => C.m1 in M と表示されます。
- 無名モジュール:
-
const_defined?
- クラス/モジュールに引数に指定した名前の定数が定義されている場合、真を返す
- 第引数は
StringまたはSymbolで指定する - 第2引数に
falseを指定するとスーパークラスやincludeしたモジュールで定義された定数は対象外になるmod = Module.new mod.module_eval do CONST_IN_BLOCK = 100 end mod.module_eval(<<-EVAL) CONST_IN_HERE_DOC = 100 EVAL puts mod.const_defined?(:CONST_IN_BLOCK) # => true puts mod.const_defined?(:CONST_IN_BLOCK, false) # => false puts Object.const_defined?(:CONST_IN_BLOCK) # => true puts Object.const_defined?(:CONST_IN_BLOCK, false) # => true puts mod.const_defined?(:CONST_IN_HERE_DOC) # => true puts mod.const_defined?(:CONST_IN_HERE_DOC, false) # => true puts Object.const_defined?(:CONST_IN_HERE_DOC) # => false puts Object.const_defined?(:CONST_IN_HERE_DOC, false) # => false
- 第引数は
const_missing
- 定義されていない定数を参照したときにこのメソッドを呼び出される
class Foo def Foo.const_missing(id) warn "undefined constant #{id.inspect}" end Bar = 'baz' end Foo::Bar # => "baz" Foo::Baz # => undefined constant :Baz
探索順
定数
-
定数名のみを記述した場合の探索経路は次のようになる
-
(1) クラス/モジュールのネストを辿る探索を行う
- クラス/モジュールのネストの状態を返すメソッド「
Module.nesting」の配列内容が、探索経路となる-
[M1::C2::M2::Cb, M1::C2::M2, M1::C2, M1]の場合-
M1::C2::M2::Cb->M1::C2::M2->M1::C2->M1の順に探索する
-
-
- クラス/モジュールのネストの状態を返すメソッド「
-
(2) 次に、継承チェーンを辿る探索を行う
module M1 class C1 CONST = "001" end class C2 < C1 CONST = "010" module M2 CONST = "011" class Cb p Module.nesting # => [M1::C2::M2::Cb, M1::C2::M2, M1::C2, M1] p CONST # => "011"(モジュールM2に定数の定義されてなければ、 010 が出力される) end end end end # クラス・モジュールのネストを辿ったが見つからず、次の継承チェーンを辿った場合 module M1 class C1 CONST = "001" end class C2 < C1 module M2 class Ca CONST = "100" end class Cb < Ca p Module.nesting # => [M1::C2::M2::Cb, M1::C2::M2, M1::C2, M1] p CONST # => "100" (継承先のクラスCaの定数を辿る) end end end end -
クラスと特異クラスに継承関係はない
class C1 FOO = 'C1' end class << C1 def foo Module.nesting # [#<Class:C1>] FOO end end C1.foo # => `foo': uninitialized constant #<Class:C1>::FOO (NameError) class << C1 FOO = '特異C1' end C1.foo # => '特異C1' -
ミックスイン先の定数は参照できる
module M1 FOO = 'M1' class ::C1 Module.nesting # [C1, M1] FOO # => "M1" end end class C2 < C1 Module.nesting # [C2] # FOO => includeする前だと下記エラーが発生する # <class:C2>': uninitialized constant C2::FOO (NameError) include M1 FOO # => 'M1' end
-
-
::演算子を用いて記述した場合の探索- 継承チェーンを(Objectクラスの手前まで)辿る探索を行う
CONST = 'TOP' module M3 CONST = 'M3!' # この定数が定義されていない状態で呼び出すと NameErrorが発生する end module M1 CONST = 'M1' module M2 CONST = 'M2' module M3 CONST = 'M3' end end end p ::M3::CONST # => "M3!"
- 継承チェーンを(Objectクラスの手前まで)辿る探索を行う
- 余談
- 代入を行うと警告が発生するが、値は変更される
class Hoge HOGE = 'hoge' end Hoge::HOGE # => "hoge" Hoge::HOGE = 'hogehoge' # warning: previous definition of HOGE was here # => "hogehoge" Hoge::HOGE # => "hogehoge"
- 代入を行うと警告が発生するが、値は変更される
クラス変数
-
クラスに所属するあらゆるもので情報を共有するためにあり、特異クラス定義の中でクラス変数を定義してもレキシカルに決定される
class C @@val = 10 end module B @@val = 30 end module M include B @@val = 20 class << C p @@val # => 20 end end-
子モジュールと親モジュールにて同名のクラス変数を書き換えるとエラーが発生する
class C @@val = 10 end module B @@val = 30 end module M @@val = 20 include B class << C p @@val # `singleton class': class variable @@val of M is overtaken by B (RuntimeError) end end- 尚、クラスとモジュールではなく、親クラスとその親クラスで同じクラス変数を書き換えている場合はエラーが発生しない
class C @@val = 10 end class C2 < C @@val = 20 end class C3 < C2 class << self def hoge p @@val end end end C3.hoge # => 20- 余談:トップレベルでクラス変数を読み書きするとエラーが発生する
@@val = 10 # `<main>': class variable access from toplevel (RuntimeError) @@val # `<main>': class variable access from toplevel (RuntimeError)
- 尚、クラスとモジュールではなく、親クラスとその親クラスで同じクラス変数を書き換えている場合はエラーが発生しない
-
クラスインスタンス変数
-
継承先のサブクラスからはクラスインスタンス変数を参照できないため、エラーにはならないが
nilを返すclass Foo attr_reader :var @var = "1" def initialize @var = "2" end end class Baz < Foo def self.var @var end end def Foo.var @var end arr = [ Foo.new.var, # "2" Foo.var, # "1" Baz.new.var, # "2" Baz.var # nil ]- 特異クラスにも同様のクラスインスタンス変数が存在する場合
- 特異メソッド
Speaker.speakはSpeakerクラスのコンテキストで評価されるため、特異クラスのインスタンス変数ではなく、Speakerクラスのインスタンス変数を参照する
class Speaker @message = "Hello!" class << self @message = "Howdy!" def speak @message end end end p Speaker.speak # => "Hello!" # 特異クラスのクラスインスタンス変数にアクセスする場合(Speakerの特異クラス定義を再オープン) speaker_message = class << Speaker; @message; end p speaker_message # => Howdy!- 余談
- インスタンスメソッド内から直接参照できない
- クラスメソッド(特異メソッド)からアクセスできる
-
attr_readerやattr_accessorなどのアクセサを使用しても参照できない
class Hoge @hoge = 1 attr_accessor :hoge def initialize @hoge *= 2 if hoge end def instance_hoge @hoge end class << self def class_hoge @hoge end end end hoge = Hoge.new hoge.instance_hoge # => nil Hoge.class_hoge # => 1 hoge.hoge # <main>': undefined local variable or method `hoge' for main:Object (NameError)
- 特異メソッド
- 特異クラスにも同様のクラスインスタンス変数が存在する場合
インスタンス変数
-
自身のクラスに定義されたインスタンス変数を最優先で参照する
class Foo def initialize @hoge = 'hoge' end end class Bar < Foo def initialize @hoge = 'hogehoge' end def bar p @hoge end end bar = Bar.new bar.bar # => "hogehoge" bar.instance_variable_get(:@hoge) # => "hogehoge" -
次クラスにはなく、継承関係のクラスにある場合、親クラスのインスタンス変数を参照する
class Foo def initialize @hoge = 'hoge' end def foo p @hoge end end class Bar < Foo def bar p @hoge end end bar = Bar.new bar.bar # => "hoge" bar.instance_variable_get(:@hoge) # => "hoge" -
ミックスインで追加された場合、親クラスのインスタンス変数を参照する
module Baz def baz @hoge = 'mixin hoge' end end class Foo def initialize @hoge = 'hoge' end def foo p @hoge end end class Bar < Foo include Baz def bar p @hoge end end bar = Bar.new bar.bar # => "mixin hoge" bar.instance_variable_get(:@hoge) # => "mixin hoge"
余談
- インスタンスメソッド内のインスタンス変数は値を保持しない
class Foo def foo @foo ||= 0 @foo += 1 end end Foo.new.foo # => 1 Foo.new.foo # => 1 Foo.new.foo # => 1
インスタンスメソッド
-
継承順に参照される
-
クラス名.ancestorsでクラス/モジュールの継承順を配列で確認することができる
class Foo def hoge p 'Foo#hoge' end def hogehoge p 'Foo#hogehoge' end end class Bar < Foo def hoge p 'Bar#hoge' end end # 探索順を確認 Bar.ancestors # => [Bar, Foo, Object, PP::ObjectMixin, Kernel, BasicObject] bar = Bar.new bar.hoge # => "Bar#hoge" # Barクラスにメソッド「hogehoge」が存在しないため、探索順通りに辿り、Fooクラスのメソッドを参照する bar.hogehoge # => "Foo#hogehoge" # どのクラスが持っているメソッドなのか確認 p bar.method(:hoge).owner # => Bar p bar.method(:hogehoge).owner # => Foo-
includeで追加したメソッドの場合、親クラスのメソッドより優先されるmodule Foo def hoge p 'Foo#hoge' end def hogehoge p 'Foo#hogehoge' end end class Bar def hoge p 'Bar#hgoe' end def hogehoge p 'Bar#hogehoge' end end class Baz < Bar include Foo def hoge p 'Baz#hgoe' end end Baz.ancestors # => [Baz, Foo, Bar, Object, PP::ObjectMixin, Kernel, BasicObject] baz = Baz.new baz.hoge # => "Baz#hgoe" baz.hogehoge # => "Foo#hogehoge" -
prependで追加したメソッドの場合、自身の持つメソッドより優先されるmodule Foo def hoge p 'Foo#hoge' end def hogehoge p 'Foo#hogehoge' end end class Bar def hoge p 'Bar#hgoe' end def hogehoge p 'Bar#hogehoge' end end class Baz < Bar prepend Foo def hoge p 'Baz#hgoe' end end Baz.ancestors # => [Foo, Baz, Bar, Object, PP::ObjectMixin, Kernel, BasicObject] baz = Baz.new baz.hoge # => "Foo#hoge" baz.hogehoge # => "Foo#hogehoge" -
同名メソッドが特異メソッドに存在する場合、クラスが持つメソッドより優先される
module Mod def foo p 'Mod Foo' end end class Foo prepend Mod def foo p 'foo' end end foo = Foo.new def foo.foo p 'FOO FOO' end foo.foo # => "FOO FOO" # 特異メソッドはインスタンスに生えているので `クラス名.ancestors` では確認できない p Foo.ancestors # => [Mod, Foo, Object, PP::ObjectMixin, Kernel, BasicObject] # 自身のオブジェクトを含んだリストを確認する場合 p foo.singleton_class.ancestors # => [#<Class:#<Foo:xxx>>, Mod, Foo, Object, PP::ObjectMixin, Kernel, BasicObject]
-
余談
-
トップクラスに定義したインスタンスメソッドをサブクラスから参照できる
def hoge p 'hoge' end class Foo def foo hoge end end Foo.new.foo # => "hoge" -
トップクラスに定義したプライベートメソッドを直接参照できる
private def hoge 'hoge' end p hoge # => "hoge" -
クラスメソッド内でインスタンスメソッドを呼び出すとエラーになる
class Hoge def self.foo bar end def bar p 'bar' end end Hoge.foo # foo': undefined local variable or method `bar' for Hoge:Class (NameError) -
クラス内に直でセットしたインスタンスメソッドは、クラス定義が読み込まれた時点で実行される
- インスタンス作成時には実行されない
def hoge p 'hoge' end class Foo hoge # 当該箇所 def foo p 'foo' end end Foo.new.foo # => "foo"
Enumeratorクラス関連
each以外のメソッドにもEnumerableの機能を提供するためのラッパークラス
enum_for, to_enum
-
Enumeratorオブジェクトを作成するa = [1, 2, 3] p(a.to_enum) # => #<Enumerator: [1, 2, 3]:each> p(a.enum_for) # => #<Enumerator: [1, 2, 3]:each>- 文字列を扱う場合、引数に
each_charを設定するenum = "hoge".enum_for(:each_char) p enum.next # => "h" p enum.next # => "o" p enum.next # => "g" p enum.next # => "e"
- 文字列を扱う場合、引数に
each_char
-
文字列の各文字に対して繰り返し処理を行う
-
enum_for(:each_char)、またはto_enum(:each_char)でも同様の結果が得られる
"hello世界".each_char {|c| print c, ' ' } # => h e l l o 世 界 -
filter_map
- ブロックが返した値の内、真の値だけを配列として返す
ary = ["foo", "bar", nil, "baz"] p ary.filter_map { |i| i&.upcase } # => ["FOO", "BAR", "BAZ"]
for文
- 指定したオブジェクトから繰り返しのたびに値を取り出し、値がなくなるまで繰り返しを行う
- ※ 他の繰り返し処理との大きな違いとして、新しいスコープを作成しないため、ループ内で定義された変数はループ外のコンテキストでも参照されてしまうため、予期しない動作を引き起こす可能性がある点に注意!
for i in 1..3 do # do は省略可 puts i end puts i # => 3 (ループ外でも参照できる)
partition
- ブロックの条件を満たす配列と条件を満たさない配列に分割して、結合した2次元配列を返す
- 条件を満たす配列が0番目に入る
a = (1..5).partition(&:odd?) p a # => [[1, 3, 5], [2, 4]]
tally
-
selfに含まれた要素を数え上げた結果をHashで返す- 返り値の
Hashのkeyはselfに含まれる要素で、valueは対応する要素が出現する回数が格納される-
Ruby 3.1から
tallyメソッドが引数としてハッシュを受け取れるようになった
fruits = ['apple', 'banana', 'grape', 'orange', 'apple'] fruits.tally # => {"apple"=>2, "banana"=>1, "grape"=>1, "orange"=>1} ['apple', 'grepe', 'peach'].tally(fruits.tally) # => {"apple"=>3, "banana"=>1, "grape"=>1, "orange"=>1, "grepe"=>1, "peach"=>1} -
Ruby 3.1から
- 返り値の
lazy
-
mapやselectメソッドに遅延評価を提供する# 先頭から3つの値を取り出す場合 (1..10).each.lazy.first(3) # => [1, 2, 3] (1..10).each.lazy.take(3).force # => [1, 2, 3] (1..10).each.lazy.take(3).to_a # => [1, 2, 3]
chunk
- 要素を前から順にブロックで評価し、その結果によって要素をチャンクに分けた要素を持つ
Enumeratorを返す- ブロックの評価値が同じ値が続くものを一つのチャンクとして扱い、ブロックの評価値が一つ前と異なる所でチャンクが区切られる
- 返り値の
Enumeratorは各チャンクのブロック評価値と各チャンクの要素を持つ配列のペアを各要素とする - 構文:
enum.chunk {|elt| key }.each {|key, ary| do_something }
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk {|n| n.even? }.each {|even, ary| p [even, ary] } # => [false, [3, 1]] # [true, [4]] # [false, [1, 5, 9]] # [true, [2, 6]] # [false, [5, 3, 5]]
Procオブジェクト
- ブロックをオブジェクト化したProcクラスのインスタンス
-
callまたは[]で呼び出すことができる- 引数の数が曖昧のため、引数を省略可能
local = 0 p1 = Proc.new { |arg1, arg2| arg1, arg2 = arg1.to_i, arg2.to_i local += [arg1, arg2].max } p p1.call("1", "2") # => 2 p p1.call("7", "5") # => 9 p p1.call("9") # => 18 -
lambda
-
Proc.newに殆ど一緒- 引数の数が厳密のため、引数を省略できない
local = 0 p1 = lambda { |arg1, arg2| arg1, arg2 = arg1.to_i, arg2.to_i local += [arg1, arg2].max } p p1.call("1", "2") # => 2 p p1.call("7", "5") # => 9 p p1.call("9") # => wrong number of arguments (given 1, expected 2) (ArgumentError)-
lambdaの省略記法(ラムダリテラル構文)を使用した場合-
->と引数リストの括弧の間にスペースがあるとエラーになる(くっついている必要がある)
local = 0 p1 = ->(arg1, arg2) { arg1, arg2 = arg1.to_i, arg2.to_i local += [arg1, arg2].max } p p1.call("1", "2") # => 2 p p1.call("7", "5") # => 9 p p1.call("9") # => wrong number of arguments (given 1, expected 2) (ArgumentError) -
yield
- メソッド内部でブロックの実行結果を呼び出すことができる
def multiply_by(n) n * yield end p multiply_by(4) { 2 + 3 } # => 20
ブロックを引数で明示的に受け取る方法
-
&が付いた引数(ブロック引数)に渡されたブロックをProcオブジェクトに変換することができるval = 100 def hoge(val) yield(15 + val) end _proc = Proc.new{|arg| val + arg } p hoge(val, &_proc) # => 215
to_proc
-
selfに対応するProcオブジェクトを返す- 第一引数をレシーバとして、
selfという名前のメソッドを残りの引数を渡して呼びだす - 生成される
Procオブジェクトはlambda - メソッドに
&と共にシンボルを渡すとto_procが呼ばれ、ブロックとして扱うことができる
# "ff".to_i(16)と同様 :to_i.to_proc["ff", 16] # => 255 :object_id.to_proc.lambda? # => true # &プレフィックスを使用して暗黙的に呼び出す場合 (1..5).select(&:odd?) # => [1, 3, 5] # &プレフィックスを使用しない場合 (1..5).select { |i|i.odd?.to_proc } # => [1, 3, 5] - 第一引数をレシーバとして、
例外処理関連
rescue
-
rescue節でほできる例外は、指定した例外クラスとそのサブクラス-
rescueに例外クラスを指定しない場合は、StandardErrorが捕捉される
class Error1 < StandardError; end class Error2 < Error1; end begin raise Error2 # rescue に Error1 を指定しているが、Error1 のサブクラスの Error2 が # raise に指定されているため、Error2 が捕捉される rescue Error1 => ex puts ex.class # Error2 end -
-
rescue節でraiseが引数なしで呼び出された場合、捕捉された例外が再度raiseされるCustomError = Class.new(StandardError) def boom raise CustomError rescue raise # 引数なし end begin boom rescue => e p e.class # => CustomError end -
殆どの組み込み例外クラスは
StandardErrorのサブクラスですが、通常捕捉されることを想定していないいくつかの例外クラスはExceptionを直接継承しているArgumentError < StandardError < Exception ScriptError < Exception
raise
-
RuntimeErrorを発生させる-
raiseに例外クラスを指定しない場合は、RuntimeErrorを捕捉する-
RuntimeErrorはStandardErrorのサブクラス
-
class Hoge raise rescue => e e.class # => RuntimeError end -
ensure
- メソッド(または
begin/endブロック)のensure節は、例外が発生してもしなくても、常に実行される- ただし、
ensure節の値は返り値としては使用されず、ensure節が実行される直前の評価結果が返り値として使用される
def greeting puts "greeting called!" "hello" ensure puts "Ensure called!" "hi" end puts greeting # 実行結果 # greeting called! # Ensure called! # hello - ただし、
気になるメソッド
block_given?
- メソッドにブロックが与えられていれば真を返す
-
{ }はdo endよりも結合度が高い為、実行結果が異なる
def m1(*) str = yield if block_given? p "m1 #{str}" end def m2(*) str = yield if block_given? p "m2 #{str}" end # ブロックの場合 m1 (m2 { "hello" } ) # 実行結果 # "m2 hello" # "m1 " # do endで置き換えた場合 m1 m2 do # m1(m2) do と解釈される "hello" end # 実行結果 # "m2 " # "m1 hello" -
instance
- そのクラスの唯一のインスタンスを返す
-
Singletonをincludeしないと使用することができない-
Singletonをincludeしたクラスで定義されるので、正確にはSingletonモジュールのメソッドではない
-
- ミックスインしたクラスのインスタンスは常に同一のものを返す
require 'singleton' class Hoge include Singleton def hoge @hoge ||= 0 @hoge += 1 end end p Hoge.instance.hoge # => 1 p Hoge.instance.hoge # => 2 p Hoge.instance.hoge # => 3
-
catch
-
catchブロックでは、throwが実行されるまでコードが実行される-
catchに対応するシンボルがthrowに渡されると、Rubyはブロックの実行を終了し、ブロックの次の処理から実行を継続する
def x puts "x" end def y puts "y" throw :done # catchに対応するシンボル end def z puts "z" end catch :done do x y z end puts "done!" # [実行結果] # x # y # done!-
catchブロック内でreturnを使用するとLocalJumpErrorになる
result = catch do |tag| for i in 1..2 for j in 1..2 for k in 1..2 return k # => `block (4 levels) in <top (required)>': unexpected return (LocalJumpError) end end end end -
succ
-
selfの次の文字列を返すp "aa".succ # => "ab" p "88".succ.succ # => "90" # 繰り上がり p "99".succ # => "100" p "a9".succ # => "b0" p "Az".succ # => "Ba" p "zz".succ # => "aaa" p "-9".succ # => "-10" p "9".succ # => "10" p "09".succ # => "10" # アルファベット・数字とそれ以外の混在 p "1.9.9".succ # => # "2.0.0" # アルファベット・数字以外のみ p ".".succ # => "/" p "\0".succ # => "\001" p "\377".succ # => "\001\000"
__method__
- 現在のメソッド名を返す
- メソッド外で呼ぶと
nilを返す
def foo p __method__ end alias :bar :foo foo # => :foo bar # => :foo (メソッド名が変更された場合でも元のメソッド名を返す) p __method__ # => nil - メソッド外で呼ぶと
ord
- 文字列の最初の文字の文字コードを整数で返す
-
selfが空文字の場合はArgumentErrorを返す
p "h".ord # => 104 p "hoge".ord # => 104 p "".ord # ord': empty string (ArgumentError) -
def_delegator
- メソッドの委譲先を設定する
- 第一引数に移譲先のオブジェクト、第二引数に移譲先のメソッド、第三引数に移譲元のメソッドを設定する
- 委譲元と委譲先のメソッド名が同じ場合、第三引数を省略することが可能
-
extend Forwardableで拡張することで使用できるようになる
require 'forwardable' class List extend Forwardable def initialize @contents = [] end def_delegator :@contents, :push, :mypush def_delegator :@contents, :[] end list = List.new list.mypush("a") list.mypush("b") list.mypush("c") p list[1] # => "b" - 第一引数に移譲先のオブジェクト、第二引数に移譲先のメソッド、第三引数に移譲元のメソッドを設定する
match
- 指定した文字列に対して文字列によるマッチングを行い、マッチした場合には結果を
MatchDataオブジェクトで返す- マッチしなかった場合、
nilを返す - 第二引数には、マッチの位置を指定することができる(デフォルトは0)
s = 'aage bbge ccge' s.match('bbge') # => #<MatchData "bbge"> s.match('cc') # => #<MatchData "cc"> s.match('hoge') # => nil s.match('..ge', 1) # => #<MatchData "bbge"> s.match('ge', 13) # => nil
- マッチしなかった場合、
match?
- 指定した文字列に対して文字列によるマッチングを行い、マッチした場合には
trueを返す- マッチしない場合には
falseを返す - 第二引数には、マッチの位置を指定することができる(デフォルトは0)
"Ruby".match?('Ru') # => true "Ruby".match?('Ru', 1) # => false "Ruby".match?('ho') # => false
- マッチしない場合には
super
- 現在のメソッドがオーバーライドしているメソッドを呼び出す
- 括弧と引数が省略された場合には現在のメソッドの引数をそのまま引き渡す
- 引数を渡さずにオーバーライドしたメソッドを呼び出すには、
super()と括弧を明示する
- 引数を渡さずにオーバーライドしたメソッドを呼び出すには、
class Foo def foo(arg=nil) p arg end end class Bar < Foo def foo(arg) super(5) # 5 を引数にして呼び出す super(arg) # 5 を引数にして呼び出す super # 5 を引数にして呼び出す super(arg) の略記法 arg = 1 super # 1 を引数にして呼び出す super(arg) の略記法 super() # 引数なしで呼び出す end end Bar.new.foo 5 # 実行結果 # 5 # 5 # 5 # 1 # nil-
プライベートメソッドも参照できる
class C private def c p 'c' end end class B < C def c super end end B.new.c # => "c"- オーバーライド先のメソッドが引数を受け取らない場合、
super()と括弧を明示しないとエラーになる
# 括弧を明示しない場合 class Parent def say puts "親です" end end class Child < Parent def say(message) super end end Child.new.say('Hi!') # => ArgumentError (wrong number of arguments (given 1, expected 0)) # 括弧を明示した場合 class Child < Parent def say(message) super() # superキーワードに丸括弧を明示する end end Child.new.say('Hi!') # => 親です - オーバーライド先のメソッドが引数を受け取らない場合、
- 括弧と引数が省略された場合には現在のメソッドの引数をそのまま引き渡す
dig
- ハッシュや配列などのデータ構造に対して、
keyやindexを設定 することで要素を参照できる(ネストしたオブジェクトを再帰的に返す)- 存在しない要素にアクセスする場合でもエラーは発生せず、
nilを返す
h = { foo: {bar: {baz: 1}}} h.dig(:foo, :bar, :baz) # => 1 h.dig(:foo, :zot, :xyz) # => nil g = { foo: [10, 11, 12] } g.dig(:foo, 1) # => 11 - 存在しない要素にアクセスする場合でもエラーは発生せず、
exit
- 例外
SystemExitを発生させることによってプログラムの実行を終了させる-
SystemExitでエラーをキャッチした場合、exitによるプログラム終了処理自体が中断されるため、後続処理が実行される - ※ 下記コードは
irbでは再現不可(rails consoleで再現可能)
puts 'start' begin puts 'start1...' exit rescue SystemExit => err puts "end1 with #{err.inspect}" end begin puts 'start2...' exit ensure puts 'end2...' end puts 'end' # 実行されない # => start # start1... # end1 with #<SystemExit: exit> # start2... # end2... -
squeeze
- 引数に含まれる文字が複数並んでいたら1文字にまとめる
- 引数を 1 つも指定しない場合は、すべての連続した文字を 1 文字にまとめる
- 引数を複数指定した場合は、すべての引数にマッチする文字を 1 文字にまとめる
p "112233445566778899".squeeze # => "123456789" p "112233445566778899".squeeze("2-8") # => "11234567899" # 以下の2つは同じ意味 p "112233445566778899".squeeze("2378") # => "11234455667899" p "112233445566778899".squeeze("2-8", "^4-6") # => "11234455667899"
strip
- 文字列の先頭と末尾の空白文字を全て取り除いた文字列を生成して返す
- 空白文字の定義は
"\t\r\n\f\v"。また、文字列右側からは\0も取り除くが、左側の\0は取り除かない
p " abc \r\n".strip # => "abc" p " abc ".strip # => "abc" p " \0 abc \0".strip # => "abc" p "abc".strip # => "abc" # 非破壊的メソッドのため、元の文字列は変わらない str = "\tabc\n" p str.strip # => "abc" p str # => "\tabc\n" # !を末尾に付けると破壊的メソッドになり、元の文字列が変わる str = "\tabc\n" p str.strip! # => "abc" p str # => "abc" - 空白文字の定義は
演算子関連
AND
-
&&と同じ動作をするが優先順位が低いis_empty = true AND false is_empty # => true (AND よりも = の優先度が高いので true が代入される)
::
- ダブルコロン(
::)を用いて、探索の始点となるオブジェクトを指定して定数やメソッドを参照できる- 左辺を省略して
::が先頭にするとトップレベルから定数の探索を行う
CONST = 'Top' class Foo CONST = 'Foo' def foo 'foo' end end class Bar CONST = 'bar' end class Baz < Bar CONST = 'baz' end Foo::CONST # => "Foo" Foo.new::foo # => "foo" ::CONST # => "Top" 'str'::upcase # => "STR" - 左辺を省略して
...
-
転送引数と言い、キーワード引数やブロックを含め、残りの引数を転送する
- 引数は括弧で囲う必要がある(省略できない)
class Hoge def foo(*arg) p arg end def bar(...) foo(...) end end hoge = Hoge.new hoge.bar('foo', 'bar', 'baz') # => ["foo", "bar", "baz"]
..
-
半無限区間と言い、開始インデックスと終了インデックスを指定して対象の範囲の要素を新しい配列として取得する
- 構文:
Arrayオブジェクト[start..end]
arr = %i[a b c d] arr[2..3] # => [:c, :d] arr[2..-1] # => [:c, :d] arr[2..-3] # => [] arr[2..] # => [:c, :d] arr[..2] # => [:a, :b, :c] - 構文:
splat演算子
- 全ての残りの右辺値を配列として一つの変数に代入する
- 引数を指定しない場合、空配列を返す
- デフォルト値を直接設定することはできない
a, *b = *[1, 2, 3] p a # => 1 p b # => [2, 3] a, b = [1, [2, 3]] p a # => 1 p b # => [2, 3] a, b = [1, *[2, 3]] p a # => 1 p b # => 2 def ary(*args) p args end ary # => []
-
配列展開の挙動の違い
# 引数が文字列の場合 def fx(*args) p(args) end fx("apple", "banana", "carrot") # => ["apple", "banana", "carrot"] # 引数が配列の場合 def fx(*args) p(args) end fx(["apple", "banana", "carrot"]) # => [["apple", "banana", "carrot"]] # 引数が配列且つpメソッド呼び出し時にsplat演算子を付与した場合 def fx(*args) p(*args) end fx(["apple", "banana", "carrot"]) # => ["apple", "banana", "carrot"] # 引数が配列且つsplat演算子を付与した場合 def fx(*args) p(args) end fx(*["apple", "banana", "carrot"]) # => ["apple", "banana", "carrot"] -
メソッド内でデフォルト値を明示的に設定する場合
def example_method(arg1, *args) default_value = "default" # デフォルト値をセット args = [default_value] if args.empty? puts "args: #{args.inspect}" end example_method("first_arg") # => args: ["default"] example_method("first_arg", "second_arg") # => args: ["second_arg"]
<<
- 指定されたオブジェクトを自身の末尾に破壊的に追加する
-
selfを返すので、連続して書くことができる - オーバーライド可能
ary = [1] ary << 2 << 3 << 4 p ary # => [1, 2, 3, 4] -
フリップフロップ
- 条件式の中で範囲式を使うことでフリップフロップ回路のように一時的に真偽を保持するような挙動をとる
- 評価順
- 左辺が
trueになるまでfalseを返し、左辺がtrueになると、次から右辺を評価する - 右辺が
trueになるまでtrueを返し、右辺がtrueになると、次から左辺を評価する - 左辺が
trueになるまでfalseを返す
- 左辺が
10.times{|d| print d < 2...d > 5 ? "O" : "X" } # OOOOOOOXXX=> 10 - 評価順
日付関連
- クラス同士の減算
require 'date' # Date同士の減算 d = Date.today - Date.today - 1 d.class # => Rational # DateTime同士の減算 dt = DateTime.now - DateTime.now - 1 dt.class # => Rational # Time同士の減算 t = Time.now - Time.now - 1 t.class # => Float
date << | >>
-
<<は現在の日付のn月前を表すDateオブジェクトを返す -
>>は現在の日付のn月後を表すDateオブジェクトを返すrequire "date" date = Date.new(2000, 2, 24) puts(date << 12) # => 1999-02-24 puts(date >> 12) # => 2001-02-24
strptime
-
Date,DateTime,Timeクラスの特異メソッドで、与えられた雛形(書式文字列)で日時表現を解析し、その情報に基づいてオブジェクトを生成する- ※
DateTimeクラスは非推奨なクラスとなり、DateTimeクラスではなくTimeクラスを使うよう、公式にアナウンスされているので割愛
- ※
Timeクラスの場合
- 第一引数に時刻を表す文字列を、第二引数に書式文字列を設定する
- 引数を設定しないと
ArgumentErrorになる -
Timeクラスを拡張するRubyのライブラリのため、require 'time'が必要# 構文 # Time.strptime(date, format, now=self.now) # Time.strptime(date, format, now=self.now) {|y| ... } require 'time' Time.strptime("00000024021993", "%S%M%H%d%m%Y") # => 1993-02-24 00:00:00 +0900 # ブロックを渡すと年の部分を変更できる Time.strptime('91/5/18 4:13:00', '%Y/%m/%d %T'){|y| # y には year(91)が渡される if y > 100 then y elsif y >= 69 then y + 1900 else y + 2000 end } # => 1991-05-18 04:13:00 +0900
Dateクラスの場合
- 第一引数に時刻を表す文字列を、第二引数に書式文字列を、第三引数にグレゴリオ暦をつかい始めた日をあらわすユリウス日を設定
- 第三引数を省略した場合、
「ITALY (1582年10月15日)」が設定される(気になる方は下記参考記事まで) -
Dateクラスを拡張するRubyのライブラリのため、require 'date'が必要require 'date' Date.strptime('2001-02-03T12:13:14Z') # => #<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)> date = Date.strptime('2001-02-03T12:13:14Z').to_s # => "2001-02-03" Date.strptime(date, '%Y-%m-%d') # => #<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>
- 第三引数を省略した場合、
strftime
- 与えられた雛形(書式文字列)で日時表現を解析し、その情報に基づいてオブジェクトを生成する
- 引数には書式文字列を設定
p t = Time.now # => 2023-12-29 10:13:44.373922 +0900 p t.strftime("Printed on %m/%d/%Y") # => "Printed on 12/29/2023" p t.strftime("%Y%m%d") # => "20231229" p t.strftime("%F") # => "2023-12-29" p t.strftime("%Y%jT%H%MZ") # => "2023363T1014Z"
- 引数には書式文字列を設定
Time.iso8601
- XML Schemaで定義されている
dateTimeとして引数に指定した日付文字列をパースしてTimeオブジェクトに変換する- 引数が
ISO 8601で定義されている形式に準拠していない、またはTimeクラスが指定された日時を表現できないときにArgumentErrorが発生するrequire 'time' iso8601_time = '2008-08-31T12:34:56+09:00' Time.iso8601(iso8601_time) # => 2008-08-31 12:34:56 +0900 # => 2008-08-31の後の「A」が形式に準拠していないため、エラーとなる begin non_iso8601_time = '2008-08-31A12:34:56+09:00' Time.iso8601(non_iso8601_time) rescue ArgumentError => err puts err #=> invalid date: "2008-08-31A12:34:56+09:00" end
- 引数が
正規表現関連
- ある一定の規則で並んでいる文字列を検出するための技法
正規表現一覧
-
.:任意の1文字(改行は含まない) -
*:0回以上の繰り返し -
+:1回以上の繰り返し -
?:0回か1回の繰り返し'hello'.match(/.*/) # => #<MatchData "hello"> 'hello'.match(/l+/) # => #<MatchData "ll"> 'hello'.match(/he?llo/) # => #<MatchData "hello"> -
{n}:ちょうどn回(nは数字) -
{n,}:n回以上(nは数字) -
{,m}:m回以下(mは数字) -
{n,m}: n回以上m回以下(n,mは数字)'aabbcc'.match(/a{1}b{2}c{1}/) # => #<MatchData "abbc"> 'aabbcc'.match(/a{,1}b{2,}c{,1}/) # => #<MatchData "abbc"> 'aabbcc'.match(/a{1,2}b{1,2}c{0,2}/) # => #<MatchData "aabbcc"> -
-(ハイフン):範囲の表現 -
[..](ブランケットで囲む):いずれかの1文字にマッチ -
[^..]:いずれかの文字にもマッチしない'hello'.match(/h[a-e][l-n]lo/) # => #<MatchData "hello"> 'hello'.match(/he[^a-g][^h-j]o/) # => #<MatchData "hello"> 'hello world'.match(/[^ ]*/) # => #<MatchData "hello"> -
\w:単語構成文字[a-zA-Z0-9_] -
\W:非単語構成文字[^a-zA-Z0-9_] -
\s:空白文字[ \t\r\n\f] -
\S:非空白文字[^ \t\r\n\f] -
\d:16進数字[0-9a-fA-F] -
\D:非10進数字[^0-9] -
\h:16進数字[0-9a-fA-F] -
\H:非16進数字[^0-9a-fA-F]/\w+/.match("ABC_def") # => #<MatchData "ABC_def"> /\W+/.match("ABC def") # => #<MatchData " "> /\s+/.match(" ") # => #<MatchData " "> /\S+/.match(" ") # => nil /\d+/.match('ABCabc123') # => #<MatchData "123"> /\D+/.match('ABCabc123') # => #<MatchData "ABCabc"> /\h+/.match('ABCabc123Z') # => #<MatchData "ABCabc123"> /\H+/.match('ABCabc123Z') # => #<MatchData "Z">応用
p "Matz is my tEacher"[/[J-P]\w+[^ ]/] # => "Matz"
$1..
- 特殊なローカル変数で
$の後にn(整数)を付けるとn個目の括弧内の正規表現にマッチした文字列を参照できる- 該当する括弧がなければ
nilが入る
- 該当する括弧がなければ
-
$0:Rubyスクリプトが実行された時のプログラムのファイル名を表す特別な変数%r|(http://www(\.)(.*)/)| =~ "http://www.example.com/" p $0 # => irb (irbで実行した場合) p $1 # => "http://www.example.com/" p $2 # => "." p $3 # => "example.com" p $4 # => nil
=~
- 正規表現もしくは
=~メソッドを持つオブジェクト(self)とのマッチを行う- 構文:
self =~ other- マッチしていればマッチした位置のインデックスを、そうでなければ
nilを返す -
otherが正規表現でも文字列でもない場合はother =~ selfを行う -
otherが文字列の場合、TypeErrorが発生する
- マッチしていればマッチした位置のインデックスを、そうでなければ
p "string" =~ /str/ # => 0 p "string" =~ /not/ # => nil p "abcfoo" =~ /foo/ # => 3 p /str/ =~ "str" # => 0 p "string" =~ "str" # =~': type mismatch: String given (TypeError) # 正規表現としての利用例 regex = /\d+/ # 1つ以上の数字にマッチする正規表現 regex =~ "123" # 0 - 構文:
制御構造関連
case(when節)
- 一つの式に対する一致判定による分岐を行う
- 上から順番に
whenの直後の式を評価した結果をレシーバとし、caseの直後の式を評価した値を引数として===演算子を呼び出し、最初に真を返したwhen節の本体を実行する - どの
when節でも条件が成立しなかった場合は、else節を実行する
def hoge(arg) case arg when 'foo' p 'foo' when 'bar' p 'bar' else p 'baz' end end hoge('bar') => "bar" - 上から順番に
case(in節)
- パターンマッチしているか確認する
def hoge case { a: '1', b: '2', c: '3' } in { a: 1, b: 2, c: 3 } p 'foo' in { a: '1', b: '2', c: '3' } p 'bar' else p 'baz' end end hoge # => 'bar' # ハッシュ式の設定値によっては、エラーになる(in節の設定値でシンボルを使う文法ではない場合) def hoge case { a: '1', b: '2', c: '3' } # syntax error, unexpected symbol literal, expecting label or ** or **arg or string literal (SyntaxError) in { :a => 1, :b => 2, :c => 3 } p 'foo' in { :a => '1', :b => '2', :c => '3' } p 'bar' else p 'baz' end end hoge # undefined local variable or method `hoge' for main:Object (NameError)
パーセント記法
%/ /
- ダブルクォート文字列にする
fruits = %/apple banana orange/ p fruits # => "apple banana orange"
%r/ /
- 正規表現にする
fruits = %r/apple banana orange/ p fruits # => /apple banana orange/
%w/ /
- 要素を文字列の配列にする
fruits = %w/apple banana orange/ p fruits # => ["apple", "banana", "orange"]
Rubyオプション一覧
- 前提:
MacのTerminalでシェル(zsh)で実行
-e
- 引数にファイル名を取らずに、引数に与えられた文字列をそのまま Ruby スクリプトとして解釈して実行する
% ruby -e 'puts "hoge"' # => hoge
-n
- 標準入力として与えられた文字列の各行に対してRubyスクリプトを実行する
- 各行の文字列は
$_変数に格納される
% echo 'foo\nbar\nbaz' | ruby -ne 'puts "hoge" + $_' # => hogefoo # => hogebar # => hogebaz - 各行の文字列は
-p
-
-nフラグとほぼ同じだが, 各ループの最後に$_の値を出力するようになる% echo 'foo\nbar\nbaz' | ruby -pe '$_ = "hoge" + $_' # => hogefoo # => hogebar # => hogebaz
-a
-
-nや-pと一緒に用いた際に、各行の文字列が split されて $F 配列に格納される-
-nか-pオプションが同時に指定されない限り, このオプションは意味を持たない
% echo 'foo bar baz' | ruby -ane 'puts $F' # => foo # => bar # => baz % echo 'foo bar baz' | ruby -ane 'puts $F[1]' # => bar -
-l
- 行末の自動処理(読み込み時の改行除去、出力時に行末に自動で改行を付与)を行う
-
$_変数の末尾に改行が残っていることにより、意図しない結果が出力される場合の回避策として使用できる
echo 'foo\nbar\nbaz' | ruby -lne 'puts $_ + "hoge"' # => foohoge # => barhoge # => bazhoge # -lオプションを使用しなかった場合 echo 'foo\nbar\nbaz' | ruby -ne 'puts $_ + "hoge"' # => foo # => hoge # => bar # => hoge # => baz # => hoge -
-i
-
-pや-nと併用することで、引数として指定されたファイルの内容を置換する-
-i.bakのように拡張子を指定すると、バックアップファイルとして指定拡張子が付いたファイルを作成する
% echo matz > /tmp/junk % cat /tmp/junk # => matz % ruby -i.bak -pe '$_.upcase!' /tmp/junk % cat /tmp/junk # => MATZ % cat /tmp/junk.bak # => matz -
-C
- スクリプト実行前に指定されたディレクトリに移動する
# /tmp/junk の中身 puts 'MATZ' % ruby -C /tmp junk # => MATZ
-c
- スクリプトの内部形式へのコンパイルのみを行い, 実行しない
- コンパイル終了後, 文法エラーが無ければ, Syntax OKと出力する
% ruby -c /tmp/junk # Syntax OK
-r
- スクリプト実行前に指定したライブラリを
requireする# SecureRandomを読み込む場合 ruby -r 'SecureRandom' -e 'puts SecureRandom.hex' # => 1e0b847dd05a195ffa0f6d302e64d8c7
その他
require
- Rubyライブラリをロードする
- 同じファイルは1度のみロードする
-
.rbや.soを自動補完する
# lib.rb module Lib $num += 1 end # program.rb $num = 0 1..10.times do |n| require './lib.rb' end puts $num # => 1
load
- 設定ファイルの読み込みに用いる
- ファイルを無条件ロードする
- 補完はしない
# lib.rb module Lib $num += 1 end # program.rb $num = 0 1..10.times do |n| load './lib.rb' end puts $num # => 10
Numbered parameter
- 暗黙的にブロックの引数を参照することができる構文
-
_1~_9を使用することができる
h = {a: "gold", b: "silver"} puts h.map { puts "#{_1}, #{_2}"} # 実行結果 a, gold b, silver -