Ruby on rails 如何避免在rails中使用回调
Rails中的回调问题困扰了我一段时间。问题是,我不喜欢它们。主要是因为它们减慢了我的测试速度,因为我必须在单元测试中点击数据库才能持久化对象,这会触发回调(例如,在保存之后) 我将用一个简单的例子来说明我想做什么,让自己更清楚。假设我有一个账户,每次提款时,我都要从余额中扣除金额。我的模型是:Ruby on rails 如何避免在rails中使用回调,ruby-on-rails,ruby-on-rails-3,ruby-on-rails-3.1,Ruby On Rails,Ruby On Rails 3,Ruby On Rails 3.1,Rails中的回调问题困扰了我一段时间。问题是,我不喜欢它们。主要是因为它们减慢了我的测试速度,因为我必须在单元测试中点击数据库才能持久化对象,这会触发回调(例如,在保存之后) 我将用一个简单的例子来说明我想做什么,让自己更清楚。假设我有一个账户,每次提款时,我都要从余额中扣除金额。我的模型是: class Withdraw < ActiveRecord::Base belongs_to :account after_save :update_account_balance
class Withdraw < ActiveRecord::Base
belongs_to :account
after_save :update_account_balance
private
def update_account_balance
self.account.balance -= self.amount
self.account.save
end
end
class Account < ActiveRecord::Base
has_many :withdraws
end
注意,在单元测试中,我必须两次访问数据库。这在一个简单的项目中是可以的,但是当测试套件增长时(大约500个示例),它开始成为一种负担
我可以将update\u account\u balance
方法公开,并从控制器调用它,但我认为这是业务逻辑,不属于控制器代码
我试着在谷歌上搜索解决方案,但找不到。你们这些拥有快速测试套件的人是如何解决这个问题的
提前感谢。我认为您的问题在于您已将执行
提取的操作隐式化(即,它是通过创建提取对象而发生的)
我认为有更好的办法
account.withdraw!(1000)
代码可能是这样的
class Account
def withdraw!(amount)
transaction do
withdrawal = self.withdrawals.build(:amount => amount)
self.subtract_balance(amount)
withdrawal.save!
end
end
private
def subtract_balance(amount)
connection.execute(
"UPDATE #{self.class.table_name} SET balance = balance - #{amount} WHERE id = #{self.id}"
)
end
end
如果要跳过回调,可以使用类似于my\u model.save(false)
的方法。顺便说一句,你应该在更新账户余额后保存账户。我的意思是你应该更新账户余额,比如set balance=balance amount
@taro这只是我为了解释我的观点而编的一个例子,不是实际运行的代码,而是我添加了self.account.save
,所以我的例子在语义上是正确的。现在,我真的不明白跳过回调会有什么帮助。在我看来,不管怎样,这个模型很粗略,要求账户提取金额会更有意义,数学可以在那里完成。这确实有助于让我的意图更明确,但它仍然会保留对象以更新账户余额,这正是我一开始想要避免的。好吧,你不需要某种收据来储存这个吗?如果不这样做,只需跳过构建对象的行即可。但在任何实际业务中,您都需要考虑这些因素。此外,我不认为您应该根据测试的速度来做出编码决策。哦,但我知道。如果我的测试驱动着我的代码,我应该根据可测试性做出编码决策。如果我的测试没有效率,我的代码也没有效率。我的(有限的)经验表明,它会产生更易于维护的代码。就拿这个例子来说。如果我不担心我的测试,我最终会使用回调,这在我看来是不好的——它们隐藏了你的意图,并且很难调试。
class Account
def withdraw!(amount)
transaction do
withdrawal = self.withdrawals.build(:amount => amount)
self.subtract_balance(amount)
withdrawal.save!
end
end
private
def subtract_balance(amount)
connection.execute(
"UPDATE #{self.class.table_name} SET balance = balance - #{amount} WHERE id = #{self.id}"
)
end
end