我有以下内容:
module Thing
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
attr_reader :things
def has_things(*args)
options = args.extract_options! # Ruby on Rails: pops the last arg if it's a Hash
# Get a list of the things (Symbols only)
@things = args.select { |p| p.is_a?(Symbol) }
include InstanceMethods
end
end
module InstanceMethods
self.class.things.each do |thing_name| # !!! Problem is here, explaination below
define_method "foo_for_thing_#{thing_name}" do
"bar for thing #{thing_name}"
end
end
end
end
在另一个混合了Thing模块的类中:
class Group
has_things :one, :two, :option => "something"
end
在类中调用has_things
时,我希望动态的“foo_for_thing_one”和“foo_for_thing_two”实例方法可用。例如:
@group = Group.new
@group.foo_for_thing_one # => "bar for thing one"
@group.foo_for_thing_two # => "bar for thing two"
但是,我收到以下错误:
`<module:InstanceMethods>': undefined method `things' for Module:Class (NoMethodError)
我意识到上面指出的问题行中的“self”(InstanceMethods模块的第一行)引用了InstanceMethods模块。
如何引用“things”类方法(在此示例中返回[:one,:two]),以便我可以遍历并为每个方法创建动态实例方法?谢谢。或者如果您有其他建议来完成此任务,请告诉我。
答案 0 :(得分:19)
将InstanceMethods的内容放在has_things
方法定义中,然后删除InstanceMethods模块。
您对InstanceMethods-ClassMethods反模式的使用在这里尤其没有根据,因此货物结果增加了您对范围和上下文的困惑。做最简单的事可能有用。不要在没有批判性思维的情况下复制别人的代码。
您需要的唯一模块是ClassMethods,它应该被赋予一个有用的名称,不应该被包含在内,而是用于扩展要授予has_things
功能的类。这是最简单的事情:
module HasThings
def has_things(*args)
args.each do |thing|
define_method "thing_#{thing}" do
"this is thing #{thing}"
end
end
end
end
class ThingWithThings
extend HasThings
has_things :foo
end
ThingWithThings.new.thing_foo # => "this is thing foo"
只在需要时添加复杂性(选项提取,输入规范化等)。代码及时,不仅仅是以防万一。