whenever 0.9.7 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,12 +2,13 @@ require 'shellwords'
2
2
 
3
3
  module Whenever
4
4
  class Job
5
- attr_reader :at, :roles
5
+ attr_reader :at, :roles, :mailto
6
6
 
7
7
  def initialize(options = {})
8
8
  @options = options
9
9
  @at = options.delete(:at)
10
10
  @template = options.delete(:template)
11
+ @mailto = options.fetch(:mailto, :default_mailto)
11
12
  @job_template = options.delete(:job_template) || ":job"
12
13
  @roles = Array(options.delete(:roles))
13
14
  @options[:output] = options.has_key?(:output) ? Whenever::Output::Redirection.new(options[:output]).to_s : ''
@@ -30,10 +30,17 @@ module Whenever
30
30
  return if @pre_set_variables[variable]
31
31
 
32
32
  instance_variable_set("@#{variable}".to_sym, value)
33
- self.class.send(:attr_reader, variable.to_sym)
34
33
  @set_variables[variable] = value
35
34
  end
36
35
 
36
+ def method_missing(name, *args, &block)
37
+ @set_variables.has_key?(name) ? @set_variables[name] : super
38
+ end
39
+
40
+ def self.respond_to?(name, include_private = false)
41
+ @set_variables.has_key?(name) || super
42
+ end
43
+
37
44
  def env(variable, value)
38
45
  @env[variable.to_s] = value
39
46
  end
@@ -50,13 +57,16 @@ module Whenever
50
57
  options = { :task => task, :template => template }
51
58
  options.merge!(args[0]) if args[0].is_a? Hash
52
59
 
60
+ options[:mailto] ||= @options.fetch(:mailto, :default_mailto)
61
+
53
62
  # :cron_log was an old option for output redirection, it remains for backwards compatibility
54
63
  options[:output] = (options[:cron_log] || @cron_log) if defined?(@cron_log) || options.has_key?(:cron_log)
55
64
  # :output is the newer, more flexible option.
56
65
  options[:output] = @output if defined?(@output) && !options.has_key?(:output)
57
66
 
58
- @jobs[@current_time_scope] ||= []
59
- @jobs[@current_time_scope] << Whenever::Job.new(@options.merge(@set_variables).merge(options))
67
+ @jobs[options.fetch(:mailto)] ||= {}
68
+ @jobs[options.fetch(:mailto)][@current_time_scope] ||= []
69
+ @jobs[options.fetch(:mailto)][@current_time_scope] << Whenever::Job.new(@options.merge(@set_variables).merge(options))
60
70
  end
61
71
  end
62
72
  end
@@ -125,31 +135,51 @@ module Whenever
125
135
  entries.map { |entry| entry.join(' ') }
126
136
  end
127
137
 
138
+ def cron_jobs_of_time(time, jobs)
139
+ shortcut_jobs, regular_jobs = [], []
140
+
141
+ jobs.each do |job|
142
+ next unless roles.empty? || roles.any? do |r|
143
+ job.has_role?(r)
144
+ end
145
+ Whenever::Output::Cron.output(time, job, :chronic_options => @chronic_options) do |cron|
146
+ cron << "\n\n"
147
+
148
+ if cron[0,1] == "@"
149
+ shortcut_jobs << cron
150
+ else
151
+ regular_jobs << cron
152
+ end
153
+ end
154
+ end
155
+
156
+ shortcut_jobs.join + combine(regular_jobs).join
157
+ end
158
+
128
159
  def cron_jobs
129
160
  return if @jobs.empty?
130
161
 
131
- shortcut_jobs = []
132
- regular_jobs = []
162
+ output = []
133
163
 
134
- output_all = roles.empty?
135
- @jobs.each do |time, jobs|
136
- jobs.each do |job|
137
- next unless output_all || roles.any? do |r|
138
- job.has_role?(r)
139
- end
140
- Whenever::Output::Cron.output(time, job) do |cron|
141
- cron << "\n\n"
164
+ # jobs with default mailto's must be output before the ones with non-default mailto's.
165
+ @jobs.delete(:default_mailto) { Hash.new }.each do |time, jobs|
166
+ output << cron_jobs_of_time(time, jobs)
167
+ end
142
168
 
143
- if cron[0,1] == "@"
144
- shortcut_jobs << cron
145
- else
146
- regular_jobs << cron
147
- end
148
- end
169
+ @jobs.each do |mailto, time_and_jobs|
170
+ output_jobs = []
171
+
172
+ time_and_jobs.each do |time, jobs|
173
+ output_jobs << cron_jobs_of_time(time, jobs)
149
174
  end
175
+
176
+ output_jobs.reject! { |output_job| output_job.empty? }
177
+
178
+ output << "MAILTO=#{mailto}\n\n" unless output_jobs.empty?
179
+ output << output_jobs
150
180
  end
151
181
 
152
- shortcut_jobs.join + combine(regular_jobs).join
182
+ output.join
153
183
  end
154
184
  end
155
185
  end
@@ -5,6 +5,10 @@ set :environment, "production"
5
5
  # Path defaults to the directory `whenever` was run from
6
6
  set :path, Whenever.path
7
7
 
8
+ # Custom Chronic configuration for time parsing, empty by default
9
+ # Full list of options at: https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb
10
+ set :chronic_options, {}
11
+
8
12
  # All jobs are wrapped in this template.
9
13
  # http://blog.scoutapp.com/articles/2010/09/07/rvm-and-cron-in-production
10
14
  set :job_template, "/bin/bash -l -c ':job'"
@@ -1,3 +1,3 @@
1
1
  module Whenever
2
- VERSION = '0.9.7'
2
+ VERSION = '0.10.0'
3
3
  end
@@ -2,6 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class CommandLineWriteTest < Whenever::TestCase
4
4
  setup do
5
+ Time.stubs(:now).returns(Time.new(2017, 2, 24, 16, 21, 30, '+01:00'))
5
6
  File.expects(:exist?).with('config/schedule.rb').returns(true)
6
7
  @command = Whenever::CommandLine.new(:write => true, :identifier => 'My identifier')
7
8
  @task = "#{two_hours} /my/command"
@@ -10,9 +11,9 @@ class CommandLineWriteTest < Whenever::TestCase
10
11
 
11
12
  should "output the cron job with identifier blocks" do
12
13
  output = <<-EXPECTED
13
- # Begin Whenever generated tasks for: My identifier
14
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
14
15
  #{@task}
15
- # End Whenever generated tasks for: My identifier
16
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
16
17
  EXPECTED
17
18
 
18
19
  assert_equal output, @command.send(:whenever_cron)
@@ -26,6 +27,7 @@ end
26
27
 
27
28
  class CommandLineUpdateTest < Whenever::TestCase
28
29
  setup do
30
+ Time.stubs(:now).returns(Time.new(2017, 2, 24, 16, 21, 30, '+01:00'))
29
31
  File.expects(:exist?).with('config/schedule.rb').returns(true)
30
32
  @command = Whenever::CommandLine.new(:update => true, :identifier => 'My identifier')
31
33
  @task = "#{two_hours} /my/command"
@@ -39,9 +41,9 @@ class CommandLineUpdateTest < Whenever::TestCase
39
41
  new_cron = <<-EXPECTED
40
42
  #{existing}
41
43
 
42
- # Begin Whenever generated tasks for: My identifier
44
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
43
45
  #{@task}
44
- # End Whenever generated tasks for: My identifier
46
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
45
47
  EXPECTED
46
48
 
47
49
  assert_equal new_cron, @command.send(:updated_crontab)
@@ -50,7 +52,71 @@ EXPECTED
50
52
  assert @command.run
51
53
  end
52
54
 
53
- should "replace an existing block if the identifier matches" do
55
+ should "replace an existing block if the identifier matches and the timestamp doesn't" do
56
+ existing = <<-EXISTING_CRON
57
+ # Something
58
+
59
+ # Begin Whenever generated tasks for: My identifier at: 2017-01-03 08:02:22 +0500
60
+ My whenever job that was already here
61
+ # End Whenever generated tasks for: My identifier at: 2017-01-03 08:22:22 +0500
62
+
63
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
64
+ This shouldn't get replaced
65
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
66
+ EXISTING_CRON
67
+
68
+ new_cron = <<-NEW_CRON
69
+ # Something
70
+
71
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
72
+ #{@task}
73
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
74
+
75
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
76
+ This shouldn't get replaced
77
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
78
+ NEW_CRON
79
+
80
+ @command.expects(:read_crontab).at_least_once.returns(existing)
81
+ assert_equal new_cron, @command.send(:updated_crontab)
82
+
83
+ @command.expects(:write_crontab).with(new_cron).returns(true)
84
+ assert @command.run
85
+ end
86
+
87
+ should "replace an existing block if the identifier matches and the UTC timestamp doesn't" do
88
+ existing = <<-EXISTING_CRON
89
+ # Something
90
+
91
+ # Begin Whenever generated tasks for: My identifier at: 2017-01-03 08:02:22 UTC
92
+ My whenever job that was already here
93
+ # End Whenever generated tasks for: My identifier at: 2017-01-03 08:22:22 UTC
94
+
95
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
96
+ This shouldn't get replaced
97
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
98
+ EXISTING_CRON
99
+
100
+ new_cron = <<-NEW_CRON
101
+ # Something
102
+
103
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
104
+ #{@task}
105
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
106
+
107
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
108
+ This shouldn't get replaced
109
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
110
+ NEW_CRON
111
+
112
+ @command.expects(:read_crontab).at_least_once.returns(existing)
113
+ assert_equal new_cron, @command.send(:updated_crontab)
114
+
115
+ @command.expects(:write_crontab).with(new_cron).returns(true)
116
+ assert @command.run
117
+ end
118
+
119
+ should "replace an existing block if the identifier matches and it doesn't contain a timestamp" do
54
120
  existing = <<-EXISTING_CRON
55
121
  # Something
56
122
 
@@ -58,21 +124,21 @@ EXPECTED
58
124
  My whenever job that was already here
59
125
  # End Whenever generated tasks for: My identifier
60
126
 
61
- # Begin Whenever generated tasks for: Other identifier
127
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
62
128
  This shouldn't get replaced
63
- # End Whenever generated tasks for: Other identifier
129
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
64
130
  EXISTING_CRON
65
131
 
66
132
  new_cron = <<-NEW_CRON
67
133
  # Something
68
134
 
69
- # Begin Whenever generated tasks for: My identifier
135
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
70
136
  #{@task}
71
- # End Whenever generated tasks for: My identifier
137
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
72
138
 
73
- # Begin Whenever generated tasks for: Other identifier
139
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
74
140
  This shouldn't get replaced
75
- # End Whenever generated tasks for: Other identifier
141
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
76
142
  NEW_CRON
77
143
 
78
144
  @command.expects(:read_crontab).at_least_once.returns(existing)
@@ -85,10 +151,11 @@ end
85
151
 
86
152
  class CommandLineUpdateWithBackslashesTest < Whenever::TestCase
87
153
  setup do
154
+ Time.stubs(:now).returns(Time.new(2017, 2, 24, 16, 21, 30, '+01:00'))
88
155
  @existing = <<-EXISTING_CRON
89
- # Begin Whenever generated tasks for: My identifier
156
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
90
157
  script/runner -e production 'puts '\\''hello'\\'''
91
- # End Whenever generated tasks for: My identifier
158
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
92
159
  EXISTING_CRON
93
160
  File.expects(:exist?).with('config/schedule.rb').returns(true)
94
161
  @command = Whenever::CommandLine.new(:update => true, :identifier => 'My identifier')
@@ -104,12 +171,12 @@ end
104
171
  class CommandLineUpdateToSimilarCrontabTest < Whenever::TestCase
105
172
  setup do
106
173
  @existing = <<-EXISTING_CRON
107
- # Begin Whenever generated tasks for: WheneverExisting
108
- # End Whenever generated tasks for: WheneverExisting
174
+ # Begin Whenever generated tasks for: WheneverExisting at: 2017-02-24 16:21:30 +0100
175
+ # End Whenever generated tasks for: WheneverExisting at: 2017-02-24 16:21:30 +0100
109
176
  EXISTING_CRON
110
177
  @new = <<-NEW_CRON
111
- # Begin Whenever generated tasks for: Whenever
112
- # End Whenever generated tasks for: Whenever
178
+ # Begin Whenever generated tasks for: Whenever at: 2017-02-24 16:21:30 +0100
179
+ # End Whenever generated tasks for: Whenever at: 2017-02-24 16:21:30 +0100
113
180
  NEW_CRON
114
181
  File.expects(:exist?).with('config/schedule.rb').returns(true)
115
182
  @command = Whenever::CommandLine.new(:update => true, :identifier => 'Whenever')
@@ -124,12 +191,71 @@ end
124
191
 
125
192
  class CommandLineClearTest < Whenever::TestCase
126
193
  setup do
194
+ Time.stubs(:now).returns(Time.new(2017, 2, 24, 16, 21, 30, '+01:00'))
127
195
  File.expects(:exist?).with('config/schedule.rb').returns(true)
128
196
  @command = Whenever::CommandLine.new(:clear => true, :identifier => 'My identifier')
129
197
  @task = "#{two_hours} /my/command"
130
198
  end
131
199
 
132
- should "clear an existing block if the identifier matches" do
200
+ should "clear an existing block if the identifier matches and the timestamp doesn't" do
201
+ existing = <<-EXISTING_CRON
202
+ # Something
203
+
204
+ # Begin Whenever generated tasks for: My identifier at: 2017-01-03 08:20:02 +0500
205
+ My whenever job that was already here
206
+ # End Whenever generated tasks for: My identifier at: 2017-01-03 08:20:02 +0500
207
+
208
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
209
+ This shouldn't get replaced
210
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
211
+ EXISTING_CRON
212
+
213
+ @command.expects(:read_crontab).at_least_once.returns(existing)
214
+
215
+ new_cron = <<-NEW_CRON
216
+ # Something
217
+
218
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
219
+ This shouldn't get replaced
220
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
221
+ NEW_CRON
222
+
223
+ assert_equal new_cron, @command.send(:updated_crontab)
224
+
225
+ @command.expects(:write_crontab).with(new_cron).returns(true)
226
+ assert @command.run
227
+ end
228
+
229
+ should "clear an existing block if the identifier matches and the UTC timestamp doesn't" do
230
+ existing = <<-EXISTING_CRON
231
+ # Something
232
+
233
+ # Begin Whenever generated tasks for: My identifier at: 2017-01-03 08:20:02 UTC
234
+ My whenever job that was already here
235
+ # End Whenever generated tasks for: My identifier at: 2017-01-03 08:20:02 UTC
236
+
237
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
238
+ This shouldn't get replaced
239
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
240
+ EXISTING_CRON
241
+
242
+ @command.expects(:read_crontab).at_least_once.returns(existing)
243
+
244
+ new_cron = <<-NEW_CRON
245
+ # Something
246
+
247
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
248
+ This shouldn't get replaced
249
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
250
+ NEW_CRON
251
+
252
+ assert_equal new_cron, @command.send(:updated_crontab)
253
+
254
+ @command.expects(:write_crontab).with(new_cron).returns(true)
255
+ assert @command.run
256
+ end
257
+
258
+ should "clear an existing block if the identifier matches and it doesn't have a timestamp" do
133
259
  existing = <<-EXISTING_CRON
134
260
  # Something
135
261
 
@@ -137,9 +263,9 @@ class CommandLineClearTest < Whenever::TestCase
137
263
  My whenever job that was already here
138
264
  # End Whenever generated tasks for: My identifier
139
265
 
140
- # Begin Whenever generated tasks for: Other identifier
266
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
141
267
  This shouldn't get replaced
142
- # End Whenever generated tasks for: Other identifier
268
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
143
269
  EXISTING_CRON
144
270
 
145
271
  @command.expects(:read_crontab).at_least_once.returns(existing)
@@ -147,9 +273,9 @@ EXISTING_CRON
147
273
  new_cron = <<-NEW_CRON
148
274
  # Something
149
275
 
150
- # Begin Whenever generated tasks for: Other identifier
276
+ # Begin Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
151
277
  This shouldn't get replaced
152
- # End Whenever generated tasks for: Other identifier
278
+ # End Whenever generated tasks for: Other identifier at: 2017-02-24 16:21:30 +0100
153
279
  NEW_CRON
154
280
 
155
281
  assert_equal new_cron, @command.send(:updated_crontab)
@@ -173,13 +299,14 @@ end
173
299
 
174
300
  class CommandLineUpdateWithNoIdentifierTest < Whenever::TestCase
175
301
  setup do
302
+ Time.stubs(:now).returns(Time.new(2017, 2, 24, 16, 21, 30, '+01:00'))
176
303
  File.expects(:exist?).with('config/schedule.rb').returns(true)
177
304
  Whenever::CommandLine.any_instance.expects(:default_identifier).returns('DEFAULT')
178
- @command = Whenever::CommandLine.new(:update => true, :file => @file)
305
+ @command = Whenever::CommandLine.new(:update => true)
179
306
  end
180
307
 
181
308
  should "use the default identifier" do
182
- assert_equal "Whenever generated tasks for: DEFAULT", @command.send(:comment_base)
309
+ assert_equal "Whenever generated tasks for: DEFAULT at: 2017-02-24 16:21:30 +0100", @command.send(:comment_base)
183
310
  end
184
311
  end
185
312
 
@@ -287,9 +414,9 @@ class PreparingOutputTest < Whenever::TestCase
287
414
  # Useless Comments
288
415
  # at the top of the file
289
416
 
290
- # Begin Whenever generated tasks for: My identifier
417
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
291
418
  My whenever job that was already here
292
- # End Whenever generated tasks for: My identifier
419
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
293
420
  EXISTING_CRON
294
421
 
295
422
  assert_equal existing, @command.send(:prepare, existing)
@@ -301,15 +428,15 @@ EXISTING_CRON
301
428
  # Useless Comments
302
429
  # at the top of the file
303
430
 
304
- # Begin Whenever generated tasks for: My identifier
431
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
305
432
  My whenever job that was already here
306
- # End Whenever generated tasks for: My identifier
433
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
307
434
  EXISTING_CRON
308
435
 
309
436
  new_cron = <<-NEW_CRON
310
- # Begin Whenever generated tasks for: My identifier
437
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
311
438
  My whenever job that was already here
312
- # End Whenever generated tasks for: My identifier
439
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
313
440
  NEW_CRON
314
441
 
315
442
  assert_equal new_cron, @command.send(:prepare, existing)
@@ -318,9 +445,9 @@ NEW_CRON
318
445
  should "preserve terminating newlines in files" do
319
446
  @command = Whenever::CommandLine.new(:update => true, :identifier => 'My identifier')
320
447
  existing = <<-EXISTING_CRON
321
- # Begin Whenever generated tasks for: My identifier
448
+ # Begin Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
322
449
  My whenever job that was already here
323
- # End Whenever generated tasks for: My identifier
450
+ # End Whenever generated tasks for: My identifier at: 2017-02-24 16:21:30 +0100
324
451
 
325
452
  # A non-Whenever task
326
453
  My non-whenever job that was already here