我有以下课程:
class AwardBase
class AwardOne < AwardBase
class Post < ActiveRecord::Base
The Post是一个ActiveRecord,奖励有一个can_award? class方法,它接受一个post对象并检查它是否符合某些条件。如果是,则更新post.owner.awards。
我知道我可以使用Observer模式(我测试它并且代码工作正常)。但是,这需要我向模型添加其他代码。如果可能的话,我根本不想触摸模型。我想做的是像这样运行奖励检查(触发器将在课程加载时调用):
class AwardOne < AwardBase
trigger :post, :after_save
def self.can_award?(post)
...
end
end
上述代码的意图是它应该自动添加AwardOne.can_award?发布的post_save方法
基本上我要做的就是让trigger
来电相当于:
class Post < ActiveRecord::Base
after_save AwardOne.can_award?(self)
...
end
基本上是:
class Post < ActiveRecord::Base
after_save :check_award
def check_award
AwardOne.can_award?(self)
end
end
如何在不修改Post类的情况下执行此操作?
这是我所做的(似乎不起作用):
class AwardBase
def self.trigger (klass, active_record_event)
model_class = klass.to_class
this = self
model_class.instance_eval do
def award_callback
this.can_award?(self)
end
end
model_class.class_eval do
self.send(active_record_event, :award_callback)
end
end
def self.can_award? (model)
raise NotImplementedError
end
end
上述代码失败并显示错误:
NameError (undefined local variable or method `award_callback' for #<Post:0x002b57c04d52e0>):
答案 0 :(得分:1)
您应该考虑为什么要这样做。我认为它比使用观察者模式更糟糕。你违反了最不惊讶的原则(也称为最小惊讶原则)。
想象一下,这是一个更大的项目,我作为这个项目的新开发人员而来。我正在调试Post无法正确保存的问题。 当然,我将首先介绍模型的代码。我甚至可能会查看posts控制器的代码。这样做没有迹象表明保存帖子涉及第二类。由于我不知道AwardOne的代码是否涉及,因此我很难弄清楚问题是什么。 在这种情况下,实际上最好在控制器中执行此操作。这是最容易调试和理解的地方(因为模型已经有足够的责任并且通常更大)。
这是元编程的常见问题。大多数情况下,最好避免它,因为最不惊讶的原则。由于您需要调试一些问题,当您回到此代码时,您将很高兴从现在起一年内不使用它。你会忘记你做过什么“聪明”的事情。如果你没有一个很好的理由,那么只要坚持既定的惯例,它们就是有原因的。
如果没有别的,那么至少可以通过在Post模型中声明一些东西来找到一种优雅的方法。例如,在awardable
上注册ActiveRecord::Base
类方法。但最好的方法可能是在控制器中或通过服务对象。 AwardOne
负责处理Post
应如何保存!{/ p>
答案 1 :(得分:0)
因为您要将award_callback
添加为class
方法。我打赌如果你grep类方法将会注册。
所以改变你的代码如下。它应该工作正常。
model_class.class_eval do ## Changed to class_eval
def award_callback
this.can_award?(self)
end
end
如果听起来令人困惑,请举一个详细的例子。
class Test
end
Test.instance_eval do
def class_fun
p "from class method "
end
end
Test.class_eval do
def instance_fun
p "from instance method "
end
end
Test.methods.grep /class_fun/
# => [:class_fun]
Test.instance_methods.grep /instance_fun/
# => [:instance_fun]
Test.class_fun
# => "from class method "
Test.new.instance_fun
# => "from instance method "