use*_*603 3 ruby metaprogramming
我有一个名为 的模块Notifier。
module Notifier
def self.prepended(host_class)
host_class.extend(ClassMethods)
end
module ClassMethods
def emit_after(*methods)
methods.each do |method|
define_method(method) do |thing, block|
r = super(thing)
block.call
r
end
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
它公开了一个类方法emit_after。我这样使用它:
class Player
prepend Notifier
attr_reader :inventory
emit_after :take
def take(thing)
# ...
end
end
Run Code Online (Sandbox Code Playgroud)
目的是通过调用emit_after :take,模块#take用它自己的方法覆盖。
但实例方法没有被重写。
但是,我可以显式覆盖它而不使用ClassMethods
module Notifier
def self.prepended(host_class)
define_method(:take) do |thing, block|
r = super(thing)
block.call
r
end
end
class Player
prepend Notifier
attr_reader :inventory
def take(thing)
# ...
end
end
#> @player.take @apple, -> { puts "Taking apple" }
#Taking apple
#=> #<Inventory:0x00007fe35f608a98...
Run Code Online (Sandbox Code Playgroud)
我知道它ClassMethods#emit_after被调用,所以我假设该方法正在被定义,但它永远不会被调用。
我想动态创建方法。如何确保生成方法覆盖我的实例方法?
@Konstantin Strukov的解决方案很好,但可能有点令人困惑。因此,我建议另一种解决方案,它更像原来的解决方案。
您的第一个目标是向您的类添加一个类方法( emit_after)。为此,您应该使用extend不带任何钩子的方法,例如self.prepended(),self.included()或self.extended()。
prepend以及include用于添加或重写实例方法。但这是你的第二个目标,当你打电话时它就会发生emit_after。所以你不应该在扩展你的类时使用prependor include。
module Notifier
def emit_after(*methods)
prepend(Module.new do
methods.each do |method|
define_method(method) do |thing, &block|
super(thing)
block.call if block
end
end
end)
end
end
class Player
extend Notifier
emit_after :take
def take(thing)
puts thing
end
end
Player.new.take("foo") { puts "bar" }
# foo
# bar
# => nil
Run Code Online (Sandbox Code Playgroud)
现在很明显,您调用是extend Notifier为了添加emit_after类方法,并且所有魔法都隐藏在该方法中。
| 归档时间: |
|
| 查看次数: |
1821 次 |
| 最近记录: |