say_when 0.3.0 → 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.
- checksums.yaml +7 -0
- data/.gitignore +13 -4
- data/Gemfile +1 -1
- data/LICENSE +21 -0
- data/README.md +31 -0
- data/Rakefile +9 -1
- data/lib/generators/say_when/migration/migration_generator.rb +8 -5
- data/lib/generators/say_when/migration/templates/migration.rb +11 -8
- data/lib/say_when/base_job.rb +2 -0
- data/lib/say_when/cron_expression.rb +129 -191
- data/lib/say_when/processor/active_messaging.rb +7 -8
- data/lib/say_when/processor/base.rb +5 -3
- data/lib/say_when/processor/shoryuken.rb +14 -0
- data/lib/say_when/processor/simple.rb +2 -0
- data/lib/say_when/railtie.rb +9 -0
- data/lib/say_when/scheduler.rb +30 -56
- data/lib/say_when/storage/active_record/acts.rb +16 -16
- data/lib/say_when/storage/active_record/job.rb +17 -24
- data/lib/say_when/storage/active_record/job_execution.rb +5 -8
- data/lib/say_when/storage/memory/base.rb +3 -1
- data/lib/say_when/storage/memory/job.rb +14 -42
- data/lib/say_when/tasks.rb +4 -4
- data/lib/say_when/triggers/base.rb +4 -3
- data/lib/say_when/triggers/cron_strategy.rb +4 -3
- data/lib/say_when/triggers/instance_strategy.rb +4 -3
- data/lib/say_when/triggers/once_strategy.rb +3 -2
- data/lib/say_when/version.rb +3 -1
- data/lib/say_when.rb +8 -7
- data/lib/tasks/say_when.rake +3 -1
- data/say_when.gemspec +25 -19
- data/test/active_record_helper.rb +13 -0
- data/{spec → test}/db/schema.rb +4 -2
- data/{spec/spec_helper.rb → test/minitest_helper.rb} +9 -12
- data/test/say_when/cron_expression_spec.rb +74 -0
- data/{spec/say_when/processor/active_messaging_spec.rb → test/say_when/processor/active_messaging_test.rb} +16 -13
- data/test/say_when/scheduler_test.rb +75 -0
- data/test/say_when/storage/active_record/job_test.rb +90 -0
- data/test/say_when/storage/memory/job_test.rb +32 -0
- data/test/say_when/storage/memory/trigger_test.rb +54 -0
- data/test/say_when/triggers/once_strategy_test.rb +23 -0
- data/{spec → test}/support/models.rb +5 -3
- metadata +166 -153
- data/.travis.yml +0 -4
- data/generators/say_when_migration/say_when_migration_generator.rb +0 -11
- data/generators/say_when_migration/templates/migration.rb +0 -48
- data/spec/active_record_spec_helper.rb +0 -11
- data/spec/say_when/cron_expression_spec.rb +0 -72
- data/spec/say_when/scheduler_spec.rb +0 -76
- data/spec/say_when/storage/active_record/job_spec.rb +0 -98
- data/spec/say_when/storage/memory/job_spec.rb +0 -45
- data/spec/say_when/storage/memory/trigger_spec.rb +0 -54
- data/spec/say_when/triggers/once_strategy_spec.rb +0 -22
- data/spec/spec.opts +0 -4
@@ -1,8 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
|
3
5
|
module SayWhen
|
4
6
|
|
5
|
-
# Based on the extended cron capabilties
|
7
|
+
# Based on the extended cron capabilties
|
6
8
|
# http://wiki.opensymphony.com/display/QRTZ1/CronTriggers+Tutorial
|
7
9
|
class CronExpression
|
8
10
|
attr_reader :expression
|
@@ -22,11 +24,11 @@ module SayWhen
|
|
22
24
|
[:seconds, :minutes, :hours, :days_of_month, :months, :days_of_week, :years].each do |f|
|
23
25
|
opts[f] ||= '*'
|
24
26
|
end
|
25
|
-
|
27
|
+
|
26
28
|
"#{opts[:seconds]} #{opts[:minutes]} #{opts[:hours]} #{opts[:days_of_month]} #{opts[:months]} #{opts[:days_of_week]} #{opts[:years]}"
|
27
29
|
end
|
28
|
-
|
29
|
-
|
30
|
+
|
31
|
+
self.time_zone = if opts.has_key?(:time_zone) && !opts[:time_zone].blank?
|
30
32
|
opts[:time_zone]
|
31
33
|
else
|
32
34
|
Time.zone.nil? ? "UTC" : Time.zone.name
|
@@ -34,7 +36,7 @@ module SayWhen
|
|
34
36
|
|
35
37
|
else
|
36
38
|
@expression = expression
|
37
|
-
|
39
|
+
self.time_zone = if time_zone.blank?
|
38
40
|
Time.zone.nil? ? "UTC" : Time.zone.name
|
39
41
|
else
|
40
42
|
time_zone
|
@@ -44,75 +46,62 @@ module SayWhen
|
|
44
46
|
parse
|
45
47
|
validate
|
46
48
|
end
|
47
|
-
|
49
|
+
|
48
50
|
def parse
|
49
|
-
return if
|
50
|
-
vals =
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
59
|
-
|
51
|
+
return if expression.blank?
|
52
|
+
vals = expression.split.map{ |word| word.upcase.gsub(/\s/, '') }
|
53
|
+
self.seconds = SecondsCronValue.new(vals[0])
|
54
|
+
self.minutes = MinutesCronValue.new(vals[1])
|
55
|
+
self.hours = HoursCronValue.new(vals[2])
|
56
|
+
self.days_of_month = DaysOfMonthCronValue.new(vals[3])
|
57
|
+
self.months = MonthsCronValue.new(vals[4])
|
58
|
+
self.days_of_week = DaysOfWeekCronValue.new(vals[5])
|
59
|
+
self.years = YearsCronValue.new(vals[6] || "*")
|
60
|
+
end
|
61
|
+
|
60
62
|
def validate
|
61
|
-
return if
|
62
|
-
raise "days_of_week or days_of_month needs to be ?" if (
|
63
|
+
return if expression.blank?
|
64
|
+
raise "days_of_week or days_of_month needs to be ?" if (days_of_month.is_specified && days_of_week.is_specified)
|
63
65
|
end
|
64
|
-
|
66
|
+
|
65
67
|
def to_s
|
66
68
|
"s:#{seconds}m:#{minutes}h:#{hours}dom:#{days_of_month}m:#{months}dow:#{days_of_week}y:#{years}"
|
67
69
|
end
|
68
|
-
|
70
|
+
|
69
71
|
def will_fire_on?(date)
|
70
|
-
|
71
|
-
[@seconds, @minutes, @hours, @days_of_month, @months, @days_of_week, @years].detect{|part| !part.include?(date)}.nil?
|
72
|
+
[seconds, minutes, hours, days_of_month, months, days_of_week, years].detect { |part| !part.include?(date) }.nil?
|
72
73
|
end
|
73
74
|
|
74
75
|
def next_fire_at(time=nil)
|
75
|
-
Time.zone =
|
76
|
+
Time.zone = time_zone
|
76
77
|
after = time.nil? ? Time.zone.now : time.in_time_zone(@time_zone)
|
77
|
-
# after = 1.second.since(after)
|
78
|
-
# puts "next fire at after: #{after.inspect}"
|
79
78
|
|
80
79
|
while (true)
|
81
80
|
[years, months, days_of_month, days_of_week, hours, minutes, seconds].each do |cron_value|
|
82
|
-
# puts "next_fire_at cron val loop: #{cron_value.part}"
|
83
|
-
# puts "before move_to_next: #{after.inspect}"
|
84
81
|
after, changed = move_to_next(cron_value, after)
|
85
|
-
# puts "after move_to_next: #{after.inspect}"
|
86
82
|
return if after.nil?
|
87
83
|
break if changed
|
88
84
|
end
|
89
|
-
|
85
|
+
|
90
86
|
break if will_fire_on?(after)
|
91
87
|
end
|
92
|
-
|
93
|
-
return after
|
88
|
+
after
|
94
89
|
end
|
95
90
|
|
96
91
|
def last_fire_at(time=nil)
|
97
|
-
Time.zone =
|
92
|
+
Time.zone = time_zone
|
98
93
|
before = time.nil? ? Time.zone.now : time.in_time_zone(@time_zone)
|
99
|
-
# before = 1.second.ago(before)
|
100
|
-
# puts "last fire at before: #{before.inspect}"
|
101
94
|
|
102
95
|
while (true)
|
103
96
|
[years, months, days_of_month, days_of_week, hours, minutes, seconds].each do |cron_value|
|
104
|
-
# puts "last_fire_at cron val loop: #{cron_value.part} for #{before.inspect}"
|
105
|
-
# puts "before move_to_last: #{before.to_s}"
|
106
97
|
before, changed = move_to_last(cron_value, before)
|
107
|
-
# puts "after move_to_last: #{before.to_s}"
|
108
98
|
return if before.nil?
|
109
99
|
break if changed
|
110
100
|
end
|
111
|
-
|
101
|
+
|
112
102
|
break if will_fire_on?(before)
|
113
103
|
end
|
114
|
-
|
115
|
-
return before
|
104
|
+
before
|
116
105
|
end
|
117
106
|
|
118
107
|
protected
|
@@ -133,12 +122,12 @@ module SayWhen
|
|
133
122
|
[before, false]
|
134
123
|
end
|
135
124
|
|
136
|
-
|
125
|
+
|
137
126
|
end
|
138
127
|
|
139
128
|
class CronValue
|
140
|
-
attr_accessor :part, :min, :max, :expression, :values
|
141
|
-
|
129
|
+
attr_accessor :part, :min, :max, :expression, :values
|
130
|
+
|
142
131
|
def initialize(p, min, max, exp)
|
143
132
|
self.part = p
|
144
133
|
self.min = min
|
@@ -147,29 +136,29 @@ module SayWhen
|
|
147
136
|
self.expression = exp
|
148
137
|
parse(exp)
|
149
138
|
end
|
150
|
-
|
139
|
+
|
151
140
|
def parse(exp)
|
152
|
-
self.values =
|
141
|
+
self.values = CronValue.parse_number(min, max, exp.upcase)
|
153
142
|
end
|
154
143
|
|
155
144
|
def to_s
|
156
|
-
"[e:#{
|
145
|
+
"[e:#{expression}, v:#{values.inspect}]\n"
|
157
146
|
end
|
158
|
-
|
147
|
+
|
159
148
|
def include?(date)
|
160
|
-
|
149
|
+
values.include?(date.send(part))
|
161
150
|
end
|
162
|
-
|
151
|
+
|
163
152
|
#works for secs, mins, hours
|
164
153
|
def self.parse_number(min, max, val)
|
165
154
|
values = []
|
166
155
|
case val
|
167
156
|
#check for a '/' for increments
|
168
|
-
when /(\w+)\/(\d+)/ then (( $1 == "*") ? min : $1.to_i).step(max, $2.to_i) {|x| values << x}
|
169
|
-
|
157
|
+
when /(\w+)\/(\d+)/ then (( $1 == "*") ? min : $1.to_i).step(max, $2.to_i) { |x| values << x }
|
158
|
+
|
170
159
|
#check for ',' for list of values
|
171
|
-
when /(\d+)(,\d+)+/ then values = val.split(',').
|
172
|
-
|
160
|
+
when /(\d+)(,\d+)+/ then values = val.split(',').map{ |v| v.to_i }.sort
|
161
|
+
|
173
162
|
#check for '-' for range of values
|
174
163
|
when /(\d+)-(\d+)/ then values = (($1.to_i)..($2.to_i)).to_a
|
175
164
|
|
@@ -192,26 +181,22 @@ module SayWhen
|
|
192
181
|
end
|
193
182
|
|
194
183
|
def next(date)
|
195
|
-
|
196
|
-
n = self.values.detect{|v| v > date.sec}
|
184
|
+
n = values.detect { |v| v > date.sec }
|
197
185
|
if n.blank?
|
198
|
-
1.minute.since(date).change(:
|
186
|
+
1.minute.since(date).change(sec: values.first)
|
199
187
|
else
|
200
|
-
date.change(:
|
188
|
+
date.change(sec: n)
|
201
189
|
end
|
202
190
|
end
|
203
|
-
|
191
|
+
|
204
192
|
def last(date)
|
205
|
-
|
206
|
-
n = self.values.reverse.detect{|v| v < date.sec}
|
193
|
+
n = values.reverse.detect { |v| v < date.sec }
|
207
194
|
if n.blank?
|
208
|
-
1.minute.ago(date).change(:
|
195
|
+
1.minute.ago(date).change(sec: values.last)
|
209
196
|
else
|
210
|
-
date.change(:
|
197
|
+
date.change(sec: n)
|
211
198
|
end
|
212
199
|
end
|
213
|
-
|
214
|
-
|
215
200
|
end
|
216
201
|
|
217
202
|
class MinutesCronValue < CronValue
|
@@ -220,22 +205,20 @@ module SayWhen
|
|
220
205
|
end
|
221
206
|
|
222
207
|
def next(date)
|
223
|
-
|
224
|
-
n = self.values.detect{|v| v > date.min}
|
208
|
+
n = values.detect { |v| v > date.min }
|
225
209
|
if n.blank?
|
226
|
-
1.hour.since(date).change(:
|
210
|
+
1.hour.since(date).change(min: values.first, sec: 0)
|
227
211
|
else
|
228
|
-
date.change(:
|
212
|
+
date.change(min: n, sec: 0)
|
229
213
|
end
|
230
214
|
end
|
231
215
|
|
232
216
|
def last(date)
|
233
|
-
|
234
|
-
n = self.values.reverse.detect{|v| v < date.min}
|
217
|
+
n = values.reverse.detect { |v| v < date.min }
|
235
218
|
if n.blank?
|
236
|
-
1.hour.ago(date).change(:
|
219
|
+
1.hour.ago(date).change(min: values.last, sec: 59)
|
237
220
|
else
|
238
|
-
date.change(:
|
221
|
+
date.change(min: n, sec: 59)
|
239
222
|
end
|
240
223
|
end
|
241
224
|
end
|
@@ -244,28 +227,24 @@ module SayWhen
|
|
244
227
|
def initialize(exp)
|
245
228
|
super(:hour, 0, 24, exp)
|
246
229
|
end
|
247
|
-
|
230
|
+
|
248
231
|
def next(date)
|
249
|
-
|
250
|
-
# puts "HoursCronValue next: date: #{date.inspect}, hour: #{date.hour}"
|
251
|
-
n = self.values.detect{|v| v > date.hour}
|
232
|
+
n = values.detect { |v| v > date.hour }
|
252
233
|
if n.blank?
|
253
|
-
1.day.since(date).change(:
|
234
|
+
1.day.since(date).change(hour: values.first, min: 0, sec: 0)
|
254
235
|
else
|
255
|
-
date.change(:
|
236
|
+
date.change(hour: n, min: 0, sec: 0)
|
256
237
|
end
|
257
238
|
end
|
258
239
|
|
259
240
|
def last(date)
|
260
|
-
|
261
|
-
n = self.values.reverse.detect{|v| v < date.hour}
|
241
|
+
n = values.reverse.detect { |v| v < date.hour }
|
262
242
|
if n.blank?
|
263
|
-
1.day.ago(date).change(:
|
243
|
+
1.day.ago(date).change(hour: values.last, min: 59, sec: 59)
|
264
244
|
else
|
265
|
-
date.change(:
|
245
|
+
date.change(hour: n, min: 59, sec: 59)
|
266
246
|
end
|
267
247
|
end
|
268
|
-
|
269
248
|
end
|
270
249
|
|
271
250
|
class DaysOfMonthCronValue < CronValue
|
@@ -275,14 +254,14 @@ module SayWhen
|
|
275
254
|
self.is_weekday = false
|
276
255
|
super(:mday, 1, 31, exp)
|
277
256
|
end
|
278
|
-
|
257
|
+
|
279
258
|
def parse(exp)
|
280
|
-
if self.is_specified = !(
|
259
|
+
if self.is_specified = !(expression =~ /\?/)
|
281
260
|
case exp
|
282
261
|
when /^(L)$/ then self.is_last = true
|
283
262
|
when /^(W)$/ then self.is_weekday = true
|
284
263
|
when /^(WL|LW)$/ then self.is_last = (self.is_weekday = true)
|
285
|
-
when /^(\d+)W$/ then self.is_weekday = true; self.values << $1.to_i
|
264
|
+
when /^(\d+)W$/ then self.is_weekday = true; self.values << $1.to_i
|
286
265
|
else super(exp)
|
287
266
|
end
|
288
267
|
end
|
@@ -296,7 +275,7 @@ module SayWhen
|
|
296
275
|
eom = nearest_week_day(eom) if is_weekday
|
297
276
|
if eom > date
|
298
277
|
eom = 1.month.ago(date)
|
299
|
-
eom = nearest_week_day(eom.change(:
|
278
|
+
eom = nearest_week_day(eom.change(day: eom.end_of_month))
|
300
279
|
end
|
301
280
|
eom
|
302
281
|
elsif is_weekday
|
@@ -304,72 +283,58 @@ module SayWhen
|
|
304
283
|
nearest = nearest_week_day(date)
|
305
284
|
if nearest > date
|
306
285
|
nearest = 1.month.ago(date)
|
307
|
-
nearest = nearest_week_day(date.change(:
|
286
|
+
nearest = nearest_week_day(date.change(day: date.end_of_month))
|
308
287
|
end
|
309
288
|
else
|
310
|
-
nearest = nearest_week_day(date.change(:
|
311
|
-
nearest = nearest_week_day(1.month.ago(date).change(:
|
289
|
+
nearest = nearest_week_day(date.change(day: values.first))
|
290
|
+
nearest = nearest_week_day(1.month.ago(date).change(day: values.first)) if nearest > date
|
312
291
|
end
|
313
292
|
nearest
|
314
293
|
else
|
315
|
-
|
316
|
-
l = self.values.reverse.detect{|v| v < date.mday}
|
317
|
-
# puts "last should be #{n}"
|
294
|
+
l = values.reverse.detect { |v| v < date.mday }
|
318
295
|
if l.blank?
|
319
|
-
1.month.ago(date).change(:
|
296
|
+
1.month.ago(date).change(day: values.last)
|
320
297
|
else
|
321
|
-
|
322
|
-
date.change(:day=>l.to_i)
|
298
|
+
date.change(day: l.to_i)
|
323
299
|
end
|
324
300
|
end
|
325
|
-
result
|
326
|
-
# puts "result after change #{result}"
|
327
|
-
result
|
301
|
+
result.change(hour: 23, min: 59, sec: 59)
|
328
302
|
end
|
329
|
-
|
303
|
+
|
330
304
|
def next(date)
|
331
305
|
result = if !is_specified
|
332
306
|
date
|
333
307
|
elsif is_last
|
334
308
|
last = date.end_of_month
|
335
309
|
last = nearest_week_day(last) if is_weekday
|
336
|
-
# last = nearest_week_day(1.month.since(date).change(:day=>1)) if last < date
|
337
310
|
if last < date
|
338
311
|
last = 1.month.since(date)
|
339
|
-
last = nearest_week_day(last.change(:
|
312
|
+
last = nearest_week_day(last.change(day: last.end_of_month))
|
340
313
|
end
|
341
314
|
last
|
342
315
|
elsif is_weekday
|
343
316
|
if values.empty?
|
344
317
|
nearest = nearest_week_day(date)
|
345
|
-
nearest = nearest_week_day(1.month.since(date).change(:
|
318
|
+
nearest = nearest_week_day(1.month.since(date).change(day: 1)) if nearest < date
|
346
319
|
else
|
347
|
-
nearest = nearest_week_day(date.change(:
|
348
|
-
nearest = nearest_week_day(1.month.since(date).change(:
|
320
|
+
nearest = nearest_week_day(date.change(day: values.first))
|
321
|
+
nearest = nearest_week_day(1.month.since(date).change(day: values.first)) if nearest < date
|
349
322
|
end
|
350
323
|
nearest
|
351
324
|
else
|
352
|
-
|
353
|
-
n = self.values.detect{|v| v > date.mday}
|
354
|
-
# puts "next should be #{n}"
|
325
|
+
n = values.detect { |v| v > date.mday }
|
355
326
|
if n.blank?
|
356
|
-
date.months_since(1).change(:
|
327
|
+
date.months_since(1).change(day: values.first)
|
357
328
|
else
|
358
|
-
|
359
|
-
ndate = date.change(:day=>n.to_i)
|
360
|
-
# puts "after change date #{ndate}, #{ndate.class.name}"
|
361
|
-
ndate
|
329
|
+
date.change(day: n.to_i)
|
362
330
|
end
|
363
331
|
end
|
364
|
-
result
|
365
|
-
# puts "result after change #{result}"
|
366
|
-
result
|
332
|
+
result.change(hour: 0)
|
367
333
|
end
|
368
|
-
|
334
|
+
|
369
335
|
def include?(date)
|
370
336
|
return true unless is_specified
|
371
337
|
last = date.clone
|
372
|
-
#must be last weekday of the month
|
373
338
|
if is_last
|
374
339
|
last = last.end_of_month.to_date
|
375
340
|
last = nearest_week_day(last) if is_weekday
|
@@ -378,13 +343,13 @@ module SayWhen
|
|
378
343
|
if values.empty?
|
379
344
|
(1..5).include?(date.wday)
|
380
345
|
else
|
381
|
-
nearest_week_day(date.change(:
|
346
|
+
nearest_week_day(date.change(day: values.first)) == date
|
382
347
|
end
|
383
348
|
else
|
384
349
|
super(date)
|
385
350
|
end
|
386
351
|
end
|
387
|
-
|
352
|
+
|
388
353
|
def nearest_week_day(date)
|
389
354
|
if (1..5).include?(date.wday)
|
390
355
|
date
|
@@ -394,52 +359,48 @@ module SayWhen
|
|
394
359
|
(date.end_of_month.to_date == date.to_date) ? date = 2.days.ago(date) : 1.day.since(date)
|
395
360
|
end
|
396
361
|
end
|
397
|
-
|
362
|
+
|
398
363
|
def to_s
|
399
|
-
"[e:#{
|
400
|
-
end
|
364
|
+
"[e:#{expression}, v:#{values.inspect}, is:#{is_specified}, il:#{is_last}, iw:#{is_weekday}]\n"
|
365
|
+
end
|
401
366
|
end
|
402
367
|
|
403
368
|
class MonthsCronValue < CronValue
|
404
|
-
MONTHS = Date::ABBR_MONTHNAMES[1..-1].
|
405
|
-
|
369
|
+
MONTHS = Date::ABBR_MONTHNAMES[1..-1].map { |a| a.upcase }
|
370
|
+
|
406
371
|
def initialize(exp)
|
407
372
|
super(:month, 1, 12, exp)
|
408
373
|
end
|
409
|
-
|
410
|
-
def parse(exp)
|
374
|
+
|
375
|
+
def parse(exp)
|
411
376
|
if exp =~ /[A-Z]+/
|
412
|
-
MONTHS.each_with_index{|mon, index|
|
413
|
-
exp = exp.gsub(mon, (index+1).to_s)
|
414
|
-
}
|
377
|
+
MONTHS.each_with_index { |mon, index| exp = exp.gsub(mon, (index + 1).to_s) }
|
415
378
|
end
|
416
379
|
super(exp)
|
417
380
|
end
|
418
381
|
|
419
382
|
def last(date)
|
420
|
-
last_month =
|
383
|
+
last_month = values.reverse.detect { |v| v < date.month }
|
421
384
|
result = if last_month.nil?
|
422
|
-
date.change(:
|
385
|
+
date.change(year: date.year - 1, month: values.last)
|
423
386
|
else
|
424
|
-
date.change(:
|
387
|
+
date.change(month: last_month)
|
425
388
|
end
|
426
|
-
result.change(:
|
389
|
+
result.change(day: result.end_of_month, hour: 23, min: 59, sec: 59)
|
427
390
|
end
|
428
391
|
|
429
392
|
def next(date)
|
430
|
-
next_month =
|
431
|
-
|
432
|
-
date.change(:
|
393
|
+
next_month = values.detect { |v| v > date.month }
|
394
|
+
if next_month.nil?
|
395
|
+
date.change(year: date.year + 1, month: values.first, day: 1, hour: 0)
|
433
396
|
else
|
434
|
-
date.change(:
|
397
|
+
date.change(month: next_month, day: 1, hour: 0)
|
435
398
|
end
|
436
|
-
result
|
437
399
|
end
|
438
|
-
|
439
400
|
end
|
440
401
|
|
441
402
|
class DaysOfWeekCronValue < CronValue
|
442
|
-
DAYS = Date::ABBR_DAYNAMES.
|
403
|
+
DAYS = Date::ABBR_DAYNAMES.map { |a| a.upcase }
|
443
404
|
attr_accessor :is_specified, :is_last, :nth_day
|
444
405
|
|
445
406
|
def initialize(exp)
|
@@ -448,14 +409,12 @@ module SayWhen
|
|
448
409
|
end
|
449
410
|
|
450
411
|
def parse(exp)
|
451
|
-
if self.is_specified = !(
|
412
|
+
if self.is_specified = !(expression =~ /\?/)
|
452
413
|
if exp =~ /[A-Z]+/
|
453
|
-
DAYS.each_with_index{|day, index|
|
454
|
-
exp = exp.gsub(day, (index+1).to_s)
|
455
|
-
}
|
414
|
+
DAYS.each_with_index { |day, index| exp = exp.gsub(day, (index + 1).to_s) }
|
456
415
|
end
|
457
416
|
case exp
|
458
|
-
when /^L$/ then values <<
|
417
|
+
when /^L$/ then values << max
|
459
418
|
when /^(\d+)L$/ then self.is_last = true; values << $1.to_i
|
460
419
|
when /^(\d+)#(\d+)/ then self.values << $1.to_i; self.nth_day = $2.to_i
|
461
420
|
else super(exp)
|
@@ -464,48 +423,41 @@ module SayWhen
|
|
464
423
|
end
|
465
424
|
|
466
425
|
def include?(date)
|
467
|
-
# puts "DaysOfWeekCronValue::include? is_specified:#{is_specified}, date:#{date}"
|
468
426
|
return true unless is_specified
|
469
427
|
if is_last
|
470
428
|
last = last_wday(date, values.first).to_date
|
471
|
-
# puts "checking is_last: date=#{date} == last #{last}"
|
472
429
|
date.to_date == last
|
473
430
|
elsif nth_day
|
474
|
-
date.to_date == nth_wday(
|
431
|
+
date.to_date == nth_wday(nth_day, values.first, date.month, date.year).to_date
|
475
432
|
else
|
476
|
-
|
433
|
+
values.include?(date.wday + 1)
|
477
434
|
end
|
478
435
|
end
|
479
|
-
|
436
|
+
|
480
437
|
def last(date)
|
481
|
-
# puts "DaysOfWeekCronValue::last date:#{date}, is_last:#{is_last}"
|
482
438
|
last_dow = if !is_specified
|
483
439
|
date
|
484
440
|
elsif is_last
|
485
441
|
last = last_wday(date, values.first)
|
486
|
-
# puts "DaysOfWeekCronValue::last after first last_wday: #{date}"
|
487
442
|
if last.to_date > date.to_date
|
488
|
-
last = last_wday(1.month.ago(date).change(:
|
443
|
+
last = last_wday(1.month.ago(date).change(day: 1), values.first)
|
489
444
|
end
|
490
445
|
last
|
491
446
|
elsif nth_day
|
492
|
-
nth = nth_wday(
|
493
|
-
# puts "DaysOfWeekCronValue::last after first nth_wday: #{nth}"
|
447
|
+
nth = nth_wday(nth_day, values.first, date.month, date.year)
|
494
448
|
if nth.to_date > date.to_date
|
495
|
-
nth = 1.month.ago(date).change(:
|
496
|
-
nth = nth_wday(
|
449
|
+
nth = 1.month.ago(date).change(day: 1)
|
450
|
+
nth = nth_wday(nth_day, values.first, nth.month, nth.year)
|
497
451
|
end
|
498
452
|
nth
|
499
453
|
else
|
500
|
-
n =
|
501
|
-
n =
|
454
|
+
n = values.detect { |v| v > date.wday }
|
455
|
+
n = values[0] if n.blank?
|
502
456
|
base = (n < (date.wday + 1)) ? 7 : 0
|
503
457
|
days_forward = n + (base - (date.wday + 1))
|
504
458
|
days_forward.days.since(date)
|
505
459
|
end
|
506
|
-
last_dow
|
507
|
-
# puts "DaysOfWeekCronValue: #{last_dow.class.name}: #{last_dow.to_s}"
|
508
|
-
last_dow
|
460
|
+
last_dow.change(hour: 23, min: 59, sec: 59)
|
509
461
|
end
|
510
462
|
|
511
463
|
def next(date)
|
@@ -513,36 +465,28 @@ module SayWhen
|
|
513
465
|
date
|
514
466
|
elsif is_last
|
515
467
|
last = last_wday(date, values.first)
|
516
|
-
# puts "1 last_wday = #{last}"
|
517
468
|
if last.to_date <= date.to_date
|
518
|
-
last = last_wday(1.month.since(date).change(:
|
519
|
-
# puts "2 last_wday = #{last}"
|
469
|
+
last = last_wday(1.month.since(date).change(day: 1), values.first)
|
520
470
|
end
|
521
471
|
last
|
522
472
|
elsif nth_day
|
523
|
-
nth = nth_wday(
|
473
|
+
nth = nth_wday(nth_day, values.first, date.month, date.year)
|
524
474
|
if nth.to_date <= date.to_date
|
525
475
|
date = 1.month.since(date)
|
526
|
-
nth = nth_wday(
|
476
|
+
nth = nth_wday(nth_day, values.first, date.month, date.year)
|
527
477
|
end
|
528
478
|
nth
|
529
479
|
else
|
530
|
-
n =
|
531
|
-
n =
|
480
|
+
n = values.detect { |v| v > date.wday }
|
481
|
+
n = values[0] if n.blank?
|
532
482
|
base = (n < (date.wday + 1)) ? 7 : 0
|
533
483
|
days_forward = n + (base - (date.wday + 1))
|
534
484
|
days_forward.days.since(date)
|
535
485
|
end
|
536
|
-
next_dow.change(:
|
486
|
+
next_dow.change(hour: 0)
|
537
487
|
end
|
538
488
|
|
539
|
-
# 1 last_wday = 2008-05-30
|
540
|
-
# latest_wday date=Sun Jun 01 10:00:00 -0400 2008, wday=5
|
541
|
-
# 2 last_wday = 2008-06-28 '
|
542
|
-
# **** should be 06-27
|
543
489
|
def last_wday(date, aWday)
|
544
|
-
# puts "last_wday date=#{date.to_time}, wday=#{wday}"
|
545
|
-
# sleep(1)
|
546
490
|
wday = aWday - 1
|
547
491
|
eom = date.end_of_month
|
548
492
|
if eom.wday == wday
|
@@ -563,7 +507,6 @@ module SayWhen
|
|
563
507
|
raise ArgumentError
|
564
508
|
end
|
565
509
|
t = Time.zone.local year, month, 1
|
566
|
-
# puts "t = #{t}"
|
567
510
|
first = t.wday
|
568
511
|
if first == wday
|
569
512
|
fwd = 1
|
@@ -575,7 +518,6 @@ module SayWhen
|
|
575
518
|
target = fwd + (n-1)*7
|
576
519
|
begin
|
577
520
|
t2 = Time.zone.local year, month, target
|
578
|
-
# puts "t2 = #{t2}"
|
579
521
|
rescue ArgumentError
|
580
522
|
return nil
|
581
523
|
end
|
@@ -586,11 +528,9 @@ module SayWhen
|
|
586
528
|
end
|
587
529
|
end
|
588
530
|
|
589
|
-
|
590
531
|
def to_s
|
591
|
-
"[e:#{
|
532
|
+
"[e:#{expression}, v:#{values.inspect}, is:#{is_specified}, il:#{is_last}, nd:#{nth_day}]\n"
|
592
533
|
end
|
593
|
-
|
594
534
|
end
|
595
535
|
|
596
536
|
class YearsCronValue < CronValue
|
@@ -599,23 +539,21 @@ module SayWhen
|
|
599
539
|
end
|
600
540
|
|
601
541
|
def next(date)
|
602
|
-
next_year =
|
603
|
-
if next_year.nil?
|
542
|
+
next_year = values.detect { |v| v > date.year }
|
543
|
+
if next_year.nil?
|
604
544
|
return nil
|
605
545
|
else
|
606
|
-
date.change(:
|
546
|
+
date.change(year: next_year, month: 1, day: 1, hour: 0)
|
607
547
|
end
|
608
548
|
end
|
609
549
|
|
610
550
|
def last(date)
|
611
|
-
last_year =
|
612
|
-
if last_year.nil?
|
551
|
+
last_year = values.reverse.detect { |v| v < date.year }
|
552
|
+
if last_year.nil?
|
613
553
|
return nil
|
614
554
|
else
|
615
|
-
date.change(:
|
555
|
+
date.change(year: last_year, month: 12, day: 31, hour: 23, min: 59, sec: 59)
|
616
556
|
end
|
617
557
|
end
|
618
|
-
|
619
558
|
end
|
620
|
-
|
621
559
|
end
|