1

私はタスクマネージャーを作成しており、「完了」のブール属性を持っています。「finished」を true に切り替えたときに「finished_at」日付を実装するようにセッターをオーバーライドしようとしました。

しかし、私はいくつかの複雑な結果を得ています。ブラウザでは動作しませんが、rspec テストでは動作します。

私を助けてください。

class TasksController < ApplicationController 
# ...
def update
  # ..
  if @task.update_attributes(params[:task]) # where params[:task][:finished] is true
# ...
end

class Task < ActiveRecord::Base
#...
  def finished=(f)
   write_attribute :finished, f
   write_attribute :finished_at, f == true ? DateTime.now : nil
  end
end

# and in rspec i have
describe "when marked as finished" do
  before { @task.update_attributes(finished: true) }

  its(:finished_at) { should_not be_nil }
  its(:finished_at) { should > (DateTime.now - 1.minute) }

  describe "and then marked as unfinished" do 
    before { @task.update_attributes(finished: false) }
    its(:finished_at) { should be_nil }
  end
end

ブラウザで「UPDATE "tasks" SET "finished" = 't', "updated_at" = '2012-10-02 18:55:07.220361' WHERE "tasks"."id" = 17" を実行します。

Railsコンソールでは、update_attributesと同じ結果になりました。

しかし、update_attributes を使用した rspec では、「UPDATE "tasks" SET "finished" = 't', "finished_at" = '2012-10-02 18:36:47.725813', "updated_at" = '2012-10-02 18: 36:51.607143' WHERE "タスク"."id" = 1"

だから私は同じ方法を使用しますが、それはいくつかの理由でrspecでのみ機能します...


最新のレールと最新の仕様を使用します (rc やベータ版ではありません)。

解決

編集する必要はありませんでした。ヒントをくれた@Frederick Cheungに感謝します。「write_attribute」よりも「self[:attr]」の方が好きだということに気付きました。見栄えが良くなりました。

def finished=(value)
  self[:finished] = value
  self[:finished_at] = (self.finished? ? Time.now.utc : nil)
end
4

3 に答える 3

1

セッターには、に渡される値が渡されupdate_attributesます。特に、これがフォームの送信によってトリガーされる場合 (通常の Rails フォーム ヘルパーを使用している場合)fは、実際には "0" または "1" になるため、 との比較trueは常に false になります。

finished?最も簡単な方法は、 への最初の呼び出しの後に の値をチェックすることですwrite_attribute。これにより、Rails は送信された値を true/false に変換できます。また、これを行うのは無作法です== true-テストしているものが実際に真ではなく真の値を返す場合、これは壊れます(たとえば、文字列の =~ は、一致がある場合に整数を返します)

于 2012-10-03T03:51:54.893 に答える
1

ActiveRecord Dirty Tracking を使用して、この変更の通知を受けることができます。

http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

class Task < ActiveRecord::Base

  before_save :toggle_finished_at

  def toggle_finished_at
    if finished_changed?
      before = changes['finished'][0]
      after = changes['finished'][1]
      # transition from finished => not-finished
      if before == true && after == false
        self.finished_at = nil
      end
      # transition from not finished => finished
      if before == false && after == true
        self.finished_at = Time.now.utc
      end
    end
  end

end
于 2012-10-03T04:58:57.793 に答える
0

これは、ステート マシンのユース ケースです。:finish!状態を変更し、その他必要なことを行うように構成されたイベント (メソッド) を呼び出します。

https://github.com/pluginaweek/state_machine/

于 2012-10-02T23:10:27.040 に答える