Class_eval 不在每个块内工作
我定义了一个模块来扩展 ActiveRecord。
就我而言,我必须使用作为 compound_datetime
类方法的参数给出的符号来生成实例方法。当在 each
块外部而不是在其内部调用 class_eval
时,它会起作用;在后一种情况下,我收到未定义的方法错误。
有谁知道我做错了什么?
module DateTimeComposer
mattr_accessor :attrs
@@attrs = []
module ActiveRecordExtensions
module ClassMethods
def compound_datetime(*attrs)
DateTimeComposer::attrs = attrs
include ActiveRecordExtensions::InstanceMethods
end
end
module InstanceMethods
def datetime_compounds
DateTimeComposer::attrs
end
def self.define_compounds(attrs)
attrs.each do |attr|
class_eval <<-METHODS
def #{attr.to_s}_to()
puts 'tes'
end
METHODS
end
end
define_compounds(DateTimeComposer::attrs)
end
end
end
class Account < ActiveRecord::Base
compound_datetime :sales_at, :published_at
end
当我尝试访问该方法时:
Account.new.sales_at_to
我收到 MethodError: undefined method sales_at_to for #
。
I have defined a module to extend ActiveRecord.
In my case I have to generate instance methods with the symbols given as arguments to the compound_datetime
class method. It works when class_eval
is called outside the each
block but not inside it; in the latter case I get an undefined method error.
Does anyone know what I am doing wrong?
module DateTimeComposer
mattr_accessor :attrs
@@attrs = []
module ActiveRecordExtensions
module ClassMethods
def compound_datetime(*attrs)
DateTimeComposer::attrs = attrs
include ActiveRecordExtensions::InstanceMethods
end
end
module InstanceMethods
def datetime_compounds
DateTimeComposer::attrs
end
def self.define_compounds(attrs)
attrs.each do |attr|
class_eval <<-METHODS
def #{attr.to_s}_to()
puts 'tes'
end
METHODS
end
end
define_compounds(DateTimeComposer::attrs)
end
end
end
class Account < ActiveRecord::Base
compound_datetime :sales_at, :published_at
end
When I try to access the method:
Account.new.sales_at_to
I get a MethodError: undefined method sales_at_to for #<Account:0x007fd7910235a8>
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您在
InstanceMethods
模块定义末尾调用define_compounds(DateTimeComposer::attrs)
。此时,在代码中,attrs
仍然是一个空数组,self
是InstanceMethods
模块。这意味着不会定义任何方法,即使定义了方法,它们也会绑定到 InstanceMethods 的元类,使它们成为该模块的类方法,而不是该模块的实例方法您的
Account
类。发生这种情况是因为
InstanceMethods
模块定义内的方法调用按照 Ruby 解释器所看到的方式进行计算,而当您调用include ActiveRecordExtensions::InstanceMethods
不会 >。这意味着可以在最不寻常的地方运行任意代码,例如在类定义中 。要解决该问题,您可以使用
included
回调< /a> 由 ruby 提供,每当一个模块包含在另一个模块中时就会调用它:作为附加建议,您应该能够通过在调用
compound_datetime
时简单地定义方法来实现相同的结果,因此消除对attrs
全局类变量。但是,如果您必须访问声明为复合日期时间的字段,则应使用类实例变量,这些变量对于每个类都是唯一的,并且不在层次结构中共享:
You are calling
define_compounds(DateTimeComposer::attrs)
at the end of theInstanceMethods
module definition. At that point in the code,attrs
is still an empty array, andself
is theInstanceMethods
module.This means no methods will be defined, and even if they were, they would be bound to
InstanceMethods
's metaclass, making them class methods of that module, not instance methods of yourAccount
class.This happens because method calls inside the
InstanceMethods
module definition are evaluated as they are seen by the ruby interpreter, not when you callinclude ActiveRecordExtensions::InstanceMethods
. An implication of this is that it is possible to run arbitrary code in the most unusual of places, such as within a class definition.To solve the problem, you could use the
included
callback provided by ruby, which is called whenever a module is included in another:As an additional suggestion, you should be able to achieve the same result by simply defining the methods when
compound_datetime
is called, thus eliminating the dependence on theattrs
global class variable.However, if you must have access to the fields which were declared as compound datetime, you should use class instance variables, which are unique to each class and not shared on the hierarchy: