我有一个rails 4 app,它有一个警报模型和与每个警报相关的测试。
创建新警报时,我有一个after_create过滤器,它使用实例方法创建一个新测试:
class Alert < ActiveRecord::Base
has_many :tests
after_create :create_test
private
def create_test
#bunch of code using external api to get some data
Test.create
end
end
我还有一个cron作业,我想用它来为每个警报创建一个新的测试。我的计划是有一个类方法来做到这一点:
def self.scheduled_test_creation
@alerts = Alert.all
@alerts.each do |a|
a.create_test
end
end
由于实例方法是私有的,因此无法工作。我知道我可以使用send来解决这个问题。或者我可以公开这些方法。或者我可以在实例方法中重写那一堆api代码。
我不确定最好的方法是什么。我不想两次写相同的代码,我想确保是好的做法。也许在这种情况下,这些方法不一定是私有的 - 我知道公共/私人/受保护之间的区别,但我并不真正理解何时方法应该是私有/受保护的。
非常感谢任何帮助
答案 0 :(得分:2)
我喜欢多个模型之间交互的服务类。回调可能使逻辑很难遵循。
例如:
class AlertCreator
def initialize(alert)
@alert = alert
end
def call
if @alert.save
alert_test = TestBuilder.new(@alert).call
alert_test.save
true
end
end
end
class TestBuilder
def initialize(alert)
@alert = alert
end
def call
# external API interaction stuff
# return unsaved test
end
end
在您的控制器中,您可以拨打AlertCreator.new(@alert).call
而不是通常的@alert.save
。
答案 1 :(得分:0)
我同意@SergioTulentsev:虽然从长远来看,你可以通过将这个逻辑分解为服务类来提供更好的服务,在短期内你只需就不应该将方法设为私有它需要在实例之外调用。
在某些情况下,实际上想要访问私有方法,例如在测试期间验证对象状态时。这很容易做到:
@alert.instance_eval{ create_test }
您甚至可以通过这种方式获取或更改实例变量:
@alert.instance_eval{ @has_code_smells = true }
一般来说,如果你觉得有必要这样做,那就是一种警告气味,你的逻辑需要重新考虑。忽略那种气味是将Ruby从一种美妙的语言变成一种过于强大的语言,让你可以用脚射击自己。但它是可行的。