开发者

Rails app needs to perform task once a month

I'm making an app called book club. Lots of books with votes will be in the system. Each month, on the first of the month, I need the system to automatically promote the book with the highest number of votes to be the "book of the month". The logic to promote a book and ensure only one book of the month exists has already been implemented.

book.promote!

Lovely, huh?

I have me a test case hurr

    Given the following books exist:
  | title            开发者_StackOverflow   | author            | year_published | votes | created_at        |
  | Lord of the Flies   | William Golding   | 1954           | 18    | January 12, 2010  |
  | The Virgin Suicides | Jeffrey Eugenides | 1993           | 12    | February 15, 2010 |
  | Island              | Richard Laymon    | 1991           | 6     | November 22, 2009 |
And the book "Lord of the Flies" is the current book of the month
And the date is "February 24, 2010"
Then the book "Lord of the Flies" should be the current book of the month
When the date is "March 1, 2010"
And I am on the home page
Then I should see "This Month's Book of the Month Club Book"
And I should see "The Virgin Suicides"
And the book "The Virgin Suicides" should be the current book of the month
And the book "Lord of the Flies" should not be the current book of the month
And the book "Island" should not be the current book of the month

And I'm trying to get that passing.

So the question is, how do I best implement an automated, once a month update that can be tested by this scenario?

Cron is a bit too sloppy for my taste. I would like a more portable solution.

delayed_job/Resque seems a bit too heavy for the situation. Also I'm a bit unsure of how to get them to run jobs once a month.

Just looking for a simple, but robust, and TESTABLE solution.

Cheers, as always!


I use delayed_job for this type of requirements.

class Book
  def promote
    # code for the promote method..

  ensure
    # re-run the task in another 30 days.
    promote
  end
  # Date.today.nextmonth.beginning_of_month will be evaluated 
  #when promote is called
  handle_asynchronously :promote, :run_at => Proc.new { 
    Date.today.next_month.beginning_of_month 
  }

end


A nice way to manage periodic tasks is with whenever: https://github.com/javan/whenever

I does use OS cron, but all the config lives inside your rails app, so it's nicer to work with.

However, while it's quite appropriate for maintenance type tasks where, say, if for some reason something doesn't run exactly at the top of the hour or gets skipped over for some reason, the slack will be picked up the next time, in your case where the periodic thing is part of the task, it's a tougher call.

Maybe the app can just "always" ask itself if it's the first of the month yet when the rankings view loads, and if it is, it will do the tally, using only votes within the appropriate date range?

And to test time-dependent behavior, checkout out timecop: https://github.com/jtrupiano/timecop


As John started to say Cron/whenever is the way to go here. Other background daemons require a separate process that will be idle most of the time. You shouldn't have any portability issues with cron unless you're concerned about running on Windows.


We are talking about 2 different things here:

  1. A job that runs and performs task for the system: That would be through rake, it is pretty reliable and well tested.
  2. A scheduler, which runs this task on a schedule you specify. It seems you are looking for alternatives to cron. You can try launchd for this.


delayed_job is really a good choice. You are currently testing with a small number of books, so the promote calculation is done fairly quickly.

When your app scales, you will want to run this calculation in a separate worker process to reduce the impact on the User Experience. delayed_job will kill two birds with one stone: provide a scheduling mechanism and offload the promote calculation to a separate worker process.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜