覆盖setter不适用于update_attributes

时间:2012-10-02 19:24:32

标签: ruby-on-rails activerecord rspec override rspec-rails

我正在创建一个任务管理器,并为'finished'设置了一个布尔属性。当我将'finished'切换为true时,我试图覆盖setter来实现'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”任务“SET”完成“='t',”updated_at“='2012-10-02 18:55:07.220361'WHERE”任务“。”id“= 17”

并且在rails控制台中我得到了与update_attributes相同的内容。

但是在带有update_attributes的rspec中,我得到“UPDATE”任务“SET”完成“='t',”finished_at“='2012-10-02 18:36:47.725813',”updated_at“=''2012-10- 02 18:36:51.607143'WHERE'任务“。”id“= 1”

所以我使用相同的方法,但它仅在rspec中用于某些共振......


使用最新的rails和最新规范(不是任何rc或beta)。

解决方案

我不需要编辑。谢谢@Frederick Cheung的暗示。 我注意到我确实喜欢“self [:attr]”而不是“write_attribute”。看起来更好imo。

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

3 个答案:

答案 0 :(得分:1)

您的setter会在传递给update_attributes时传递值。特别是当这是由表单提交触发时(假设您使用常规rails表单助手)f实际上将是“0”或“1”,因此与true的比较将永远是假的。

最简单的方法是在第一次调用finished?后检查write_attribute的值,以便rails可以将提交的值转换为true / false。做== true也很麻烦 - 如果你测试的东西返回一个真实的值而不是实际的真值,这将会破坏(例如=〜字符串在匹配时返回一个整数)

答案 1 :(得分: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

答案 2 :(得分:0)

这是状态机的用例。您调用:finish!事件(一种方法),该事件被配置为更改状态并执行其他任何操作。

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