Hashの値にあわせて動的にMethodを定義したいのです
例えば、
hash = {:foo => "foofoofoo"} obj = Hoge.create hash obj.foo # => "foofoofoo"
みたいなことは出来ないものか、と。method_missingを使えばいけそうだけど、あんまり使うなって「るびま本」に書いてあったような気がするので、別の手段を調べる。で、define_methodなるものを発見。っていうか、これも「るびま」に書いてあるやん。
早速、http://jp.rubyist.net/magazine/?0011-CodeReviewを参考にサンプルコードを書いてみる。
class Foo def self.create_methods(hash) hash.each do |key, val| define_method(key) do val end end end end Foo.create_methods :hoge => "hogehogehoge", :moge => "mogemogemoge" foo = Foo.new p foo.hoge # => "hogehogehoge" p foo.moge # => "mogemogemoge"
ん、動いた。
望ましくないのです → るびまのコードを真似る
でも、この続きで
Foo.create_methods :bar => "barbarbar" foo = Foo.new p foo.bar # => "barbarbar" p foo.hoge # => "hogehogehoge"
ってやると foo.hoge も有効になってて、これは望む結果ではない。Fooのクラス定義を共有してるから当たり前か〜と思いつつ、るびまの記事を読む。で、こんなコードを書いてみた。
class Foo class << self def define(&block) new_class(&block) end def methods(hash) hash.each do |key, val| define_method(key) do val end end end def new_class(&block) c = Class.new(::Foo) c.instance_variable_set :@field_specs, [] c.module_eval(&block) c end end end
使い方は
Foo_ = Foo.define do methods :hoge => "hogehogehoge", :moge => "mogemogemoge" end foo = Foo_.new p foo.hoge # => "hogehogehoge" p foo.moge # => "mogemogemoge"
な感じ。今度は、ここから更に
Foo_ = Foo.define do methods :bar => "barbarbar" end foo = Foo_.new p foo.bar # => "barbarbar" p foo.hoge # => undefined method
としてみると、ちゃんとfoo.hogeが無効(undefined)になっててグー。特異クラス(class << self)?ってのと、new_class(&block)辺りがポイントっぽい。
使いやすいようにごにょごにょごにょ
で、動かすことは出来たので、自分の使いやすい形にゴリゴリと書き換える。最終的に、以下の形になりますた。
module BuildFromHash def create(&block) c = new_class(&block) c.new end def methods(hash) hash.each do |key, val| define_method(key) do val end end new end private def new_class(&block) c = Class.new(self) c.instance_variable_set :@field_specs, [] c.module_eval(&block) c end end class Foo class << self include BuildFromHash end def org "orgorgorg" end end foo = Foo.create do methods :hoge => "hogehogehoge", :moge => "mogemogemoge" end p foo.hoge # => "hogehogehoge" p foo.moge # => "mogemogemoge" p foo.org # => "orgorgorg"
満足。