Permalink
Browse files

Massive changes for 1.0. Scheduler no longer runs as part of the rail…

…s app, now part of the scheduler daemon gem.

Also scheduled tasks folder has moved to RAILS_ROOT/lib/scheduled_tasks/
  • Loading branch information...
ssoroka committed Oct 29, 2009
1 parent 600d233 commit 4d7d94ec5a2adf48f2c3507ac5263b33724cb4f3
View
10 CHANGES
@@ -1,10 +0,0 @@
-Version 0.3.0
-
-add a default task to delete old AR sessions
-
-Version 0.2.0
-
-- syntax to load up only selected tasks for testing, by partial name:
-
- ruby daemons/bin/task_runner.rb run -- --only=toadcamp,newsfeed
-
View
@@ -1,7 +1,7 @@
Scheduler Daemon
================
-Rails 2.3.2 compatible scheduler daemon. Replaces cron/rake pattern of periodically running rake tasks
+Rails 2.2+ compatible scheduler daemon. Replaces cron/rake pattern of periodically running rake tasks
to perform maintenance tasks in Rails apps. Scheduler Daemon is made specifically for your Rails app,
and only loads the environment once, no matter how many tasks run.
@@ -10,30 +10,39 @@ What's so great about it? Well, I'm glad you asked!
- Only loads your Rails environment once on daemon start, not every time a task is run
- Allows you to easily deploy the scheduled tasks with your Rails app instead of depending on an
administrator to update crontab
+- Can be installed as a gem or a plugin (I suggest gem)
- It doesn't use rake or cron!
- Gets you up and running with your own daemon in under 2 minutes
+- Specially designed to work with your rails app!
Setup
=====
-Install the plugin
+Install as a gem or plugin.
- script/plugin install git://github.com/ssoroka/scheduler_daemon.git
+As a gem, the old-fashioned way:
-Install required gems
+ gem install scheduler_daemon -s http://gemcutter.org
- gem sources -a http://gems.github.com # if you haven't already...
+As a gem with bundler, add to your ./Gemfile:
- sudo gem install daemons rufus-scheduler eventmachine chronic
+ source "http://gemcutter.org"
+ gem 'scheduler_daemon', :only => :bundle
-You'll need the chronic gem if you want to be able to use english time descriptions in your scheduled tasks, like:
+As a plugin: (might be awkward to call the binary to start up the daemon...)
- every '3h', :first_at => Chronic.parse('midnight')
+ script/plugin install git://github.com/ssoroka/scheduler_daemon.git
+ # Install required gems
+ gem install daemons rufus-scheduler eventmachine chronic -s http://gemcutter.org
-generate the scheduler daemon files in your rails app:
+Optionally generate the default scheduler daemon task for your rails app:
script/generate scheduler
+which will create an excellent example task named:
+
+ lib/scheduled_tasks/session_cleaner_task.rb
+
Usage
=====
@@ -45,36 +54,63 @@ generate a new scheduled task:
Tasks support their own special DSL; commands are:
environments :production, :staging # run only in environments listed. (:all by default)
- every '1d' # run once a day
- every '1d', :first_at => Chronic.parse("2 am") # run once a day, starting at 2 am
- at Cronic.parse('5 pm') # run once at 5 pm, today (today would be relative to scheduler start/restart)
- cron '* 4 * * *' # cron style (the example is run at 4 am, I do believe)
+ every '1d' # run every day
+ every '1d', :first_at => Chronic.parse("2 am") # run every day, starting at 2 am (see caveat below)
+ at Cronic.parse('5 pm') # run *once* at 5 pm today
+ # (relative to scheduler start/restart time )
+ # (happens every time scheduler starts/restarts)
+ # (see caveat below )
+ cron '* 4 * * *' # cron style (run every 4 am)
in '30s' # run once, 30 seconds from scheduler start/restart
fire up the daemon in console mode to test it out
- ruby scheduler/bin/scheduler_daemon.rb run
+ scheduler_daemon run
-When you're done, get your system admin (or switch hats) to add the daemon to the system start-up, and
+For production environments, add the daemon to the system start-up, and
capistrano deploy scripts, etc. Something like:
- RAILS_ENV=production ruby scheduler/bin/scheduler_daemon.rb start
+ RAILS_ENV=production scheduler_daemon start
-Run individual tasks like so:
+Selectively run tasks like so:
- ruby daemons/bin/task_runner.rb run -- --only=task_name1,task_name2
+ scheduler_daemon start -- --only=task_name1,task_name2 --except=not_me
Specs
=====
-There are some default specs supplied, you are encouraged to write more specs for your tasks as you create them. Use the existing spec as a template.
-
-See spec/README for more information
+See the spec for session cleaner for an idea on how to write specs for your tasks
To Do
=====
-- dynamically add and remove tasks while daemon is running (? anyone want this?) Perhaps a web interface?
+Looking for suggestions!
+
+Send requests to ssoroka78@gmail.com or on twitter, @ssoroka
+
+Bugs
+====
+
+Submit bugs here http://github.com/ssoroka/scheduler_daemon/issues
+
+I'd especially like to hear about success/failures with Rails versions outside of 2.2.x to 2.3.x
+
+Caveats
+=======
+
+When using the cronic gem to parse dates, be careful of how it interprets your date,
+for example:
+
+ every '24h', :first_at => Chronic.parse('noon')
+
+will be once a day at noon, but the first time the server starts up (or restarts), noon
+is relative to the current time of day. Before lunch, and it's in the future. If the
+daemon starts up after lunch, the date is in the past, *and the task is immediately run*
+because it thinks it missed its last execution time. Depending on what your task is,
+this may or may not be a problem. If you always want the date to resolve in the future
+with terms like "noon", "3 am" and "midnight", prepend "next" to it. ie:
+
+ every '24h', :first_at => Chronic.parse('next noon')
Author
======
@@ -88,4 +124,4 @@ Steven Soroka
Thanks
======
-Special thanks to [Goldstar](http://www.goldstar.com) for sponsoring the plugin and promoting open-sourcesness.
+Special thanks to [Goldstar](http://www.goldstar.com) for promoting open-source in the workplace.
View
@@ -24,6 +24,12 @@ begin
gem.add_dependency('daemons', '>= 1.0.10')
gem.add_dependency('rufus-scheduler', '>= 2.0.1')
gem.add_dependency('chronic', '>= 0.2.0')
+
+ gem.default_executable('bin/scheduler_daemon')
+
+ everything_from_dirs = %w(bin generators lib spec)
+ gem.files = everything_from_dirs.map{|d| Dir["#{d}/**/*"] }.flatten
+ gem.files += Dir['*'] - (everything_from_dirs + ['pkg'])
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
View
@@ -0,0 +1,51 @@
+#!/usr/bin/env ruby
+# This file launches the scheduler as a daemon.
+# USAGE:
+#
+# scheduler_daemon run # start the daemon and stay on top
+# scheduler_daemon start # start the daemon and stay on top
+# scheduler_daemon stop # stop all instances of the application
+# scheduler_daemon restart # stop all instances and restart them afterwards
+#
+# options can be passed to the scheduler like so:
+#
+# scheduler_daemon start -- --except=session_cleaner
+#
+# options can be passed to the daemon:
+#
+# scheduler_daemon start --dir=/my/rails/root/
+#
+# see README for more info
+require 'rubygems'
+require 'daemons'
+require File.expand_path(File.join(File.dirname(__FILE__), %w(.. lib loader find_rails_root)))
+
+def launch_args(options = {})
+ params = options.map{|k,v| "--#{k}=#{v}"}
+ if params.empty?
+ ARGV
+ else
+ args = ARGV.dup
+ args << '--' unless args.include?('--')
+ args + params
+ end
+end
+
+rails_root = FindRailsRoot.locate
+pid_dir = File.expand_path(File.join(rails_root, 'log'))
+scheduler = File.join(File.dirname(__FILE__), %w(.. lib loader scheduler_loader.rb))
+
+raise "#{pid_dir} does not exist" unless File.exist?(pid_dir)
+
+app_options = {
+ :app_name => 'scheduler_daemon',
+ :ARGV => launch_args(:dir => rails_root),
+ :dir_mode => :normal,
+ :dir => pid_dir,
+ :multiple => false,
+ :backtrace => true,
+ :log_output => true,
+ :monitor => true
+}
+
+Daemons.run(scheduler, app_options)
@@ -5,19 +5,10 @@ def banner
def manifest
record do |m|
- m.directory File.join('scheduler', 'bin')
- m.directory File.join('scheduler', 'lib', 'scheduled_tasks')
- m.directory File.join('scheduler', 'lib', 'scheduler')
+ m.directory File.join('lib', 'scheduled_tasks')
- m.template 'bin/scheduler_daemon.rb', 'scheduler/bin/scheduler_daemon.rb'
- m.template 'bin/boot.rb', 'scheduler/bin/boot.rb'
-
- m.template 'lib/scheduler.rb', 'scheduler/lib/scheduler.rb'
- m.template 'lib/scheduler/scheduler_task.rb', 'scheduler/lib/scheduler/scheduler_task.rb'
- m.template 'lib/scheduler/hijack_puts.rb', 'scheduler/lib/scheduler/hijack_puts.rb'
- m.template 'lib/scheduler/exception_handler.rb', 'scheduler/lib/scheduler/exception_handler.rb'
-
- m.template 'lib/scheduled_tasks/session_cleaner_task.rb', 'scheduler/lib/scheduled_tasks/session_cleaner_task.rb'
+ m.template 'lib/scheduled_tasks/session_cleaner_task.rb',
+ 'lib/scheduled_tasks/session_cleaner_task.rb'
m.readme('README')
end
@@ -1,8 +1,11 @@
GREAT!
-Now that you've generated the scheduler daemon, try:
+Now that you've generated the scheduler daemon and a default task,
+you can take a look at the new task:
- script/generate scheduler_task Example
+ lib/scheduled_tasks/session_cleaner_task.rb
-and lets take a look at an example task!
+or make your own:
+
+ script/generate scheduler_task MyTaskName
@@ -1,10 +0,0 @@
-# load the environment, just once. yay!
-rails_root = File.expand_path(File.join(File.dirname(__FILE__), %w(.. ..)))
-daemons_lib_dir = File.expand_path(File.join(File.dirname(__FILE__), %w(.. lib)))
-
-Dir.chdir(rails_root)
-
-require 'config/environment'
-require File.join(daemons_lib_dir, 'scheduler')
-
-Scheduler::Base.new(ARGV)
@@ -1,25 +0,0 @@
-#!/usr/bin/env ruby
-# USAGE:
-#
-# ruby scheduler/bin/scheduler_daemon.rb run -- start the daemon and stay on top
-# ruby scheduler/bin/scheduler_daemon.rb start -- start the daemon and stay on top
-# ruby scheduler/bin/scheduler_daemon.rb stop -- stop the daemon
-# ruby scheduler/bin/scheduler_daemon.rb restart -- stop the daemon and restart it afterwards
-
-require 'rubygems'
-require 'daemons'
-
-boot_scheduler = File.join(File.dirname(__FILE__), 'boot.rb')
-pid_dir = File.expand_path(File.join(File.dirname(__FILE__), %w(.. .. log)))
-
-FileUtils.mkdir_p(pid_dir) unless File.exist?(pid_dir)
-
-app_options = {
- :dir_mode => :normal,
- :dir => pid_dir,
- :multiple => false,
- :backtrace => true,
- :log_output => true
-}
-
-Daemons.run(boot_scheduler, app_options)
@@ -1,20 +1,28 @@
class SessionCleanerTask < Scheduler::SchedulerTask
environments :all
- every '1d', :first_at => Chronic.parse('2 am')
+ every '1d', :first_at => Chronic.parse('next 2 am')
def run
- puts "running the session cleaner"
remove_old_sessions
- puts "old sessions are gone!"
end
def remove_old_sessions
- if ActionController::Base.session_store == ActiveRecord::SessionStore
+ log "running the session cleaner"
+ if ActionController::Base.session_store == session_store_class
ActiveRecord::Base.connection.execute("DELETE FROM #{session_table_name} WHERE updated_at < '#{7.days.ago.to_s(:db)}'")
+ log "old sessions are gone!"
+ else
+ log "sessions are not stored in the database; nothing to do."
end
end
-
+
+ def session_store_class
+ return ActiveRecord::SessionStore if defined?(ActiveRecord::SessionStore)
+ # pre rails 2.3 support...
+ return CGI::Session::ActiveRecordStore if defined?(CGI::Session::ActiveRecordStore)
+ end
+
def session_table_name
ActiveRecord::Base.pluralize_table_names ? :sessions : :session
end
Oops, something went wrong.

0 comments on commit 4d7d94e

Please sign in to comment.