一部ソースコードは省略しています。
def delegate(*methods)
module_eval(<<-EOS, file, line - 1)
def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block)
#{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
rescue NoMethodError # rescue NoMethodError
if #{to}.nil? # if client.nil?
#{exception} # # add helpful message to the exception
else # else
raise # raise
end # end
end # end
EOS
end
これ、メソッドの中でmodule_evalを使っているんですよね。え、そんなことできたっけ?って感じでした。 例えば、これとか。
class Zombie
def hi
module_eval <<-EOS
def hello_zombie
puts "Hello Zombie"
end
EOS
end
end
Zombie.new.hi
moduke_eval2.rb:7:in `hi': undefined method `module_eval' for #class Zombie
module_eval <<-EOS
def hello_zombie
puts "Hello Zombie"
end
EOS
end
Zombie.new.hello_zombie
じゃあ、Railsではどうやってメソッドの中にmodule_evalを入れているのか?もう一度、delegation.rbを見てみる。
class Module endなんとModuleを再オープンしていた。class Delegationではないらしい。
それをquerying.rbでrequireして使っていた。
へ〜、こうやるとメソッドの中で使えるようになるんだ〜、ってことで試してみる。
class Module
def delegate(value)
module_eval <<-EOS
def #{value}
p "hello Zombie"
end
EOS
end
end
module Human
delegate("hello")
end
class Zombie
include Human
end
Zombie.new.hello
Ruby では、モジュールもオブジェクトの一つで Module クラスのインスタンスです。なのでclass Moduleを再オープンしてdelegateを定義。 もうちょっと、確認する。
Module.methods.grep(/eval/) Module.instance_methods.grep(/eval/)実行結果
[:module_eval, :class_eval, :instance_eval]
[:module_eval, :class_eval, :instance_eval]
class Zombie end Zombie.methods.grep(/eval/) Zombie.instance_methods.grep(/eval/)実行結果
[:module_eval, :class_eval, :instance_eval]
[:instance_eval]
なるほど。つまり、インスタンスメソッドとしてmodule_evalを持っていれば、
メソッド定義の中でmodule_evalを使えるってことか。
そんなわけで納得できました。
Railsのソースコードは勉強になりますね。
0 件のコメント:
コメントを投稿