Как избежать использования обратных вызовов в рельсах - PullRequest
1 голос
/ 05 января 2012

В Rails есть такая штука обратного вызова, которая давным-давно не давала мне покоя.Дело в том, что они мне не нравятся.Главным образом потому, что они замедляют мои тесты, так как мне приходится обращаться к базе данных в моих модульных тестах, чтобы сохранить объект, который вызывает обратные вызовы (например, after_save).

Я собираюсь использоватьПростой пример того, что я хочу сделать, чтобы прояснить себя.Предположим, у меня есть аккаунт, и каждый раз, когда я делаю вывод, мне нужно вычесть сумму из баланса.Мои модели:

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

Так что, если бы я должен был проверить это поведение, мне нужно было бы (используя RSpec):

describe Withdraw

  it 'updates the account balance' do
    account = Account.create({ :name => "foo", :balance => 100 })
    withdraw.create({ :amount => 10, :account => account })
    account.balance.should == 90
  end
end

Обратите внимание, что мне нужно было нажатьбаза данных дважды в этом модульном тесте.Это было бы нормально в простом проекте, но он начинает становиться проблемой, когда набор тестов растет (500 примеров или около того).

Я мог бы сделать метод update_account_balance общедоступным и вызвать его из контроллера,но я думаю, что это бизнес-логика и она не входит в код контроллера.

Я пытался найти решение, но не смог его найти.Как вы, ребята с быстрыми тестами, решаете эту проблему?

Заранее спасибо.

1 Ответ

2 голосов
/ 05 января 2012

Я думаю, что ваша проблема в том, что вы сделали действие неявным withdrawal (а именно, это происходит в результате создаваемого объекта Withdraw)

Я думаю, что есть лучший способ сделатьэто.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...