Search code examples
ruby-on-railsrubymethodsbefore-filter

Rails - Using a before_filter to run a method


I would like this before filter to run every time the page is loaded (for now) to check if an item is over 7 days old or not and if so, run some actions on it to update its attributes.

I have before_filter :update_it in the application controller. update_it is defined below that in the same controller as:

def update_it
  @books = Book.all
  @books.each do |book|
    book.update_queue
  end
end

Then update_queue is defined in the book model. Here's everything in the model that pertains to this:

scope :my_books, lambda {|user_id|
    {:conditions => {:user_id => user_id}}  
  }

  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
            currently_reading = Book.my_books(user_id).reading_books.last
            currently_reading.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 relationship is that a book belongs_to a user and a user has_many books. I'm showing these books in the view through the user show view, not that it matters though.

So the errors I keep getting are that move_from_queue_to_reading and move_from_reading_to_list are undefined methods. How can this be? I'm clearly defining them and then calling them below. I really am at a loss and would greatly appreciate some insight into what I'm doing wrong. I'm a beginner here, so any structured criticism would be great :)

EDIT

The exact error message I get and stack trace is as follows:

NoMethodError in UsersController#show
undefined method `move_from_queue_to_reading' for nil:NilClass

app/models/book.rb:41:in `update_queue'
app/controllers/application_controller.rb:22:in `block in update_it'
app/controllers/application_controller.rb:21:in `each'
app/controllers/application_controller.rb:21:in `update_it'

Solution

  • I suspect that the collection returned is an empty array (which is still 'truthy' when tested). So calling .last is returning nil to the new_book and currently_reading local variables. Try changing:

    if Book.my_books(user_id).where(:queued => true)
    

    to:

    if Book.my_books(user_id).where(:queued => true).exists?
    

    Additionally, you are modifying the scope when finding currently_reading. This can potentially cause the query to again return no results. Change:

    currently_reading.move_from_reading_to_list
    

    to:

    currently_reading.move_from_reading_to_list if currently_reading