Skip to content

Commit

Permalink
created a new :job_template to wrap all commands with. using 'bash -l…
Browse files Browse the repository at this point in the history
… -c' by default now to load the entire enviroment which should help with RVM issues.
  • Loading branch information
javan committed Oct 20, 2010
1 parent 2effe0b commit e66226e
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 82 deletions.
59 changes: 37 additions & 22 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
== Introduction

Whenever is a Ruby gem that provides a clear syntax for defining cron jobs. It outputs valid cron syntax and can even write your crontab file for you. It is designed to work well with Rails applications and can be deployed with Capistrano. Whenever works fine independently as well.

Ryan Bates created a great Railscast about Whenever: http://railscasts.com/episodes/164-cron-in-ruby

Discussion: http://groups.google.com/group/whenever-gem
Whenever is a Ruby gem that provides a clear syntax for writing and deploying cron jobs.

== Installation

$ sudo gem install whenever
$ gem install whenever

Or with Bundler in your Gemfile.

gem 'whenever', :require => false

== Getting started

$ cd /my/rails/app
Expand Down Expand Up @@ -41,7 +41,7 @@ More examples on the wiki: http://wiki.github.com/javan/whenever/instructions-an

== Define your own job types

Whenever ships with three pre-defined job types: command, runner, and rake. You can define your own with <code>job_type</code>.
Whenever ships with three pre-defined job types: command, runner, and rake. You can define your own with `job_type`.

For example:

Expand All @@ -51,15 +51,29 @@ For example:
awesome "party", :fun_level => "extreme"
end

Would run <code>/usr/local/bin/awesome party extreme</code> every two hours. <code>:task</code> is always replaced with the first argument, and any additional <code>:whatevers</code> are replaced with the options passed in or by variables that have been defined with <code>set</code>.
Would run `/usr/local/bin/awesome party extreme` every two hours. `:task` is always replaced with the first argument, and any additional `:whatevers` are replaced with the options passed in or by variables that have been defined with `set`.

The default job types that ship with Whenever are defined like so:

job_type :command, ":task"
job_type :runner, "cd :path && script/runner -e :environment ':task'"
job_type :rake, "cd :path && RAILS_ENV=:environment /usr/bin/env rake :task"
job_type :command, ":task :output"
job_type :rake, "cd :path && RAILS_ENV=:environment rake :task :output"
job_type :runner, "cd :path && script/runner -e :environment ':task' :output"

If a script/rails file is detected (like in a Rails 3 app), runner will be defined to fit:

job_type :runner, "cd :path && script/rails runner -e :environment ':task' :output"

If a `:path` is not set it will default to the directory in which `whenever` was executed. `:environment` will default to 'production'. `:output` will be replaced with your output redirection settings which you can read more about here: http://github.com/javan/whenever/wiki/Output-redirection-(logging-your-cron-jobs)

All jobs are by default run with `bash -l -c 'command...'`. Among other things, this allows your cron jobs to play nice with RVM by loading the entire environment instead of cron's somewhat limited environment. Read more: http://blog.scoutapp.com/articles/2010/09/07/rvm-and-cron-in-production

You can change this by setting your own job_template.

set :job_template, "bash -l -c ':job'"

Or set the job_template to nil to have your jobs execute normally.

If a <code>:path</code> is not set it will default to the directory in which <code>whenever</code> was executed. <code>:environment</code> will default to 'production'.
set :job_template, nil

== Cron output

Expand All @@ -81,26 +95,27 @@ In your "config/deploy.rb" file do something like:
end
end

This will update your crontab file, leaving any existing entries unharmed. When using the <code>--update-crontab</code> option, Whenever will only update the entries in your crontab file related to the current schedule.rb file. You can replace the <code>#{application}</code> with any identifying string you'd like. You can have any number of apps deploy to the same crontab file peacefully given they each use a different identifier.
This will update your crontab file, leaving any existing entries unharmed. When using the `--update-crontab` option, Whenever will only update the entries in your crontab file related to the current schedule.rb file. You can replace the `#{application}` with any identifying string you'd like. You can have any number of apps deploy to the same crontab file peacefully given they each use a different identifier.

If you wish to simply overwrite your crontab file each time you deploy, use the <code>--write-crontab</code> option. This is ideal if you are only working with one app and every crontab entry is contained in a single schedule.rb file.
If you wish to simply overwrite your crontab file each time you deploy, use the `--write-crontab` option. This is ideal if you are only working with one app and every crontab entry is contained in a single schedule.rb file.

By mixing and matching the <code>--load-file</code> and <code>--user</code> options with your various :roles in Capistrano it is entirely possible to deploy different crontab schedules under different users to all your various servers. Get creative!
By mixing and matching the `--load-file` and `--user` options with your various :roles in Capistrano it is entirely possible to deploy different crontab schedules under different users to all your various servers. Get creative!

If you want to override a variable (like your environment) at the time of deployment you can do so with the <code>--set</code> option: http://wiki.github.com/javan/whenever/setting-variables-on-the-fly

== Credit

Whenever was created for use at Inkling (http://inklingmarkets.com) where I work. Their take on it: http://blog.inklingmarkets.com/2009/02/whenever-easy-way-to-do-cron-jobs-from.html

While building Whenever, I learned a lot by digging through the source code of Capistrano - http://github.com/jamis/capistrano
If you want to override a variable (like your environment) at the time of deployment you can do so with the `--set` option: http://wiki.github.com/javan/whenever/setting-variables-on-the-fly

== Discussion / Feedback / Issues / Bugs

For general discussion and questions, please use the google group: http://groups.google.com/group/whenever-gem

If you've found a genuine bug or issue, please use the Issues section on github: http://github.com/javan/whenever/issues

Ryan Bates created a great Railscast about Whenever: http://railscasts.com/episodes/164-cron-in-ruby
It's a little bit dated now, but remains a good introduction.

== Credit

Whenever was created for use at Inkling (http://inklingmarkets.com) where I work. Their take on it: http://blog.inklingmarkets.com/2009/02/whenever-easy-way-to-do-cron-jobs-from.html

== License

Copyright (c) 2009+ Javan Makhmali
Expand Down
18 changes: 12 additions & 6 deletions lib/whenever/job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@ class Job

def initialize(options = {})
@options = options

@at = options[:at]
@at = options.delete(:at)
@template = options.delete(:template)
@job_template = options.delete(:job_template) || ":job"
@options[:output] = Whenever::Output::Redirection.new(options[:output]).to_s if options.has_key?(:output)
@options[:environment] ||= :production
@options[:path] ||= Whenever.path
end

def output
@options[:template].dup.gsub(/:\w+/) do |key|
job = process_template(@template, @options).strip
process_template(@job_template, { :job => job }).strip
end

protected

def process_template(template, options)
template.gsub(/:\w+/) do |key|
before_and_after = [$`[-1..-1], $'[0..0]]
option = @options[key.sub(':', '').to_sym]
option = options[key.sub(':', '').to_sym]

if before_and_after.all? { |c| c == "'" }
escape_single_quotes(option)
Expand All @@ -26,8 +34,6 @@ def output
end
end
end

protected

def escape_single_quotes(str)
str.gsub(/'/) { "'\\''" }
Expand Down
28 changes: 9 additions & 19 deletions lib/whenever/job_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Whenever
class JobList

def initialize(options)
@jobs, @env, @set_variables = {}, {}, {}
@jobs, @env, @set_variables, @pre_set_variables = {}, {}, {}, {}

case options
when String
Expand All @@ -25,7 +25,8 @@ def initialize(options)
end

def set(variable, value)
return if instance_variable_defined?("@#{variable}".to_sym)
variable = variable.to_sym
return if @pre_set_variables[variable]

instance_variable_set("@#{variable}".to_sym, value)
self.class.send(:attr_reader, variable.to_sym)
Expand Down Expand Up @@ -59,9 +60,7 @@ def job_type(name, template)
end
end

def generate_cron_output
set_path_environment_variable

def generate_cron_output
[environment_variables, cron_jobs].compact.join
end

Expand All @@ -79,22 +78,13 @@ def pre_set(variable_string = nil)
pairs.each do |pair|
next unless pair.index('=')
variable, value = *pair.split('=')
set(variable.strip.to_sym, value.strip) unless variable.blank? || value.blank?
unless variable.blank? || value.blank?
variable = variable.strip.to_sym
set(variable, value.strip)
@pre_set_variables[variable] = value
end
end
end

def set_path_environment_variable
return if path_should_not_be_set_automatically?
@env[:PATH] = read_path unless read_path.blank?
end

def read_path
ENV['PATH'] if ENV
end

def path_should_not_be_set_automatically?
@set_path_automatically === false || @env[:PATH] || @env["PATH"]
end

def environment_variables
return if @env.empty?
Expand Down
3 changes: 3 additions & 0 deletions lib/whenever/job_types/default.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# http://blog.scoutapp.com/articles/2010/09/07/rvm-and-cron-in-production
set :job_template, "/bin/bash -l -c ':job'"

job_type :command, ":task :output"
job_type :rake, "cd :path && RAILS_ENV=:environment rake :task --silent :output"

Expand Down
4 changes: 4 additions & 0 deletions test/functional/command_line_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ class CommandLineTest < Test::Unit::TestCase
setup do
@output = Whenever.cron :set => 'environment=serious', :string => \
<<-file
set :job_template, nil
set :environment, :silly
set :path, '/my/path'
every 2.hours do
Expand All @@ -234,6 +235,7 @@ class CommandLineTest < Test::Unit::TestCase
setup do
@output = Whenever.cron :set => 'environment=serious&path=/serious/path', :string => \
<<-file
set :job_template, nil
set :environment, :silly
set :path, '/silly/path'
every 2.hours do
Expand All @@ -251,6 +253,7 @@ class CommandLineTest < Test::Unit::TestCase
setup do
@output = Whenever.cron :set => ' environment = serious& path =/serious/path', :string => \
<<-file
set :job_template, nil
set :environment, :silly
set :path, '/silly/path'
every 2.hours do
Expand All @@ -268,6 +271,7 @@ class CommandLineTest < Test::Unit::TestCase
setup do
@output = Whenever.cron :set => ' environment=', :string => \
<<-file
set :job_template, nil
set :environment, :silly
set :path, '/silly/path'
every 2.hours do
Expand Down
14 changes: 14 additions & 0 deletions test/functional/output_at_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every "weekday", :at => '5:02am' do
command "blahblah"
end
Expand All @@ -21,6 +22,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every "weekday", :at => %w(5:02am 3:52pm) do
command "blahblah"
end
Expand All @@ -37,6 +39,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every "weekday", :at => '5:02am, 3:52pm' do
command "blahblah"
end
Expand All @@ -53,6 +56,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every "weekday", :at => '5:02am, 3:02pm' do
command "blahblah"
end
Expand All @@ -68,6 +72,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every "mon,wed,fri", :at => '5:02am, 3:02pm' do
command "blahblah"
end
Expand All @@ -83,6 +88,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
set :path, '/your/path'
every "mon,wed,fri", :at => '5:02am, 3:02pm' do
runner "blahblah"
Expand All @@ -99,6 +105,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
set :path, '/your/path'
every "mon,wed,fri", :at => '5:02am, 3:02pm' do
rake "blah:blah"
Expand All @@ -115,6 +122,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every [1.month, 1.day], :at => 'january 5:02am, june 17th at 2:22pm, june 3rd at 3:33am' do
command "blahblah"
end
Expand All @@ -138,6 +146,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every :reboot do
command "command_1"
command "command_2"
Expand All @@ -155,6 +164,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
set :path, '/your/path'
every :day do
rake "blah:blah"
Expand All @@ -179,6 +189,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every 5.minutes, :at => 1 do
command "blahblah"
end
Expand All @@ -194,6 +205,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every 4.minutes, :at => 2 do
command "blahblah"
end
Expand All @@ -209,6 +221,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every 3.minutes, :at => 7 do
command "blahblah"
end
Expand All @@ -224,6 +237,7 @@ class OutputAtTest < Test::Unit::TestCase
setup do
@output = Whenever.cron \
<<-file
set :job_template, nil
every 2.minutes, :at => 27 do
command "blahblah"
end
Expand Down
Loading

0 comments on commit e66226e

Please sign in to comment.