1

我正在制作一个任务管理器,并有一个“完成”的布尔属性。当我将“完成”切换为真时,我试图覆盖设置器以实现“完成_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'在哪里“任务”。“id”= 1“

所以我使用相同的方法,但它只在 rspec 中工作了一些 reson...


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

解决方案

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

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

3 回答 3

1

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

finished?最简单的方法是在第一次调用 之后检查 的值write_attribute,以便 rails 可以将提交的值转换为真/假。这样做也是不可靠的== 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 回答