whenever 0.9.7 → 1.0.0

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.
data/lib/whenever/cron.rb CHANGED
@@ -3,16 +3,20 @@ require 'chronic'
3
3
  module Whenever
4
4
  module Output
5
5
  class Cron
6
+ DAYS = %w(sun mon tue wed thu fri sat)
7
+ MONTHS = %w(jan feb mar apr may jun jul aug sep oct nov dec)
6
8
  KEYWORDS = [:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly]
7
- REGEX = /^(@(#{KEYWORDS.join '|'})|((\*?[\d\/,\-]*)\s*){5})$/
9
+ REGEX = /^(@(#{KEYWORDS.join '|'})|((\*?[\d\/,\-]*)\s){3}(\*?([\d\/,\-]|(#{MONTHS.join '|'}))*\s)(\*?([\d\/,\-]|(#{DAYS.join '|'}))*))$/i
8
10
 
9
11
  attr_accessor :time, :task
10
12
 
11
- def initialize(time = nil, task = nil, at = nil)
13
+ def initialize(time = nil, task = nil, at = nil, options = {})
14
+ chronic_options = options[:chronic_options] || {}
15
+
12
16
  @at_given = at
13
17
  @time = time
14
18
  @task = task
15
- @at = at.is_a?(String) ? (Chronic.parse(at) || 0) : (at || 0)
19
+ @at = at.is_a?(String) ? (Chronic.parse(at, chronic_options) || 0) : (at || 0)
16
20
  end
17
21
 
18
22
  def self.enumerate(item, detect_cron = true)
@@ -30,10 +34,10 @@ module Whenever
30
34
  items
31
35
  end
32
36
 
33
- def self.output(times, job)
37
+ def self.output(times, job, options = {})
34
38
  enumerate(times).each do |time|
35
39
  enumerate(job.at, false).each do |at|
36
- yield new(time, job.output, at).output
40
+ yield new(time, job.output, at, options).output
37
41
  end
38
42
  end
39
43
  end
@@ -54,18 +58,18 @@ module Whenever
54
58
 
55
59
  protected
56
60
  def day_given?
57
- months = %w(jan feb mar apr may jun jul aug sep oct nov dec)
58
- @at_given.is_a?(String) && months.any? { |m| @at_given.downcase.index(m) }
61
+ @at_given.is_a?(String) && (MONTHS.any? { |m| @at_given.downcase.index(m) } || @at_given[/\d\/\d/])
59
62
  end
60
63
 
61
64
  def parse_symbol
62
65
  shortcut = case @time
63
66
  when *KEYWORDS then "@#{@time}" # :reboot => '@reboot'
64
- when :year then Whenever.seconds(12, :months)
67
+ when :year then Whenever.seconds(1, :year)
65
68
  when :day then Whenever.seconds(1, :day)
66
69
  when :month then Whenever.seconds(1, :month)
67
70
  when :week then Whenever.seconds(1, :week)
68
71
  when :hour then Whenever.seconds(1, :hour)
72
+ when :minute then Whenever.seconds(1, :minute)
69
73
  end
70
74
 
71
75
  if shortcut.is_a?(Numeric)
@@ -92,23 +96,36 @@ module Whenever
92
96
  timing[0] = comma_separated_timing(minute_frequency, 59, @at || 0)
93
97
  when Whenever.seconds(1, :hour)...Whenever.seconds(1, :day)
94
98
  hour_frequency = (@time / 60 / 60).round
95
- timing[0] = @at.is_a?(Time) ? @at.min : @at
99
+ timing[0] = @at.is_a?(Time) ? @at.min : range_or_integer(@at, 0..59, 'Minute')
96
100
  timing[1] = comma_separated_timing(hour_frequency, 23)
97
101
  when Whenever.seconds(1, :day)...Whenever.seconds(1, :month)
98
102
  day_frequency = (@time / 24 / 60 / 60).round
99
103
  timing[0] = @at.is_a?(Time) ? @at.min : 0
100
- timing[1] = @at.is_a?(Time) ? @at.hour : @at
104
+ timing[1] = @at.is_a?(Time) ? @at.hour : range_or_integer(@at, 0..23, 'Hour')
101
105
  timing[2] = comma_separated_timing(day_frequency, 31, 1)
102
- when Whenever.seconds(1, :month)..Whenever.seconds(12, :months)
103
- month_frequency = (@time / 30 / 24 / 60 / 60).round
106
+ when Whenever.seconds(1, :month)...Whenever.seconds(1, :year)
107
+ month_frequency = (@time / 30 / 24 / 60 / 60).round
104
108
  timing[0] = @at.is_a?(Time) ? @at.min : 0
105
109
  timing[1] = @at.is_a?(Time) ? @at.hour : 0
106
110
  timing[2] = if @at.is_a?(Time)
107
111
  day_given? ? @at.day : 1
108
112
  else
109
- @at.zero? ? 1 : @at
113
+ @at == 0 ? 1 : range_or_integer(@at, 1..31, 'Day')
110
114
  end
111
115
  timing[3] = comma_separated_timing(month_frequency, 12, 1)
116
+ when Whenever.seconds(1, :year)
117
+ timing[0] = @at.is_a?(Time) ? @at.min : 0
118
+ timing[1] = @at.is_a?(Time) ? @at.hour : 0
119
+ timing[2] = if @at.is_a?(Time)
120
+ day_given? ? @at.day : 1
121
+ else
122
+ 1
123
+ end
124
+ timing[3] = if @at.is_a?(Time)
125
+ day_given? ? @at.month : 1
126
+ else
127
+ @at == 0 ? 1 : range_or_integer(@at, 1..12, 'Month')
128
+ end
112
129
  else
113
130
  return parse_as_string
114
131
  end
@@ -126,11 +143,22 @@ module Whenever
126
143
  return (timing << '1-5') * " " if string.downcase.index('weekday')
127
144
  return (timing << '6,0') * " " if string.downcase.index('weekend')
128
145
 
129
- %w(sun mon tue wed thu fri sat).each_with_index do |day, i|
146
+ DAYS.each_with_index do |day, i|
130
147
  return (timing << i) * " " if string.downcase.index(day)
131
148
  end
132
149
 
133
- raise ArgumentError, "Couldn't parse: #{@time}"
150
+ raise ArgumentError, "Couldn't parse: #{@time.inspect}"
151
+ end
152
+
153
+ def range_or_integer(at, valid_range, name)
154
+ must_be_between = "#{name} must be between #{valid_range.min}-#{valid_range.max}"
155
+ if at.is_a?(Range)
156
+ raise ArgumentError, "#{must_be_between}, #{at.min} given" unless valid_range.include?(at.min)
157
+ raise ArgumentError, "#{must_be_between}, #{at.max} given" unless valid_range.include?(at.max)
158
+ return "#{at.min}-#{at.max}"
159
+ end
160
+ raise ArgumentError, "#{must_be_between}, #{at} given" unless valid_range.include?(at)
161
+ at
134
162
  end
135
163
 
136
164
  def comma_separated_timing(frequency, max, start = 0)
data/lib/whenever/job.rb CHANGED
@@ -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
@@ -1,4 +1,4 @@
1
- class Numeric
1
+ Numeric.class_eval do
2
2
  def respond_to?(method, include_private = false)
3
3
  super || Whenever::NumericSeconds.public_method_defined?(method)
4
4
  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 = '1.0.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
@@ -204,4 +204,43 @@ class OutputAtTest < Whenever::TestCase
204
204
 
205
205
  assert_match '0 0 27,31 * * blahblah', output
206
206
  end
207
+
208
+ test "using custom Chronic configuration to specify time using 24 hour clock" do
209
+ output = Whenever.cron \
210
+ <<-file
211
+ set :job_template, nil
212
+ set :chronic_options, :hours24 => true
213
+ every 1.day, :at => '03:00' do
214
+ command "blahblah"
215
+ end
216
+ file
217
+
218
+ assert_match '0 3 * * * blahblah', output
219
+ end
220
+
221
+ test "using custom Chronic configuration to specify date using little endian preference" do
222
+ output = Whenever.cron \
223
+ <<-file
224
+ set :job_template, nil
225
+ set :chronic_options, :endian_precedence => :little
226
+ every 1.month, :at => '02/03 10:15' do
227
+ command "blahblah"
228
+ end
229
+ file
230
+
231
+ assert_match '15 10 2 * * blahblah', output
232
+ end
233
+
234
+ test "using custom Chronic configuration to specify time using 24 hour clock and date using little endian preference" do
235
+ output = Whenever.cron \
236
+ <<-file
237
+ set :job_template, nil
238
+ set :chronic_options, :hours24 => true, :endian_precedence => :little
239
+ every 1.month, :at => '01/02 04:30' do
240
+ command "blahblah"
241
+ end
242
+ file
243
+
244
+ assert_match '30 4 1 * * blahblah', output
245
+ end
207
246
  end