我正在尝试使用一个rake任务,该任务将每天晚上运行(使用Heroku Scheduler)并更新一些属性,如果满足某些特定条件。
关于应用程序的一点逻辑:
该应用程序允许用户"接受挑战",在一年的时间里每周读一本书。这很简单:用户注册,创建他们第一周要读的第一本书,然后输入他们下周要读的书。在他们"排队"了下周的书之后,该表单将被隐藏,直到他们的第一本书创建后7天。此时,排队的书被移到列表的顶部,并标记为"当前正在阅读",而前一本"当前正在阅读"的书则移到列表的第二个位置。
如果用户没有"排队"一本书,系统将自动创建一个7天,如果最近的"当前阅读"的书被创建。
Where I need some advice
我目前卡住的地方是让书更新属性,如果它已经7天,因为最后一个"当前阅读"的书被创建。下面是我的图书模型,方法update_queue
是在rake任务期间被调用的。当前运行rake任务不会产生错误,并正确地循环代码,但它只是不改变任何属性值。所以我确信update_queue
方法中的代码在某些地方是不正确的,我希望你能帮助排除原因。我的测试方法是添加一本书,然后手动将系统日期更改为提前8天。相当野蛮,但我没有为这个应用程序编写测试套件&这对我来说是最简单的方法了:)
class Book < ActiveRecord::Base
attr_accessible :author, :date, :order, :title, :user_id, :status, :queued, :reading
belongs_to :user
scope :reading_books, lambda {
{:conditions => {:reading => 1}}
}
scope :latest_first, lambda {
{:order => "created_at DESC"}
}
def move_from_queue_to_reading
self.update_attributes(:queued => false, :reading => 1);
end
def move_from_reading_to_list
self.update_attributes(:reading => 0);
end
def update_queue
days_gone = (Date.today - Date.parse(Book.where(:reading => 1).last.created_at.to_s)).to_i
# If been 7 days since last 'currently reading' book created
if days_gone >= 7
# If there's a queued book, move it to 'currently reading'
if Book.my_books(user_id).where(:queued => true)
new_book = Book.my_books(user_id).latest_first.where(:queued => true).last
new_book.move_from_queue_to_reading
Book.my_books(user_id).reading_books.move_from_reading_to_list
# Otherwise, create a new one
else
Book.my_books(user_id).create(:title => "Sample book", :reading => 1)
end
end
end
我的rake任务看起来像这样(调度程序。
task :queue => :environment do
puts "Updating feed..."
@books = Book.all
@books.each do |book|
book.update_queue
end
puts "done."
end
我会将update_queue
逻辑移动到User模型并稍微修改Book模型,并做如下操作:
# in book.rb
# change :reading Boolean field to :reading_at Timestamp
scope :queued, where(:queued => true)
scope :earliest_first, order("books.created_at")
scope :reading_books, where("books.reading_at IS NOT NULL")
def move_from_queue_to_reading
self.update_attributes(:queued => false, :reading_at => Time.current);
end
def move_from_reading_to_list
self.update_attributes(:reading_at => nil);
end
# in user.rb
def update_queue
reading_book = books.reading_books.first
# there is an edge-case where reading_book can't be found
# for the moment we will simply exit and not address it
return unless reading_book
days_gone = Date.today - reading_book.reading_at.to_date
# If less than 7 days since last 'currently reading' book created then exit
return if days_gone < 7
# wrap modifications in a transaction so they can be rolled back together
# if an error occurs
transaction do
# First deal with the 'currently reading' book if there is one
reading_book.move_from_reading_to_list
# If there's a queued book, move it to 'currently reading'
if books.queued.exists?
books.queued.earliest_first.first.move_from_queue_to_reading
# Otherwise, create a new one
else
books.create(:title => "Sample book", :reading_at => Time.current)
end
end
end
现在可以让Heroku调度器每天运行一次:
User.all.each(&:update_queue)
修改User.all
,如果需要,只返回活动用户。
哦,您还可以在测试时使用timeop gem来操作时间和日期。
原因可能是程序流没有进入if days_gone>= 7条件。
你可以用两种方式检查
1 -简单和容易的方法(但不是一个很好的方法)
use p语句每句都有意义全文
,
def update_queue
days_gone = (Date.today - Date.parse(Book.where(:reading => 1).last.created_at.to_s)).to_i
p "days gone : #{days_gone}"
# If been 7 days since last 'currently reading' book created
if days_gone >= 7
p "inside days_gone >= 7"
etc...
2 -使用ruby调试器和调试点
在Gem文件中添加gem 'debugger'
并在需要的地方插入断点
def update_queue
days_gone = (Date.today - Date.parse(Book.where(:reading => 1).last.created_at.to_s)).to_i
debugger
的更多帮助HTH