javan-whenever 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,79 @@
1
+ = Whenever
2
+
3
+ Whenever is a ruby gem that provides a ruby syntax for defining cron jobs. It was designed to work well with Rails applications, but can be used independently as well.
4
+
5
+ == Installation
6
+
7
+ To install Whenever in a Rails (2.1 or greater) application:
8
+
9
+ in your "config/environment.rb" file:
10
+
11
+ Rails::Initializer.run do |config|
12
+ config.gem 'javan-whenever', :lib => 'whenever', :source => 'http://gems.github.com'
13
+ end
14
+
15
+ To install this gem (and all other missing gem dependencies), run rake gems:install (use sudo if necessary).
16
+
17
+ In older versions of Rails:
18
+
19
+ $ gem sources -a http://gems.github.com
20
+ $ gem install whenever
21
+
22
+ in your "config/environment.rb" file:
23
+
24
+ Rails::Initializer.run do |config|
25
+ ...
26
+ end
27
+
28
+ require "whenever"
29
+
30
+ == Getting started
31
+
32
+ $ cd /my/rails/app
33
+ $ wheneverize .
34
+
35
+ This will create an initial "config/schedule.rb" file you.
36
+
37
+ == Example schedule.rb file
38
+
39
+ set :runner_path, '/var/www/apps/my_app' # Whenever will try to use your RAILS_ROOT if this isn't set
40
+ set :runner_environment, :production # Whenever defaults to production so only set this if you want to use a different environment.
41
+ set :cron_log, '/path/to/my/cronlog.log' # Where to log (this should NOT be your Rails log)
42
+
43
+
44
+ every 2.hours do
45
+ runner "MyModel.some_process" # runners are the script/runners you know and love
46
+ command "/usr/local/bin/my_great_command" # commands are any unix command
47
+ end
48
+
49
+ every 1.day, :at => '4:30 am' do # If not :at option is set these jobs will run at midnight
50
+ runner "DB.Backup", :cron_log => false # You can specify false for no logging or a string a different log file to override logging.
51
+ end
52
+
53
+ every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
54
+ runner "SomeModel.ladeda"
55
+ end
56
+
57
+ every :sunday do # Use any day of the week or :weekend, :weekday
58
+ runner "Task.do_something_great"
59
+ end
60
+
61
+ == Cron output
62
+
63
+ $ cd /my/rails/app
64
+ $ whenever
65
+
66
+ And you'll see your schedule.rb converted to cron sytax
67
+
68
+ == Capistrano integration
69
+
70
+ Use the "whenever:write_cron" task to automatically write your crontab file with each deploy.
71
+
72
+ in your "config/deploy.rb" file do something like:
73
+
74
+ after "deploy:symlink", "whenever:write_cron"
75
+
76
+ THIS WILL COMPLETELY OVERWRITE ANY EXISTING CRONTAB ENTRIES!
77
+ ------------------------------------------------------------
78
+
79
+ Better documentation on the way!
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+ require 'lib/base'
5
+
6
+ Echoe.new('whenever', Whenever::VERSION) do |p|
7
+ p.description = "Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever."
8
+ p.url = "http://github.com/javan/whenever"
9
+ p.author = "Javan Makhmali"
10
+ p.email = "javan@javan.us"
11
+ p.dependencies = ["chronic", "activesupport"]
12
+ p.ignore_pattern = ["tmp/*", "script/*"]
13
+ p.development_dependencies = []
14
+ end
data/bin/whenever ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'optparse'
5
+ require 'whenever'
6
+
7
+ task = "whenever:output_cron"
8
+
9
+ OptionParser.new do |opts|
10
+ opts.banner = "Usage: whenever [options]"
11
+ opts.on('-c', '--write-crontab') { task = "whenever:write_cron" }
12
+ opts.on('-v', '--version') { puts "Whenever v#{Whenever::VERSION}"; exit }
13
+ end.parse!
14
+
15
+ Rake::Task[task].invoke
data/bin/wheneverize ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is based heavily on Capistrano's `capify` command
4
+
5
+ require 'optparse'
6
+
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: #{File.basename($0)} [path]"
9
+
10
+ begin
11
+ opts.parse!(ARGV)
12
+ rescue OptionParser::ParseError => e
13
+ warn e.message
14
+ puts opts
15
+ exit 1
16
+ end
17
+ end
18
+
19
+ if ARGV.empty?
20
+ abort "Please specify the directory to wheneverize, e.g. `#{File.basename($0)} .'"
21
+ elsif !File.exists?(ARGV.first)
22
+ abort "`#{ARGV.first}' does not exist."
23
+ elsif !File.directory?(ARGV.first)
24
+ abort "`#{ARGV.first}' is not a directory."
25
+ elsif ARGV.length > 1
26
+ abort "Too many arguments; please specify only the directory to wheneverize."
27
+ end
28
+
29
+
30
+ content = <<-FILE
31
+ # Use this file to easily define all of your cron jobs.
32
+ #
33
+ # It's helpful, but not entirely necessary to understand cron before proceeding.
34
+ # http://en.wikipedia.org/wiki/Cron
35
+
36
+ # Example:
37
+ #
38
+ # set :cron_log, "/path/to/my/cron_log.log"
39
+ #
40
+ # every 2.hours do
41
+ # command "/usr/bin/some_great_command"
42
+ # runner "MyModel.some_method"
43
+ # end
44
+ #
45
+ # every 4.days do
46
+ # runner "AnotherModel.prune_old_records"
47
+ # end
48
+
49
+ # Learn more: http://github.com/javan/whenever
50
+ FILE
51
+
52
+ file = 'config/schedule.rb'
53
+ base = ARGV.shift
54
+
55
+ file = File.join(base, file)
56
+ if File.exists?(file)
57
+ warn "[skip] `#{file}' already exists"
58
+ elsif File.exists?(file.downcase)
59
+ warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
60
+ elsif !File.exists?(File.dirname(file))
61
+ warn "[skip] directory `#{File.dirname(file)}' does not exist"
62
+ else
63
+ puts "[add] writing `#{file}'"
64
+ File.open(file, "w") { |f| f.write(content) }
65
+ end
66
+
67
+ puts "[done] wheneverized!"
data/lib/base.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'job_list'
2
+ require 'job_types/default'
3
+ require 'job_types/runner'
4
+ require 'outputs/cron'
5
+
6
+ module Whenever
7
+ VERSION = '0.1.0'
8
+
9
+ def self.cron(options)
10
+ Whenever::JobList.new(options).generate_cron_output
11
+ end
12
+ end
data/lib/job_list.rb ADDED
@@ -0,0 +1,84 @@
1
+ module Whenever
2
+ class JobList
3
+
4
+ def initialize(options)
5
+ @jobs = Hash.new
6
+ @env = Hash.new
7
+
8
+ config = case options
9
+ when String then options
10
+ when Hash
11
+ if options[:string]
12
+ options[:string]
13
+ elsif options[:file]
14
+ File.read(options[:file])
15
+ end
16
+ end
17
+
18
+ eval(config)
19
+ end
20
+
21
+ def set(variable, value)
22
+ instance_variable_set("@#{variable}".to_sym, value)
23
+ self.class.send(:attr_reader, variable.to_sym)
24
+ end
25
+
26
+ def env(variable, value)
27
+ @env[variable.to_s] = value
28
+ end
29
+
30
+ def every(frequency, options = {})
31
+ @current_time_scope = frequency
32
+ @options = options
33
+ yield
34
+ end
35
+
36
+ def command(task, options = {})
37
+ options[:cron_log] ||= @cron_log unless options[:cron_log] === false
38
+ options[:class] ||= Whenever::Job::Default
39
+ @jobs[@current_time_scope] ||= []
40
+ @jobs[@current_time_scope] << options[:class].new(@options.merge(:task => task).merge(options))
41
+ end
42
+
43
+ def runner(task, options = {})
44
+ options.reverse_merge!(:environment => @runner_environment, :path => @runner_path)
45
+ options[:class] = Whenever::Job::Runner
46
+ command(task, options)
47
+ end
48
+
49
+ def generate_cron_output
50
+ [environment_variables, cron_jobs].compact.join
51
+ end
52
+
53
+ private
54
+
55
+ def environment_variables
56
+ return if @env.empty?
57
+
58
+ output = []
59
+ @env.each do |key, val|
60
+ output << "#{key}=#{val}\n"
61
+ end
62
+ output << "\n"
63
+
64
+ output.join
65
+ end
66
+
67
+ def cron_jobs
68
+ return if @jobs.empty?
69
+
70
+ output = []
71
+ @jobs.each do |time, jobs|
72
+ jobs.each do |job|
73
+ cron = Whenever::Output::Cron.output(time, job)
74
+ cron << " >> #{job.cron_log} 2>&1" if job.cron_log
75
+ cron << "\n\n"
76
+ output << cron
77
+ end
78
+ end
79
+
80
+ output.join
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,17 @@
1
+ module Whenever
2
+ module Job
3
+ class Default
4
+ attr_accessor :task, :at, :cron_log
5
+
6
+ def initialize(options = {})
7
+ @task = options[:task]
8
+ @at = options[:at]
9
+ @cron_log = options[:cron_log]
10
+ end
11
+
12
+ def output
13
+ task
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module Whenever
2
+ module Job
3
+ class Runner < Whenever::Job::Default
4
+
5
+ def initialize(options = {})
6
+ super(options)
7
+
8
+ @environment = options[:environment] || :production
9
+
10
+ if [Whenever::Job::Runner.rails_root, options[:path]].all?(&:blank?)
11
+ raise ArgumentError, "no cron_path available for runner to use"
12
+ else
13
+ @path = options[:path] || Whenever::Job::Runner.rails_root
14
+ end
15
+ end
16
+
17
+ def self.rails_root
18
+ if defined?(RAILS_ROOT)
19
+ RAILS_ROOT
20
+ elsif defined?(::RAILS_ROOT)
21
+ ::RAILS_ROOT
22
+ end
23
+ end
24
+
25
+ def output
26
+ %Q(#{File.join(@path, 'script', 'runner')} -e #{@environment} "#{task}")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,104 @@
1
+ module Whenever
2
+ module Output
3
+
4
+ class Cron
5
+
6
+ attr_accessor :time, :task
7
+
8
+ def initialize(time = nil, task = nil, at = nil)
9
+ @time = time
10
+ @task = task
11
+ @at = at.is_a?(String) ? (Chronic.parse(at) || 0) : (at || 0)
12
+ end
13
+
14
+ def self.output(time, job)
15
+ out = new(time, job.output, job.at)
16
+ "#{out.time_in_cron_syntax} #{out.task}"
17
+ end
18
+
19
+ def time_in_cron_syntax
20
+ case @time
21
+ when Symbol then parse_symbol
22
+ when String then parse_as_string
23
+ else parse_time
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def parse_symbol
30
+ case @time
31
+ when :reboot then '@reboot'
32
+ when :year, :yearly then '@annually'
33
+ when :day, :daily then '@daily'
34
+ when :midnight then '@midnight'
35
+ when :month, :monthly then '@monthly'
36
+ when :week, :weekly then '@weekly'
37
+ when :hour, :hourly then '@hourly'
38
+ else parse_as_string
39
+ end
40
+ end
41
+
42
+ def parse_time
43
+ timing = Array.new(5, '*')
44
+ case @time
45
+ when 0.seconds...1.minute
46
+ raise ArgumentError, "Time must be in minutes or higher"
47
+ when 1.minute...1.hour
48
+ minute_frequency = @time / 60
49
+ timing[0] = comma_separated_timing(minute_frequency, 59)
50
+ when 1.hour...1.day
51
+ hour_frequency = (@time / 60 / 60).round
52
+ timing[0] = @at.is_a?(Time) ? @at.min : @at
53
+ timing[1] = comma_separated_timing(hour_frequency, 23)
54
+ when 1.day...1.month
55
+ day_frequency = (@time / 24 / 60 / 60).round
56
+ timing[0] = @at.is_a?(Time) ? @at.min : 0
57
+ timing[1] = @at.is_a?(Time) ? @at.hour : @at
58
+ timing[2] = comma_separated_timing(day_frequency, 31, 1)
59
+ when 1.month..12.months
60
+ month_frequency = (@time / 30 / 24 / 60 / 60).round
61
+ timing[0] = @at.is_a?(Time) ? @at.min : 0
62
+ timing[1] = @at.is_a?(Time) ? @at.hour : 0
63
+ timing[2] = @at.is_a?(Time) ? @at.day : (@at.zero? ? 1 : @at)
64
+ timing[3] = comma_separated_timing(month_frequency, 12, 1)
65
+ else
66
+ return parse_as_string
67
+ end
68
+ timing.join(' ')
69
+ end
70
+
71
+ def parse_as_string
72
+ return unless @time
73
+ string = @time.to_s
74
+
75
+ return "0 0 * * mon-fri" if string.downcase.index('weekday')
76
+ return "0 0 * * sat,sun" if string.downcase.index('weekend')
77
+
78
+ %w(sun mon tue wed thu fri sat).each do |day|
79
+ return "0 0 * * #{day}" if string.downcase.index(day)
80
+ end
81
+
82
+ raise ArgumentError, "Couldn't parse: #{@time}"
83
+ end
84
+
85
+ def comma_separated_timing(frequency, max, start = 0)
86
+ return start if frequency.blank? || frequency.zero?
87
+ return '*' if frequency == 1
88
+ return frequency if frequency > (max * 0.5).ceil
89
+
90
+ original_start = start
91
+
92
+ start += frequency unless (max + 1).modulo(frequency).zero? || start > 0
93
+ output = (start..max).step(frequency).to_a
94
+
95
+ max_occurances = (max.to_f / (frequency.to_f)).round
96
+ max_occurances += 1 if original_start.zero?
97
+
98
+ output[0, max_occurances].join(',')
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,21 @@
1
+ namespace :whenever do
2
+
3
+ desc "outputs cron"
4
+ task :output_cron do
5
+ puts Whenever.cron(:file => "config/schedule.rb")
6
+ end
7
+
8
+ desc "writes cron"
9
+ task :write_cron do
10
+ require 'tempfile'
11
+ cron_output = Whenever.cron(:file => "config/schedule.rb")
12
+
13
+ tmp_cron_file = Tempfile.new('whenever_tmp_cron').path
14
+ File.open(tmp_cron_file, File::WRONLY | File::APPEND) do |file|
15
+ file << cron_output
16
+ end
17
+ sh "crontab #{tmp_cron_file}"
18
+ puts "[write] crontab file updated"
19
+ end
20
+
21
+ end
data/lib/whenever.rb ADDED
@@ -0,0 +1,24 @@
1
+ unless defined?(Whenever)
2
+ $:.unshift(File.dirname(__FILE__))
3
+
4
+ # Hoping to load Rails' Rakefile
5
+ begin
6
+ load 'Rakefile'
7
+ rescue LoadError => e
8
+ nil
9
+ end
10
+
11
+ # Load Whenever's rake tasks
12
+ begin
13
+ Dir[File.join(File.dirname(__FILE__), 'tasks', '*.rake')].each { |rake| load rake }
14
+ rescue LoadError => e
15
+ nil
16
+ end
17
+
18
+ end
19
+
20
+
21
+ require 'activesupport'
22
+ require 'chronic'
23
+
24
+ require 'base'
data/test/cron_test.rb ADDED
@@ -0,0 +1,187 @@
1
+ require 'test_helper'
2
+
3
+ class CronTest < Test::Unit::TestCase
4
+
5
+ context "When parsing time in minutes" do
6
+ should "raise if less than 1 minute" do
7
+ assert_raises ArgumentError do
8
+ parse_time(59.seconds)
9
+ end
10
+
11
+ assert_raises ArgumentError do
12
+ parse_time(0.minutes)
13
+ end
14
+ end
15
+
16
+ # For santity, do some tests on straight String
17
+ should "parse correctly" do
18
+ assert_equal '* * * * *', parse_time(1.minute)
19
+ assert_equal '0,5,10,15,20,25,30,35,40,45,50,55 * * * *', parse_time(5.minutes)
20
+ assert_equal '7,14,21,28,35,42,49,56 * * * *', parse_time(7.minutes)
21
+ assert_equal '0,30 * * * *', parse_time(30.minutes)
22
+ assert_equal '32 * * * *', parse_time(32.minutes)
23
+ assert_not_equal '60 * * * *', parse_time(60.minutes) # 60 minutes bumps up into the hour range
24
+ end
25
+
26
+ # Test all minutes
27
+ (2..59).each do |num|
28
+ should "parse correctly for #{num} minutes" do
29
+ start = 0
30
+ start += num unless 60.modulo(num).zero?
31
+ minutes = (start..59).step(num).to_a
32
+
33
+ assert_equal "#{minutes.join(',')} * * * *", parse_time(num.minutes)
34
+ end
35
+ end
36
+ end
37
+
38
+ context "When parsing time in hours" do
39
+ should "parse correctly" do
40
+ assert_equal '0 * * * *', parse_time(1.hour)
41
+ assert_equal '0 0,2,4,6,8,10,12,14,16,18,20,22 * * *', parse_time(2.hours)
42
+ assert_equal '0 0,3,6,9,12,15,18,21 * * *', parse_time(3.hours)
43
+ assert_equal '0 5,10,15,20 * * *', parse_time(5.hours)
44
+ assert_equal '0 17 * * *', parse_time(17.hours)
45
+ assert_not_equal '0 24 * * *', parse_time(24.hours) # 24 hours bumps up into the day range
46
+ end
47
+
48
+ (2..23).each do |num|
49
+ should "parse correctly for #{num} hours" do
50
+ start = 0
51
+ start += num unless 24.modulo(num).zero?
52
+ hours = (start..23).step(num).to_a
53
+
54
+ assert_equal "0 #{hours.join(',')} * * *", parse_time(num.hours)
55
+ end
56
+ end
57
+
58
+ should "parse correctly when given an 'at' with minutes as an Integer" do
59
+ assert_minutes_equals "1", 1
60
+ assert_minutes_equals "14", 14
61
+ assert_minutes_equals "27", 27
62
+ assert_minutes_equals "55", 55
63
+ end
64
+
65
+ should "parse correctly when given an 'at' with minutes as a Time" do
66
+ # Basically just testing that Chronic parses some times and we get the minutes out of it
67
+ assert_minutes_equals "1", '3:01am'
68
+ assert_minutes_equals "1", 'January 21 2:01 PM'
69
+ assert_minutes_equals "0", 'midnight'
70
+ assert_minutes_equals "59", '13:59'
71
+ end
72
+ end
73
+
74
+ context "When parsing time in days (of month)" do
75
+ should "parse correctly" do
76
+ assert_equal '0 0 * * *', parse_time(1.days)
77
+ assert_equal '0 0 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31 * *', parse_time(2.days)
78
+ assert_equal '0 0 1,5,9,13,17,21,25,29 * *', parse_time(4.days)
79
+ assert_equal '0 0 1,8,15,22 * *', parse_time(7.days)
80
+ assert_equal '0 0 1,17 * *', parse_time(16.days)
81
+ assert_equal '0 0 17 * *', parse_time(17.days)
82
+ assert_equal '0 0 29 * *', parse_time(29.days)
83
+ assert_not_equal '0 0 30 * *', parse_time(30.days) # 30 days bumps into the month range
84
+ end
85
+
86
+ should "parse correctly when given an 'at' with hours, minutes as a Time" do
87
+ # first param is an array with [hours, minutes]
88
+ assert_hours_and_minutes_equals %w(3 45), '3:45am'
89
+ assert_hours_and_minutes_equals %w(20 1), '8:01pm'
90
+ assert_hours_and_minutes_equals %w(0 0), 'midnight'
91
+ assert_hours_and_minutes_equals %w(1 23), '1:23 AM'
92
+ assert_hours_and_minutes_equals %w(23 59), 'March 21 11:59 pM'
93
+ end
94
+
95
+ should "parse correctly when given an 'at' with hours as an Integer" do
96
+ # first param is an array with [hours, minutes]
97
+ assert_hours_and_minutes_equals %w(1 0), 1
98
+ assert_hours_and_minutes_equals %w(3 0), 3
99
+ assert_hours_and_minutes_equals %w(15 0), 15
100
+ assert_hours_and_minutes_equals %w(19 0), 19
101
+ assert_hours_and_minutes_equals %w(23 0), 23
102
+ end
103
+ end
104
+
105
+ context "When parsing time in months" do
106
+ should "parse correctly" do
107
+ assert_equal '0 0 1 * *', parse_time(1.month)
108
+ assert_equal '0 0 1 1,3,5,7,9,11 *', parse_time(2.months)
109
+ assert_equal '0 0 1 1,4,7,10 *', parse_time(3.months)
110
+ assert_equal '0 0 1 1,5,9 *', parse_time(4.months)
111
+ assert_equal '0 0 1 1,6 *', parse_time(5.months)
112
+ assert_equal '0 0 1 7 *', parse_time(7.months)
113
+ assert_equal '0 0 1 8 *', parse_time(8.months)
114
+ assert_equal '0 0 1 9 *', parse_time(9.months)
115
+ assert_equal '0 0 1 10 *', parse_time(10.months)
116
+ assert_equal '0 0 1 11 *', parse_time(11.months)
117
+ assert_equal '0 0 1 12 *', parse_time(12.months)
118
+ end
119
+
120
+ should "parse correctly when given an 'at' with days, hours, minutes as a Time" do
121
+ # first param is an array with [days, hours, minutes]
122
+ assert_days_and_hours_and_minutes_equals %w(1 3 45), 'January 1st 3:45am'
123
+ assert_days_and_hours_and_minutes_equals %w(11 23 0), 'Feb 11 11PM'
124
+ assert_days_and_hours_and_minutes_equals %w(22 1 1), 'march 22nd at 1:01 am'
125
+ assert_days_and_hours_and_minutes_equals %w(23 0 0), 'march 22nd at midnight' # looks like midnight means the next day
126
+ end
127
+
128
+ should "parse correctly when given an 'at' with days as an Integer" do
129
+ # first param is an array with [days, hours, minutes]
130
+ assert_days_and_hours_and_minutes_equals %w(1 0 0), 1
131
+ assert_days_and_hours_and_minutes_equals %w(15 0 0), 15
132
+ assert_days_and_hours_and_minutes_equals %w(29 0 0), 29
133
+ end
134
+ end
135
+
136
+ context "When parsing time in days (of week)" do
137
+ should "parse days of the week correctly" do
138
+ {
139
+ 'sun' => %w(sun Sunday SUNDAY SUN),
140
+ 'mon' => %w(mon Monday MONDAY MON),
141
+ 'tue' => %w(tue tues Tuesday TUESDAY TUE),
142
+ 'wed' => %w(wed Wednesday WEDNESDAY WED),
143
+ 'thu' => %w(thu thurs thur Thursday THURSDAY THU),
144
+ 'fri' => %w(fri Friday FRIDAY FRI),
145
+ 'sat' => %w(sat Saturday SATURDAY SAT)
146
+ }.each do |day, day_tests|
147
+ day_tests.each do |day_test|
148
+ assert_equal "0 0 * * #{day}", parse_time(day_test)
149
+ end
150
+ end
151
+ end
152
+
153
+ should "parse weekday correctly" do
154
+ assert_equal '0 0 * * mon-fri', parse_time('weekday')
155
+ assert_equal '0 0 * * mon-fri', parse_time('Weekdays')
156
+ end
157
+
158
+ should "parse weekend correctly" do
159
+ assert_equal '0 0 * * sat,sun', parse_time('weekend')
160
+ assert_equal '0 0 * * sat,sun', parse_time('Weekends')
161
+ end
162
+ end
163
+
164
+ private
165
+
166
+ def assert_days_and_hours_and_minutes_equals(expected, time)
167
+ cron = parse_time(2.months, 'some task', time)
168
+ minutes, hours, days, *garbage = cron.split(' ')
169
+ assert_equal expected, [days, hours, minutes]
170
+ end
171
+
172
+ def assert_hours_and_minutes_equals(expected, time)
173
+ cron = parse_time(2.days, 'some task', time)
174
+ minutes, hours, *garbage = cron.split(' ')
175
+ assert_equal expected, [hours, minutes]
176
+ end
177
+
178
+ def assert_minutes_equals(expected, time)
179
+ cron = parse_time(2.hours, 'some task', time)
180
+ assert_equal expected, cron.split(' ')[0]
181
+ end
182
+
183
+ def parse_time(time = nil, task = nil, at = nil)
184
+ Whenever::Output::Cron.new(time, task, at).time_in_cron_syntax
185
+ end
186
+
187
+ end
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+
3
+ class OutputCommandTest < Test::Unit::TestCase
4
+
5
+ context "A plain command" do
6
+ setup do
7
+ @output = load_whenever_output \
8
+ <<-file
9
+ every 2.hours do
10
+ command "blahblah"
11
+ end
12
+ file
13
+ end
14
+
15
+ should "output the command" do
16
+ assert_match /^.+ .+ .+ .+ blahblah$/, @output
17
+ end
18
+ end
19
+
20
+ context "A command when the cron_log is set" do
21
+ setup do
22
+ @output = load_whenever_output \
23
+ <<-file
24
+ set :cron_log, 'logfile.log'
25
+ every 2.hours do
26
+ command "blahblah"
27
+ end
28
+ file
29
+ end
30
+
31
+ should "output the command with the log syntax appended" do
32
+ assert_match /^.+ .+ .+ .+ blahblah >> logfile.log 2>&1$/, @output
33
+ end
34
+ end
35
+
36
+ context "A command when the cron_log is set and the comand overrides it" do
37
+ setup do
38
+ @output = load_whenever_output \
39
+ <<-file
40
+ set :cron_log, 'logfile.log'
41
+ every 2.hours do
42
+ command "blahblah", :cron_log => 'otherlog.log'
43
+ end
44
+ file
45
+ end
46
+
47
+ should "output the command with the log syntax appended" do
48
+ assert_no_match /.+ .+ .+ .+ blahblah >> logfile.log 2>&1/, @output
49
+ assert_match /^.+ .+ .+ .+ blahblah >> otherlog.log 2>&1$/, @output
50
+ end
51
+ end
52
+
53
+ context "A command when the cron_log is set and the comand rejects it" do
54
+ setup do
55
+ @output = load_whenever_output \
56
+ <<-file
57
+ set :cron_log, 'logfile.log'
58
+ every 2.hours do
59
+ command "blahblah", :cron_log => false
60
+ end
61
+ file
62
+ end
63
+
64
+ should "output the command without the log syntax appended" do
65
+ assert_no_match /.+ .+ .+ .+ blahblah >> logfile.log 2>&1/, @output
66
+ assert_match /^.+ .+ .+ .+ blahblah$/, @output
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class OutputEnvTest < Test::Unit::TestCase
4
+
5
+ context "The output from Whenever with environment variables set" do
6
+ setup do
7
+ @output = load_whenever_output \
8
+ <<-file
9
+ env :MYVAR, 'blah'
10
+ env 'MAILTO', "someone@example.com"
11
+ file
12
+ end
13
+
14
+ should "output MYVAR environment variable" do
15
+ assert_match "MYVAR=blah", @output
16
+ end
17
+
18
+ should "output MAILTO environment variable" do
19
+ assert_match "MAILTO=someone@example.com", @output
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,75 @@
1
+ require 'test_helper'
2
+
3
+ class OutputRunnerTest < Test::Unit::TestCase
4
+
5
+ context "A runner with runner_path set" do
6
+ setup do
7
+ @output = load_whenever_output \
8
+ <<-file
9
+ set :runner_path, '/my/path'
10
+ every 2.hours do
11
+ runner "blahblah"
12
+ end
13
+ file
14
+ end
15
+
16
+ should "output the runner using that path" do
17
+ assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
18
+ end
19
+ end
20
+
21
+ context "A runner with no runner_path set and RAILS_ROOT defined" do
22
+ setup do
23
+ Whenever::Job::Runner.stubs(:rails_root).returns('/my/path')
24
+
25
+ @output = load_whenever_output \
26
+ <<-file
27
+ every 2.hours do
28
+ runner "blahblah"
29
+ end
30
+ file
31
+ end
32
+
33
+ should "output the runner using that path" do
34
+ assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
35
+ end
36
+ end
37
+
38
+ context "A runner with runner_path set AND RAILS_ROOT defined" do
39
+ setup do
40
+ Whenever::Job::Runner.stubs(:rails_root).returns('/my/path')
41
+
42
+ @output = load_whenever_output \
43
+ <<-file
44
+ set :runner_path, '/my/path'
45
+ every 2.hours do
46
+ runner "blahblah"
47
+ end
48
+ file
49
+ end
50
+
51
+ should "use the runner_path" do
52
+ assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
53
+ assert_no_match /\/rails\/path/, @output
54
+ end
55
+ end
56
+
57
+ context "A runner with no runner_path set and no RAILS_ROOT defined" do
58
+ setup do
59
+ Whenever::Job::Runner.stubs(:rails_root).returns(nil)
60
+
61
+ @input = <<-file
62
+ every 2.hours do
63
+ runner "blahblah"
64
+ end
65
+ file
66
+ end
67
+
68
+ should "raise an exception" do
69
+ assert_raises ArgumentError do
70
+ load_whenever_output(@input)
71
+ end
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ begin
5
+ require 'shoulda'
6
+ rescue LoadError
7
+ warn 'To test Whenever you need the shoulda gem:'
8
+ warn '$ sudo gem install thoughtbot-shoulda'
9
+ exit(1)
10
+ end
11
+
12
+ begin
13
+ require 'mocha'
14
+ rescue LoadError
15
+ warn 'To test Whenever you need the mocha gem:'
16
+ warn '$ sudo gem install mocha'
17
+ exit(1)
18
+ end
19
+
20
+ require 'whenever'
21
+
22
+ module TestExtensions
23
+
24
+ def load_whenever_output(input)
25
+ Whenever.cron(input)
26
+ end
27
+
28
+ def two_hours
29
+ "0 0,2,4,6,8,10,12,14,16,18,20,22 * * *"
30
+ end
31
+ end
32
+
33
+ class Test::Unit::TestCase
34
+ include TestExtensions
35
+ end
data/whenever.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{whenever}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Javan Makhmali"]
9
+ s.date = %q{2009-02-15}
10
+ s.description = %q{Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever.}
11
+ s.email = %q{javan@javan.us}
12
+ s.executables = ["whenever", "wheneverize"]
13
+ s.extra_rdoc_files = ["bin/whenever", "bin/wheneverize", "lib/base.rb", "lib/job_list.rb", "lib/job_types/default.rb", "lib/job_types/runner.rb", "lib/outputs/cron.rb", "lib/tasks/whenever.rake", "lib/whenever.rb", "README.rdoc"]
14
+ s.files = ["bin/whenever", "bin/wheneverize", "lib/base.rb", "lib/job_list.rb", "lib/job_types/default.rb", "lib/job_types/runner.rb", "lib/outputs/cron.rb", "lib/tasks/whenever.rake", "lib/whenever.rb", "Manifest", "Rakefile", "README.rdoc", "test/cron_test.rb", "test/output_command_test.rb", "test/output_env_test.rb", "test/output_runner_test.rb", "test/test_helper.rb", "whenever.gemspec"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://github.com/javan/whenever}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Whenever", "--main", "README.rdoc"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{whenever}
20
+ s.rubygems_version = %q{1.3.1}
21
+ s.summary = %q{Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever.}
22
+ s.test_files = ["test/cron_test.rb", "test/output_command_test.rb", "test/output_env_test.rb", "test/output_runner_test.rb", "test/test_helper.rb"]
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<chronic>, [">= 0"])
30
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
31
+ else
32
+ s.add_dependency(%q<chronic>, [">= 0"])
33
+ s.add_dependency(%q<activesupport>, [">= 0"])
34
+ end
35
+ else
36
+ s.add_dependency(%q<chronic>, [">= 0"])
37
+ s.add_dependency(%q<activesupport>, [">= 0"])
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: javan-whenever
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Javan Makhmali
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-15 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: chronic
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever.
36
+ email: javan@javan.us
37
+ executables:
38
+ - whenever
39
+ - wheneverize
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - bin/whenever
44
+ - bin/wheneverize
45
+ - lib/base.rb
46
+ - lib/job_list.rb
47
+ - lib/job_types/default.rb
48
+ - lib/job_types/runner.rb
49
+ - lib/outputs/cron.rb
50
+ - lib/tasks/whenever.rake
51
+ - lib/whenever.rb
52
+ - README.rdoc
53
+ files:
54
+ - bin/whenever
55
+ - bin/wheneverize
56
+ - lib/base.rb
57
+ - lib/job_list.rb
58
+ - lib/job_types/default.rb
59
+ - lib/job_types/runner.rb
60
+ - lib/outputs/cron.rb
61
+ - lib/tasks/whenever.rake
62
+ - lib/whenever.rb
63
+ - Manifest
64
+ - Rakefile
65
+ - README.rdoc
66
+ - test/cron_test.rb
67
+ - test/output_command_test.rb
68
+ - test/output_env_test.rb
69
+ - test/output_runner_test.rb
70
+ - test/test_helper.rb
71
+ - whenever.gemspec
72
+ has_rdoc: true
73
+ homepage: http://github.com/javan/whenever
74
+ post_install_message:
75
+ rdoc_options:
76
+ - --line-numbers
77
+ - --inline-source
78
+ - --title
79
+ - Whenever
80
+ - --main
81
+ - README.rdoc
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ version:
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "1.2"
95
+ version:
96
+ requirements: []
97
+
98
+ rubyforge_project: whenever
99
+ rubygems_version: 1.2.0
100
+ signing_key:
101
+ specification_version: 2
102
+ summary: Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever.
103
+ test_files:
104
+ - test/cron_test.rb
105
+ - test/output_command_test.rb
106
+ - test/output_env_test.rb
107
+ - test/output_runner_test.rb
108
+ - test/test_helper.rb