问题
I am trying to use a rake task that will run every night (using Heroku Scheduler) and update some attributes if some certain criteria is met.
A little logic about the application:
The application allows users to "take the challenge" and read a book a week over the course of a year. It's pretty simple: users sign up, create the first book that they will read for their first week, then can enter what they're going to read next week. After they've "queued" up next week's book, that form is then hidden until it's been 7 days since their first book was created. At that point, the book that was queued up gets moved to the top of the list and marked as 'currently reading', while the previous 'currently reading' book moves down to the second position in the list.
And IF a user doesn't 'queue' a book, the system will automatically create one if it's been 7 days since the latest 'currently reading' book was created.
Where I need some advice
The place I'm currently stuck is getting the books to update attributes if it's been 7 days since the last 'currently reading' book was created. Below is my book model and the method update_queue
is what gets called during the rake task. Running the rake task currently gives no errors and properly loops through the code, but it just doesn't change any attribute values. So I'm sure the code in the update_queue
method is not correct somewhere along the lines and I would love your help troubleshooting the reason why. And how I'm testing this is by adding a book then manually changing my system's date to 8 days ahead. Pretty barbaric, but I don't have a test suite written for this application & it's the easiest way to do it for me :)
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
My rake task looks like this (scheduler.rake placed in lib/tasks):
task :queue => :environment do
puts "Updating feed..."
@books = Book.all
@books.each do |book|
book.update_queue
end
puts "done."
end
回答1:
I would move the update_queue
logic to the User model and modify the Book model somewhat, and do something like this:
# 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
Now you can have the Heroku scheduler run something like this once a day:
User.all.each(&:update_queue)
Modify User.all
to only return active users if you need to.
Oh, and you can use the timecop gem to manipulate times and dates when testing.
回答2:
Probably the reason is that, the program flow is not going inside the if days_gone >= 7 condition.
you could check this in two ways
1 - Simple and easy way (but not a very good way)
use p statements every were with some meaning full text
Ex:
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 - Use ruby debugger and use debug points
in Gem file add
gem 'debugger'
and insert break points where ever needed
def update_queue
days_gone = (Date.today - Date.parse(Book.where(:reading => 1).last.created_at.to_s)).to_i
debugger
more help
HTH
来源:https://stackoverflow.com/questions/14116242/rails-update-attributes-using-rake-task-need-help-troubleshooting-model-metho