Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何避免在rails中使用回调_Ruby On Rails_Ruby On Rails 3_Ruby On Rails 3.1 - Fatal编程技术网

Ruby on 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

Rails中的回调问题困扰了我一段时间。问题是,我不喜欢它们。主要是因为它们减慢了我的测试速度,因为我必须在单元测试中点击数据库才能持久化对象,这会触发回调(例如,在保存之后)

我将用一个简单的例子来说明我想做什么,让自己更清楚。假设我有一个账户,每次提款时,我都要从余额中扣除金额。我的模型是:

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