andrew-whenever 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ module Whenever
2
+ module Output
3
+ class Cron
4
+ class OutputRedirection
5
+
6
+ def initialize(output)
7
+ @output = output
8
+ end
9
+
10
+ def to_s
11
+ return '' unless defined?(@output)
12
+ case @output
13
+ when String then redirect_from_string
14
+ when Hash then redirect_from_hash
15
+ when NilClass then ">> /dev/null 2>&1"
16
+ else ''
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ def stdout
23
+ return unless @output.has_key?(:standard)
24
+ @output[:standard].nil? ? '/dev/null' : @output[:standard]
25
+ end
26
+
27
+ def stderr
28
+ return unless @output.has_key?(:error)
29
+ @output[:error].nil? ? '/dev/null' : @output[:error]
30
+ end
31
+
32
+ def redirect_from_hash
33
+ case
34
+ when stdout == '/dev/null' && stderr == '/dev/null'
35
+ "> /dev/null 2>&1"
36
+ when stdout && stderr == '/dev/null'
37
+ ">> #{stdout} 2> /dev/null"
38
+ when stdout && stderr
39
+ ">> #{stdout} 2>> #{stderr}"
40
+ when stderr == '/dev/null'
41
+ "2> /dev/null"
42
+ when stderr
43
+ "2>> #{stderr}"
44
+ when stdout == '/dev/null'
45
+ "> /dev/null"
46
+ when stdout
47
+ ">> #{stdout}"
48
+ else
49
+ ''
50
+ end
51
+ end
52
+
53
+ def redirect_from_string
54
+ ">> #{@output} 2>&1"
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Whenever
2
+ VERSION = '0.4.3'
3
+ end unless defined?(Whenever::VERSION)
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
+
3
+ class CommandLineTest < Test::Unit::TestCase
4
+
5
+ context "A command line write" do
6
+ setup do
7
+ File.expects(:exists?).with('config/schedule.rb').returns(true)
8
+ @command = Whenever::CommandLine.new(:write => true, :identifier => 'My identifier')
9
+ @task = "#{two_hours} /my/command"
10
+ Whenever.expects(:cron).returns(@task)
11
+ end
12
+
13
+ should "output the cron job with identifier blocks" do
14
+ output = <<-EXPECTED
15
+ # Begin Whenever generated tasks for: My identifier
16
+ #{@task}
17
+ # End Whenever generated tasks for: My identifier
18
+ EXPECTED
19
+
20
+ assert_equal output, @command.send(:whenever_cron)
21
+ end
22
+
23
+ should "write the crontab when run" do
24
+ @command.expects(:write_crontab).returns(true)
25
+ assert @command.run
26
+ end
27
+ end
28
+
29
+ context "A command line update" do
30
+ setup do
31
+ File.expects(:exists?).with('config/schedule.rb').returns(true)
32
+ @command = Whenever::CommandLine.new(:update => true, :identifier => 'My identifier')
33
+ @task = "#{two_hours} /my/command"
34
+ Whenever.expects(:cron).returns(@task)
35
+ end
36
+
37
+ should "add the cron to the end of the file if there is no existing identifier block" do
38
+ existing = '# Existing crontab'
39
+ @command.expects(:read_crontab).at_least_once.returns(existing)
40
+
41
+ new_cron = <<-EXPECTED
42
+ #{existing}
43
+
44
+ # Begin Whenever generated tasks for: My identifier
45
+ #{@task}
46
+ # End Whenever generated tasks for: My identifier
47
+ EXPECTED
48
+
49
+ assert_equal new_cron, @command.send(:updated_crontab)
50
+
51
+ @command.expects(:write_crontab).with(new_cron).returns(true)
52
+ assert @command.run
53
+ end
54
+
55
+ should "replace an existing block if the identifier matches" do
56
+ existing = <<-EXISTING_CRON
57
+ # Something
58
+
59
+ # Begin Whenever generated tasks for: My identifier
60
+ My whenever job that was already here
61
+ # End Whenever generated tasks for: My identifier
62
+
63
+ # Begin Whenever generated tasks for: Other identifier
64
+ This shouldn't get replaced
65
+ # End Whenever generated tasks for: Other identifier
66
+ EXISTING_CRON
67
+
68
+ @command.expects(:read_crontab).at_least_once.returns(existing)
69
+
70
+ new_cron = <<-NEW_CRON
71
+ # Something
72
+
73
+ # Begin Whenever generated tasks for: My identifier
74
+ #{@task}
75
+ # End Whenever generated tasks for: My identifier
76
+
77
+ # Begin Whenever generated tasks for: Other identifier
78
+ This shouldn't get replaced
79
+ # End Whenever generated tasks for: Other identifier
80
+ NEW_CRON
81
+
82
+ assert_equal new_cron, @command.send(:updated_crontab)
83
+
84
+ @command.expects(:write_crontab).with(new_cron).returns(true)
85
+ assert @command.run
86
+ end
87
+ end
88
+
89
+ context "A command line update with no identifier" do
90
+ setup do
91
+ File.expects(:exists?).with('config/schedule.rb').returns(true)
92
+ Whenever::CommandLine.any_instance.expects(:default_identifier).returns('DEFAULT')
93
+ @command = Whenever::CommandLine.new(:update => true, :file => @file)
94
+ end
95
+
96
+ should "use the default identifier" do
97
+ assert_equal "Whenever generated tasks for: DEFAULT", @command.send(:comment_base)
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,230 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/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
+
37
+ should 'parse correctly when given an "at" with hours as a integer' do
38
+ assert_equal "0,30 3 * * *", parse_time(30.minutes, nil, 3)
39
+ end
40
+ end
41
+
42
+ context "When parsing time in hours" do
43
+ should "parse correctly" do
44
+ assert_equal '0 * * * *', parse_time(1.hour)
45
+ assert_equal '0 0,2,4,6,8,10,12,14,16,18,20,22 * * *', parse_time(2.hours)
46
+ assert_equal '0 0,3,6,9,12,15,18,21 * * *', parse_time(3.hours)
47
+ assert_equal '0 5,10,15,20 * * *', parse_time(5.hours)
48
+ assert_equal '0 17 * * *', parse_time(17.hours)
49
+ assert_not_equal '0 24 * * *', parse_time(24.hours) # 24 hours bumps up into the day range
50
+ end
51
+
52
+ (2..23).each do |num|
53
+ should "parse correctly for #{num} hours" do
54
+ start = 0
55
+ start += num unless 24.modulo(num).zero?
56
+ hours = (start..23).step(num).to_a
57
+
58
+ assert_equal "0 #{hours.join(',')} * * *", parse_time(num.hours)
59
+ end
60
+ end
61
+
62
+ should "parse correctly when given an 'at' with minutes as an Integer" do
63
+ assert_minutes_equals "1", 1
64
+ assert_minutes_equals "14", 14
65
+ assert_minutes_equals "27", 27
66
+ assert_minutes_equals "55", 55
67
+ end
68
+
69
+ should "parse correctly when given an 'at' with minutes as a Time" do
70
+ # Basically just testing that Chronic parses some times and we get the minutes out of it
71
+ assert_minutes_equals "1", '3:01am'
72
+ assert_minutes_equals "1", 'January 21 2:01 PM'
73
+ assert_minutes_equals "0", 'midnight'
74
+ assert_minutes_equals "59", '13:59'
75
+ end
76
+ end
77
+
78
+ context "When parsing time in days (of month)" do
79
+ should "parse correctly" do
80
+ assert_equal '0 0 * * *', parse_time(1.days)
81
+ assert_equal '0 0 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31 * *', parse_time(2.days)
82
+ assert_equal '0 0 1,5,9,13,17,21,25,29 * *', parse_time(4.days)
83
+ assert_equal '0 0 1,8,15,22 * *', parse_time(7.days)
84
+ assert_equal '0 0 1,17 * *', parse_time(16.days)
85
+ assert_equal '0 0 17 * *', parse_time(17.days)
86
+ assert_equal '0 0 29 * *', parse_time(29.days)
87
+ assert_not_equal '0 0 30 * *', parse_time(30.days) # 30 days bumps into the month range
88
+ end
89
+
90
+ should "parse correctly when given an 'at' with hours, minutes as a Time" do
91
+ # first param is an array with [hours, minutes]
92
+ assert_hours_and_minutes_equals %w(3 45), '3:45am'
93
+ assert_hours_and_minutes_equals %w(20 1), '8:01pm'
94
+ assert_hours_and_minutes_equals %w(0 0), 'midnight'
95
+ assert_hours_and_minutes_equals %w(1 23), '1:23 AM'
96
+ assert_hours_and_minutes_equals %w(23 59), 'March 21 11:59 pM'
97
+ end
98
+
99
+ should "parse correctly when given an 'at' with hours as an Integer" do
100
+ # first param is an array with [hours, minutes]
101
+ assert_hours_and_minutes_equals %w(1 0), 1
102
+ assert_hours_and_minutes_equals %w(3 0), 3
103
+ assert_hours_and_minutes_equals %w(15 0), 15
104
+ assert_hours_and_minutes_equals %w(19 0), 19
105
+ assert_hours_and_minutes_equals %w(23 0), 23
106
+ end
107
+ end
108
+
109
+ context "When parsing time in months" do
110
+ should "parse correctly" do
111
+ assert_equal '0 0 1 * *', parse_time(1.month)
112
+ assert_equal '0 0 1 1,3,5,7,9,11 *', parse_time(2.months)
113
+ assert_equal '0 0 1 1,4,7,10 *', parse_time(3.months)
114
+ assert_equal '0 0 1 1,5,9 *', parse_time(4.months)
115
+ assert_equal '0 0 1 1,6 *', parse_time(5.months)
116
+ assert_equal '0 0 1 7 *', parse_time(7.months)
117
+ assert_equal '0 0 1 8 *', parse_time(8.months)
118
+ assert_equal '0 0 1 9 *', parse_time(9.months)
119
+ assert_equal '0 0 1 10 *', parse_time(10.months)
120
+ assert_equal '0 0 1 11 *', parse_time(11.months)
121
+ assert_equal '0 0 1 12 *', parse_time(12.months)
122
+ end
123
+
124
+ should "parse correctly when given an 'at' with days, hours, minutes as a Time" do
125
+ # first param is an array with [days, hours, minutes]
126
+ assert_days_and_hours_and_minutes_equals %w(1 3 45), 'January 1st 3:45am'
127
+ assert_days_and_hours_and_minutes_equals %w(11 23 0), 'Feb 11 11PM'
128
+ assert_days_and_hours_and_minutes_equals %w(22 1 1), 'march 22nd at 1:01 am'
129
+ assert_days_and_hours_and_minutes_equals %w(23 0 0), 'march 22nd at midnight' # looks like midnight means the next day
130
+ end
131
+
132
+ should "parse correctly when given an 'at' with days as an Integer" do
133
+ # first param is an array with [days, hours, minutes]
134
+ assert_days_and_hours_and_minutes_equals %w(1 0 0), 1
135
+ assert_days_and_hours_and_minutes_equals %w(15 0 0), 15
136
+ assert_days_and_hours_and_minutes_equals %w(29 0 0), 29
137
+ end
138
+ end
139
+
140
+ context "When parsing time in days (of week)" do
141
+ should "parse days of the week correctly" do
142
+ {
143
+ '0' => %w(sun Sunday SUNDAY SUN),
144
+ '1' => %w(mon Monday MONDAY MON),
145
+ '2' => %w(tue tues Tuesday TUESDAY TUE),
146
+ '3' => %w(wed Wednesday WEDNESDAY WED),
147
+ '4' => %w(thu thurs thur Thursday THURSDAY THU),
148
+ '5' => %w(fri Friday FRIDAY FRI),
149
+ '6' => %w(sat Saturday SATURDAY SAT)
150
+ }.each do |day, day_tests|
151
+ day_tests.each do |day_test|
152
+ assert_equal "0 0 * * #{day}", parse_time(day_test)
153
+ end
154
+ end
155
+ end
156
+
157
+ should "allow additional directives" do
158
+ assert_equal '30 13 * * 5', parse_time('friday', nil, "1:30 pm")
159
+ assert_equal '22 2 * * 1', parse_time('Monday', nil, "2:22am")
160
+ assert_equal '55 17 * * 4', parse_time('THU', nil, "5:55PM")
161
+ end
162
+
163
+ should "parse weekday correctly" do
164
+ assert_equal '0 0 * * 1-5', parse_time('weekday')
165
+ assert_equal '0 0 * * 1-5', parse_time('Weekdays')
166
+ assert_equal '0 1 * * 1-5', parse_time('Weekdays', nil, "1:00 am")
167
+ assert_equal '59 5 * * 1-5', parse_time('Weekdays', nil, "5:59 am")
168
+ end
169
+
170
+ should "parse weekend correctly" do
171
+ assert_equal '0 0 * * 6,0', parse_time('weekend')
172
+ assert_equal '0 0 * * 6,0', parse_time('Weekends')
173
+ assert_equal '0 7 * * 6,0', parse_time('Weekends', nil, "7am")
174
+ assert_equal '2 18 * * 6,0', parse_time('Weekends', nil, "6:02PM")
175
+ end
176
+ end
177
+
178
+ context "When parsing time using the cron shortcuts" do
179
+ should "parse a :symbol into the correct shortcut" do
180
+ assert_equal '@reboot', parse_time(:reboot)
181
+ assert_equal '@annually', parse_time(:year)
182
+ assert_equal '@annually', parse_time(:yearly)
183
+ assert_equal '@daily', parse_time(:day)
184
+ assert_equal '@daily', parse_time(:daily)
185
+ assert_equal '@midnight', parse_time(:midnight)
186
+ assert_equal '@monthly', parse_time(:month)
187
+ assert_equal '@monthly', parse_time(:monthly)
188
+ assert_equal '@hourly', parse_time(:hour)
189
+ assert_equal '@hourly', parse_time(:hourly)
190
+ end
191
+
192
+ should "raise an exception if a valid shortcut is given but also an :at" do
193
+ assert_raises ArgumentError do
194
+ parse_time(:hour, nil, "1:00 am")
195
+ end
196
+
197
+ assert_raises ArgumentError do
198
+ parse_time(:reboot, nil, 5)
199
+ end
200
+
201
+ assert_raises ArgumentError do
202
+ parse_time(:day, nil, '4:20pm')
203
+ end
204
+ end
205
+ end
206
+
207
+ private
208
+
209
+ def assert_days_and_hours_and_minutes_equals(expected, time)
210
+ cron = parse_time(2.months, 'some task', time)
211
+ minutes, hours, days, *garbage = cron.split(' ')
212
+ assert_equal expected, [days, hours, minutes]
213
+ end
214
+
215
+ def assert_hours_and_minutes_equals(expected, time)
216
+ cron = parse_time(2.days, 'some task', time)
217
+ minutes, hours, *garbage = cron.split(' ')
218
+ assert_equal expected, [hours, minutes]
219
+ end
220
+
221
+ def assert_minutes_equals(expected, time)
222
+ cron = parse_time(2.hours, 'some task', time)
223
+ assert_equal expected, cron.split(' ')[0]
224
+ end
225
+
226
+ def parse_time(time = nil, task = nil, at = nil)
227
+ Whenever::Output::Cron.new(time, task, at).time_in_cron_syntax
228
+ end
229
+
230
+ end
@@ -0,0 +1,178 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
+
3
+ class OutputAtTest < Test::Unit::TestCase
4
+
5
+ context "weekday at a (single) given time" do
6
+ setup do
7
+ @output = Whenever.cron \
8
+ <<-file
9
+ every "weekday", :at => '5:02am' do
10
+ command "blahblah"
11
+ end
12
+ file
13
+ end
14
+
15
+ should "output the command using that time" do
16
+ assert_match '2 5 * * 1-5 blahblah', @output
17
+ end
18
+ end
19
+
20
+ context "weekday at a multiple diverse times, via an array" do
21
+ setup do
22
+ @output = Whenever.cron \
23
+ <<-file
24
+ every "weekday", :at => %w(5:02am 3:52pm) do
25
+ command "blahblah"
26
+ end
27
+ file
28
+ end
29
+
30
+ should "output the commands for both times given" do
31
+ assert_match '2 5 * * 1-5 blahblah', @output
32
+ assert_match '52 15 * * 1-5 blahblah', @output
33
+ end
34
+ end
35
+
36
+ context "weekday at a multiple diverse times, comma separated" do
37
+ setup do
38
+ @output = Whenever.cron \
39
+ <<-file
40
+ every "weekday", :at => '5:02am, 3:52pm' do
41
+ command "blahblah"
42
+ end
43
+ file
44
+ end
45
+
46
+ should "output the commands for both times given" do
47
+ assert_match '2 5 * * 1-5 blahblah', @output
48
+ assert_match '52 15 * * 1-5 blahblah', @output
49
+ end
50
+ end
51
+
52
+ context "weekday at a multiple aligned times" do
53
+ setup do
54
+ @output = Whenever.cron \
55
+ <<-file
56
+ every "weekday", :at => '5:02am, 3:02pm' do
57
+ command "blahblah"
58
+ end
59
+ file
60
+ end
61
+
62
+ should "output the command using one entry because the times are aligned" do
63
+ assert_match '2 5,15 * * 1-5 blahblah', @output
64
+ end
65
+ end
66
+
67
+ context "various days at a various aligned times" do
68
+ setup do
69
+ @output = Whenever.cron \
70
+ <<-file
71
+ every "mon,wed,fri", :at => '5:02am, 3:02pm' do
72
+ command "blahblah"
73
+ end
74
+ file
75
+ end
76
+
77
+ should "output the command using one entry because the times are aligned" do
78
+ assert_match '2 5,15 * * 1,3,5 blahblah', @output
79
+ end
80
+ end
81
+
82
+ context "various days at a various aligned times using a runner" do
83
+ setup do
84
+ @output = Whenever.cron \
85
+ <<-file
86
+ set :path, '/your/path'
87
+ every "mon,wed,fri", :at => '5:02am, 3:02pm' do
88
+ runner "blahblah"
89
+ end
90
+ file
91
+ end
92
+
93
+ should "output the runner using one entry because the times are aligned" do
94
+ assert_match '2 5,15 * * 1,3,5 cd /your/path && script/runner -e production "blahblah"', @output
95
+ end
96
+ end
97
+
98
+ context "various days at a various aligned times using a rake task" do
99
+ setup do
100
+ @output = Whenever.cron \
101
+ <<-file
102
+ set :path, '/your/path'
103
+ every "mon,wed,fri", :at => '5:02am, 3:02pm' do
104
+ rake "blah:blah"
105
+ end
106
+ file
107
+ end
108
+
109
+ should "output the rake task using one entry because the times are aligned" do
110
+ assert_match '2 5,15 * * 1,3,5 cd /your/path && RAILS_ENV=production /usr/bin/env rake blah:blah', @output
111
+ end
112
+ end
113
+
114
+ context "A command every 1.month at very diverse times" do
115
+ setup do
116
+ @output = Whenever.cron \
117
+ <<-file
118
+ every [1.month, 1.day], :at => 'january 5:02am, june 17th at 2:22pm, june 3rd at 3:33am' do
119
+ command "blahblah"
120
+ end
121
+ file
122
+ end
123
+
124
+ should "output 6 commands since none align" do
125
+ # The 1.month commands
126
+ assert_match '2 5 1 * * blahblah', @output
127
+ assert_match '22 14 17 * * blahblah', @output
128
+ assert_match '33 3 3 * * blahblah', @output
129
+
130
+ # The 1.day commands
131
+ assert_match '2 5 * * * blahblah', @output
132
+ assert_match '22 14 * * * blahblah', @output
133
+ assert_match '33 3 * * * blahblah', @output
134
+ end
135
+ end
136
+
137
+ context "Multiple commands output every :reboot" do
138
+ setup do
139
+ @output = Whenever.cron \
140
+ <<-file
141
+ every :reboot do
142
+ command "command_1"
143
+ command "command_2"
144
+ end
145
+ file
146
+ end
147
+
148
+ should "output both commands @reboot" do
149
+ assert_match "@reboot command_1", @output
150
+ assert_match "@reboot command_2", @output
151
+ end
152
+ end
153
+
154
+ context "Many different job types output every :day" do
155
+ setup do
156
+ @output = Whenever.cron \
157
+ <<-file
158
+ set :path, '/your/path'
159
+ every :day do
160
+ rake "blah:blah"
161
+ runner "runner_1"
162
+ command "command_1"
163
+ runner "runner_2"
164
+ command "command_2"
165
+ end
166
+ file
167
+ end
168
+
169
+ should "output all of the commands @daily" do
170
+ assert_match '@daily cd /your/path && RAILS_ENV=production /usr/bin/env rake blah:blah', @output
171
+ assert_match '@daily cd /your/path && script/runner -e production "runner_1"', @output
172
+ assert_match '@daily command_1', @output
173
+ assert_match '@daily cd /your/path && script/runner -e production "runner_2"', @output
174
+ assert_match '@daily command_2', @output
175
+ end
176
+ end
177
+
178
+ end