【发布时间】:2021-06-29 07:18:07
【问题描述】:
我正在尝试定义一个包含另一个方法的宏,然后将实例传递给宏。这是一个人为的例子......
module Wrapper
def wrap(method_name, options: {})
class_eval do
new_method = "_wrap_#{method_name}"
alias_method new_method, method_name
define_method method_name do |*args, &block|
# binding.pry
puts "Options... #{options}"
send new_method, *args, &block
end
end
end
end
class Thing
extend Wrapper
class << self
extend Wrapper
def bar
puts 'Bar!'
end
wrap :bar, options: -> { bar_options }
private
def bar_options
{ bar: 2 }
end
end
def foo
puts 'Foo!'
end
wrap :foo, options: -> { foo_options }
private
def foo_options
{ foo: 1 }
end
end
所以,我知道我可以从 define_method 中访问实例,但我认为我不应该在我的类中创建/重命名/别名方法以符合模块 - 我想通过配置中,可以这么说。我觉得instance_exec/eval 是我的朋友,在这里,但我似乎无法正确理解咒语。我也尝试将块传递给代码,但 yield 的行为与 proc 相同。也许binding,但出于某种原因,我根本无法理解。
这是来自define_method 通话中...
> Thing.new.foo
=> Options... #<Proc:0x00007fcb0782cfd8@(irb):24>
Foo!
> self.class
=> Thing
> self
=> #<Thing:0x00007fdfb78b88e8>
> options
=> #<Proc:0x00007fcb0782cfd8@(irb):24>
> options.call
# NameError: undefined local variable or method `foo_options` for Thing:Class
> foo_options
=> {:foo=>1}
> self.class.instance_eval &options
# NameError: undefined local variable or method `foo_options` for Thing:Class
> self.class.instance_exec &options
# NameError: undefined local variable or method `foo_options` for Thing:Class
我了解 proc 如何捕获范围以供以后使用,因此我可以在这里看到 proc/lambda 的使用是如何不正确的。当类加载时,wrap 方法“包装”该方法并在类级别捕获 foo_options 以供稍后调用 - 这在类级别不存在。调用 options: foo_options 做同样的事情,只是在加载时爆炸。
任何帮助...谢谢!
【问题讨论】:
-
这个例子/用例对我来说没有多大意义。您将
instance_eval和instance_exec作为类方法调用,但它们应该在实例上调用(例如,instance_eval docu)。您还覆盖了控制器中自动存在的参数的定义。可能想查看Concerns 上的文档并使用模型的用例对其进行更新。 -
已澄清。是的,这正是问题所在!
标签: ruby metaprogramming