1

I am trying to update a column in my database if a book was created more than 25 seconds ago (when deploying, it will be 7 days ago, but I can't wait that long :)).

Models:

class Book < ActiveRecord::Base
  attr_accessible :author, :date, :description, :order, :title, :user_id, :author, :status, :queued
  belongs_to :user
end

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation, :remember_me, :user_id, :name, :bio
  has_many :books
end

Controller:

class UsersController < ApplicationController

def show
  @user = User.find(params[:id])

  @book = Book.new

  @books = Book.select("DISTINCT name, id") # Not used right now

  @sequence = @user.books

  @sequence.each do |book|
    book.created_at >= 25.seconds.ago ? book.queued = false : nil
  end
end

User show view:

<% @sequence.order("created_at DESC").where(:queued => false).each do |book| %>

Am I even close to getting this to work? What am I doing wrong? As you can see, I want to change the "queued" attribute to false after 25 seconds...

Edit

I've been told I need to use something like Heroku's Scheduler for this to work. Is there no way for this to update the database without something like that?

4

1 回答 1

1

You could take a different approach. Instead of having a field to know if it' queued use a scope (a query for queued books)

class Book < ActiveRecord::Base
  scope :queued, where('created_at <= ?', 25.seconds.ago)
  scope :not_queued, where('created_at > ?', 25.seconds.ago)
end

So now in your controller you can do something like:

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])

    @book = Book.new

    @not_queued_books = @user.books.not_queued
  end
end

Does it resolve your problem or you really need to have that column? Because with a scope it totally works and it's away more flexible and easy to implement!


If you want to see the ones not_queue by default Book.all and have a scope to see the queued ones here is an example

class Book < ActiveRecord::Base
  scope :queued, where('created_at <= ?', 25.seconds.ago)
  scope :not_queued, unscoped.where('created_at > ?', 25.seconds.ago)
  scope :date_desc, order("created_at DESC")
  default_scope not_queued
end


class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])

    @book = Book.new
  end
end

So now in your view just do this:

<% @user.books.date_desc.each do |book| %>

Notice now the default scope is books not queued and I moved the ordering also to a scope. Of course you can do this books.date_desc in your controller wich should preferable.


So as you said in the comment you have the issue of evaluation in scopes. Well, this is because the scope is "cached" when you start the app, so 25.seconds.ago will be relative to the time you started the app instead of the 25 seconds ago from now as you want to. There are plenty resources explaining this, and you should go check them if you don't understand what I'm saying. For example in this railscast http://railscasts.com/episodes/202-active-record-queries-in-rails-3

So what you have to do? You have to wrap your scopes with a lambda so it will be evaluated every time you use the scope instead of evaluating when you start the app.

Like this:

  scope :queued, lambda { where('created_at <= ?', 25.seconds.ago) }
  scope :not_queued, lambda { unscoped.where('created_at > ?', 25.seconds.ago) }
  scope :date_desc, order("created_at DESC")
  default_scope not_queued

Also instead of lambda { ... } you can use the new 1.9 shorthand -> { ... } instead

于 2012-12-30T18:23:53.350 回答