openstruct
ここを見ていてopenstructが出てきて分からなかったので調べました。
OpenStruct
OpenStructはRubyの標準ライブラリで、ハッシュのような機能を持つデータ構造を提供しています。
OpenStructクラスから作成したオブジェクトをレシーバにして未定義メソッド(x=)を呼ぶとmethod_missingで新しくメソッドが定義されて値が代入できます。メタですね。
アクセサメソッド(attr_accessor)のような働きをしてます。
> require 'ostruct' true > struct_var = OpenStruct.new #<OpenStruct> > struct_var.dog nil > struct_var.dog = "inu" "inu" > struct_var #<OpenStruct dog="inu">
instance_evalで中身を確認します
> struct_var.instance_eval{ p dog } "inu"
ちゃんといますね。
削除はdelete_field()でします。
> struct_var.delete_field("dog") "inu" > struct_var.dog nil
Rubyのソースコードを読む
initializeはハッシュで受け取ってます。
method_missingをモンキーパッチしてます。
#L197:L214 def method_missing(mid, *args) # :nodoc: len = args.length if mname = mid[/.*(?==\z)/m] if len != 1 raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) end modifiable?[new_ostruct_member!(mname)] = args[0] elsif len == 0 if @table.key?(mid) new_ostruct_member!(mid) unless frozen? @table[mid] end else err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args err.set_backtrace caller(1) raise err end end
mnameってなんやと思ってgrepしたらrespond_to_missing?をモンキーパッチしてました。
#L192:L195 def respond_to_missing?(mid, include_private = false) mname = mid.to_s.chomp("=").to_sym @table.key?(mname) || super end
mnameには”=“を除いたメソッド名が入ります。
method_missingに戻ると、最初の分岐はメソッド名を正規表現で抜いたやつがmnameと同じか調べてます。
ここがtrueかつ引数が1つなら203行目でメソッド定義します。
modifiableメソッドはこれらしいです。
http://docs.ruby-lang.org/ja/2.1.0/method/OpenStruct/i/modifiable.html
#L203 modifiable?[new_ostruct_member!(mname)] = args[0]
メソッドを定義するメソッド(new_ostruct_member)は173行目〜180行目にあります(メタプロだ・・・)
#new_ostruct_member def new_ostruct_member!(name) # :nodoc: name = name.to_sym unless singleton_class.method_defined?(name) define_singleton_method(name) { @table[name] } define_singleton_method("#{name}=") {|x| modifiable?[name] = x} end name end
レシーバのオブジェクトに特異メソッドを定義してます。
method_missingから追ってメソッド定義まで辿りました。