fugit 1.3.4 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fugit might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +83 -0
- data/CREDITS.md +16 -2
- data/LICENSE.txt +1 -1
- data/README.md +66 -6
- data/fugit.gemspec +3 -3
- data/lib/fugit/at.rb +1 -0
- data/lib/fugit/cron.rb +79 -28
- data/lib/fugit/duration.rb +7 -7
- data/lib/fugit/misc.rb +4 -0
- data/lib/fugit/nat.rb +590 -174
- data/lib/fugit/parse.rb +1 -0
- data/lib/fugit.rb +3 -1
- metadata +15 -15
data/lib/fugit/nat.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -15,16 +16,13 @@ module Fugit
|
|
15
16
|
return nil unless s.is_a?(String)
|
16
17
|
|
17
18
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colours: true)
|
18
|
-
|
19
|
+
#(p s; Raabro.pp(Parser.parse(s, debug: 1), colours: true)) rescue nil
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
if a.include?([ :flag, 'from' ]) && a.find { |e| e[0] == :day_range }
|
26
|
-
|
27
|
-
nil
|
21
|
+
if slots = Parser.parse(s)
|
22
|
+
slots.to_crons(opts.merge(_s: s))
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
28
26
|
end
|
29
27
|
|
30
28
|
def do_parse(s, opts={})
|
@@ -32,239 +30,657 @@ module Fugit
|
|
32
30
|
parse(s, opts) ||
|
33
31
|
fail(ArgumentError.new("could not parse a nat #{s.inspect}"))
|
34
32
|
end
|
33
|
+
end
|
35
34
|
|
36
|
-
|
35
|
+
module Parser include Raabro
|
37
36
|
|
38
|
-
|
37
|
+
one_to_nine =
|
38
|
+
%w[ one two three four five six seven eight nine ]
|
39
|
+
sixties =
|
40
|
+
%w[ zero ] + one_to_nine +
|
41
|
+
%w[ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
|
42
|
+
eighteen nineteen ] +
|
43
|
+
%w[ twenty thirty fourty fifty ]
|
44
|
+
.collect { |a|
|
45
|
+
([ nil ] + one_to_nine)
|
46
|
+
.collect { |b| [ a, b ].compact.join('-') } }
|
47
|
+
.flatten
|
48
|
+
|
49
|
+
NHOURS = sixties[0, 13]
|
50
|
+
.each_with_index
|
51
|
+
.inject({}) { |h, (n, i)| h[n] = i; h }
|
52
|
+
.merge!(
|
53
|
+
'midnight' => 0, 'oh' => 0, 'noon' => 12)
|
54
|
+
.freeze
|
55
|
+
NMINUTES = sixties
|
56
|
+
.each_with_index
|
57
|
+
.inject({}) { |h, (n, i)| h[n] = i; h }
|
58
|
+
.merge!(
|
59
|
+
"o'clock" => 0, 'hundred' => 0)
|
60
|
+
.freeze
|
61
|
+
|
62
|
+
WEEKDAYS = (
|
63
|
+
Fugit::Cron::Parser::WEEKDAYS +
|
64
|
+
Fugit::Cron::Parser::WEEKDS).freeze
|
65
|
+
|
66
|
+
POINTS = %w[
|
67
|
+
minutes? mins? seconds? secs? hours? hou h ].freeze
|
68
|
+
|
69
|
+
INTERVALS = %w[
|
70
|
+
seconds? minutes? hours? days? months?
|
71
|
+
sec min
|
72
|
+
s m h d M ].freeze
|
73
|
+
|
74
|
+
oh = {
|
75
|
+
'1st' => 1, '2nd' => 2, '3rd' => 3, '21st' => 21, '22nd' => 22,
|
76
|
+
'23rd' => 23, '31st' => 31,
|
77
|
+
'last' => 'L' }
|
78
|
+
(4..30)
|
79
|
+
.each { |i| oh["#{i}th"] = i.to_i }
|
80
|
+
%w[
|
81
|
+
first second third fourth fifth sixth seventh eighth ninth tenth
|
82
|
+
eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth
|
83
|
+
eighteenth nineteenth twentieth twenty-first twenty-second twenty-third
|
84
|
+
twenty-fourth twenty-fifth twenty-sixth twenty-seventh twenty-eighth
|
85
|
+
twenty-ninth thirtieth thirty-first ]
|
86
|
+
.each_with_index { |e, i| oh[e] = i + 1 }
|
87
|
+
OMONTHDAYS = oh.freeze
|
88
|
+
|
89
|
+
OMONTHDAY_REX = /#{OMONTHDAYS.keys.join('|')}/i.freeze
|
90
|
+
MONTHDAY_REX = /3[0-1]|[0-2]?[0-9]/.freeze
|
91
|
+
WEEKDAY_REX = /(#{WEEKDAYS.join('|')})(?=($|[-, \t]))/i.freeze
|
92
|
+
# prevent "mon" from eating "monday"
|
93
|
+
NAMED_M_REX = /#{NMINUTES.keys.join('|')}/i.freeze
|
94
|
+
NAMED_H_REX = /#{NHOURS.keys.join('|')}/i.freeze
|
95
|
+
POINT_REX = /(#{POINTS.join('|')})[ \t]+/i.freeze
|
96
|
+
INTERVAL_REX = /[ \t]*(#{INTERVALS.join('|')})/.freeze
|
97
|
+
|
98
|
+
#
|
99
|
+
# parsers bottom to top #################################################
|
100
|
+
|
101
|
+
def _every(i); rex(nil, i, /[ \t]*every[ \t]+/i); end
|
102
|
+
def _from(i); rex(nil, i, /[ \t]*from[ \t]+/i); end
|
103
|
+
def _at(i); rex(nil, i, /[ \t]*at[ \t]+/i); end
|
104
|
+
def _on(i); rex(nil, i, /[ \t]*on[ \t]+/i); end
|
105
|
+
def _to(i); rex(nil, i, /[ \t]*to[ \t]+/i); end
|
106
|
+
|
107
|
+
def _and(i); rex(nil, i, /[ \t]*and[ \t]+/i); end
|
108
|
+
def _and_or_or(i); rex(nil, i, /[ \t]*(and|or)[ \t]+/i); end
|
109
|
+
def _in_or_on(i); rex(nil, i, /(in|on)[ \t]+/i); end
|
110
|
+
|
111
|
+
def _and_or_or_or_comma(i)
|
112
|
+
rex(nil, i, /[ \t]*(,[ \t]*)?((and|or)[ \t]+|,[ \t]*)/i); end
|
113
|
+
|
114
|
+
def _to_or_dash(i);
|
115
|
+
rex(nil, i, /[ \t]*-[ \t]*|[ \t]+(to|through)[ \t]+/i); end
|
116
|
+
|
117
|
+
def _day_s(i); rex(nil, i, /[ \t]*days?[ \t]+/i); end
|
118
|
+
def _the(i); rex(nil, i, /[ \t]*the[ \t]+/i); end
|
119
|
+
|
120
|
+
def _space(i); rex(nil, i, /[ \t]+/); end
|
121
|
+
def _sep(i); rex(nil, i, /([ \t]+|[ \t]*,[ \t]*)/); end
|
122
|
+
|
123
|
+
def count(i); rex(:count, i, /\d+/); end
|
124
|
+
|
125
|
+
def omonthday(i)
|
126
|
+
rex(:omonthday, i, OMONTHDAY_REX)
|
127
|
+
end
|
128
|
+
def monthday(i)
|
129
|
+
rex(:monthday, i, MONTHDAY_REX)
|
130
|
+
end
|
131
|
+
def weekday(i)
|
132
|
+
rex(:weekday, i, WEEKDAY_REX)
|
133
|
+
end
|
39
134
|
|
40
|
-
|
41
|
-
|
42
|
-
ms = dhs
|
43
|
-
.inject({}) { |h, dh| (h[dh[1][0]] ||= []) << dh[1][1]; h }
|
44
|
-
.values
|
45
|
-
.uniq
|
135
|
+
def omonthdays(i); jseq(nil, i, :omonthday, :_and_or_or_or_comma); end
|
136
|
+
def monthdays(i); jseq(nil, i, :monthday, :_and_or_or_or_comma); end
|
46
137
|
|
47
|
-
|
48
|
-
#if ms.size <= 1 || hs.size <= 1
|
49
|
-
if ms.size <= 1
|
50
|
-
[ parse_cron(a, opts) ]
|
51
|
-
else
|
52
|
-
dhs.collect { |dh| parse_cron([ dh ] + aa, opts) }
|
53
|
-
end
|
138
|
+
def weekdays(i); jseq(:weekdays, i, :weekday, :_and_or_or_or_comma); end
|
54
139
|
|
55
|
-
|
56
|
-
"multiple crons in #{s.inspect} " +
|
57
|
-
"(#{crons.collect(&:original).join(' | ')})"
|
58
|
-
) if opts[:multi] == :fail && crons.size > 1
|
140
|
+
def on_the(i); seq(nil, i, :_the, :omonthdays); end
|
59
141
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
142
|
+
def _minute(i); rex(nil, i, /[ \t]*minute[ \t]+/i) end
|
143
|
+
|
144
|
+
def _dmin(i)
|
145
|
+
rex(:dmin, i, /[0-5]?[0-9]/)
|
146
|
+
end
|
147
|
+
def and_dmin(i)
|
148
|
+
seq(nil, i, :_and_or_or_or_comma, :_minute, '?', :_dmin)
|
65
149
|
end
|
66
150
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
hkeys = h.keys
|
71
|
-
|
72
|
-
a.each do |key, val|
|
73
|
-
if key == :biz_day
|
74
|
-
(h[:dow] ||= []) << '1-5'
|
75
|
-
elsif key == :simple_hour || key == :numeral_hour
|
76
|
-
h[:hou] << val
|
77
|
-
elsif key == :digital_hour
|
78
|
-
(h[:hou] ||= []) << val[0].to_i
|
79
|
-
(h[:min] ||= []) << val[1].to_i
|
80
|
-
elsif key == :name_day
|
81
|
-
(h[:dow] ||= []) << val
|
82
|
-
elsif key == :day_range
|
83
|
-
(h[:dow] ||= []) << val.collect { |v| v.to_s[0, 3] }.join('-')
|
84
|
-
elsif key == :tz
|
85
|
-
h[:tz] = val
|
86
|
-
elsif key == :duration
|
87
|
-
process_duration(h, *val[0].to_h.first)
|
88
|
-
end
|
89
|
-
end
|
151
|
+
def on_minutes(i)
|
152
|
+
seq(:on_minutes, i, :_minute, :_dmin, :and_dmin, '*')
|
153
|
+
end
|
90
154
|
|
91
|
-
|
92
|
-
|
155
|
+
def on_thex(i);
|
156
|
+
rex(:on_thex, i, /[ \t]*the[ \t]+(hour|minute)[ \t]*/i);
|
157
|
+
end
|
93
158
|
|
94
|
-
|
95
|
-
|
159
|
+
def on_thes(i); jseq(:on_thes, i, :on_the, :_and_or_or_or_comma); end
|
160
|
+
def on_days(i); seq(:on_days, i, :_day_s, :monthdays); end
|
161
|
+
def on_weekdays(i); ren(:on_weekdays, i, :weekdays); end
|
96
162
|
|
97
|
-
|
163
|
+
def on_object(i)
|
164
|
+
alt(nil, i, :on_days, :on_weekdays, :on_minutes, :on_thes, :on_thex)
|
165
|
+
end
|
166
|
+
def on_objects(i)
|
167
|
+
jseq(nil, i, :on_object, :_and)
|
168
|
+
end
|
98
169
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
170
|
+
#'every month on day 2 at 10:00' => '0 10 2 * *',
|
171
|
+
#'every month on day 2 and 5 at 10:00' => '0 10 2,5 * *',
|
172
|
+
#'every month on days 1,15 at 10:00' => '0 10 1,15 * *',
|
173
|
+
#
|
174
|
+
#'every week on monday 18:23' => '23 18 * * 1',
|
175
|
+
#
|
176
|
+
# every month on the 1st
|
177
|
+
def on(i)
|
178
|
+
seq(:on, i, :_on, :on_objects)
|
179
|
+
end
|
105
180
|
|
106
|
-
|
181
|
+
def city_tz(i)
|
182
|
+
rex(nil, i, /[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}/)
|
183
|
+
end
|
184
|
+
def named_tz(i)
|
185
|
+
rex(nil, i, /Z|UTC/)
|
186
|
+
end
|
187
|
+
def delta_tz(i)
|
188
|
+
rex(nil, i, /[-+]([01][0-9]|2[0-4])(:?(00|15|30|45))?/)
|
189
|
+
end
|
190
|
+
def tz(i)
|
191
|
+
alt(:tz, i, :city_tz, :named_tz, :delta_tz)
|
192
|
+
end
|
193
|
+
def tzone(i)
|
194
|
+
seq(nil, i, :_in_or_on, '?', :tz)
|
195
|
+
end
|
107
196
|
|
108
|
-
|
197
|
+
def digital_hour(i)
|
198
|
+
rex(
|
199
|
+
:digital_hour, i,
|
200
|
+
/(2[0-4]|[0-1]?[0-9]):([0-5][0-9])([ \t]*(am|pm))?/i)
|
201
|
+
end
|
202
|
+
|
203
|
+
def ampm(i)
|
204
|
+
rex(:ampm, i, /[ \t]*(am|pm|noon|midday|midnight)/i)
|
205
|
+
end
|
206
|
+
def dark(i)
|
207
|
+
rex(:dark, i, /[ \t]*dark/i)
|
109
208
|
end
|
110
209
|
|
111
|
-
def
|
210
|
+
def simple_h(i)
|
211
|
+
rex(:simple_h, i, /#{(0..24).to_a.reverse.join('|')}/)
|
212
|
+
end
|
213
|
+
def simple_hour(i)
|
214
|
+
seq(:simple_hour, i, :simple_h, :ampm, '?')
|
215
|
+
end
|
216
|
+
|
217
|
+
def named_m(i)
|
218
|
+
rex(:named_m, i, NAMED_M_REX)
|
219
|
+
end
|
220
|
+
def named_min(i)
|
221
|
+
seq(nil, i, :_space, :named_m)
|
222
|
+
end
|
112
223
|
|
113
|
-
|
224
|
+
def named_h(i)
|
225
|
+
rex(:named_h, i, NAMED_H_REX)
|
226
|
+
end
|
227
|
+
def named_hour(i)
|
228
|
+
seq(:named_hour, i, :named_h, :dark, '?', :named_min, '?', :ampm, '?')
|
114
229
|
end
|
115
230
|
|
116
|
-
def
|
231
|
+
def _point(i); rex(:point, i, POINT_REX); end
|
117
232
|
|
118
|
-
|
119
|
-
|
120
|
-
h[:mon] = [ value == 1 ? '*' : "*/#{value}" ]
|
233
|
+
def counts(i)
|
234
|
+
jseq(nil, i, :count, :_and_or_or_or_comma)
|
121
235
|
end
|
122
236
|
|
123
|
-
def
|
237
|
+
def at_p(i)
|
238
|
+
seq(:at_p, i, :_point, :counts)
|
239
|
+
end
|
240
|
+
def at_point(i)
|
241
|
+
jseq(nil, i, :at_p, :_and_or_or)
|
242
|
+
end
|
124
243
|
|
125
|
-
|
126
|
-
|
244
|
+
# at five
|
245
|
+
# at five pm
|
246
|
+
# at five o'clock
|
247
|
+
# at 16:30
|
248
|
+
# at noon
|
249
|
+
# at 18:00 UTC <-- ...tz
|
250
|
+
def at_object(i)
|
251
|
+
alt(nil, i, :named_hour, :digital_hour, :simple_hour, :at_point)
|
252
|
+
end
|
253
|
+
def at_objects(i)
|
254
|
+
jseq(nil, i, :at_object, :_and_or_or_or_comma)
|
127
255
|
end
|
128
256
|
|
129
|
-
def
|
257
|
+
def at(i)
|
258
|
+
seq(:at, i, :_at, '?', :at_objects)
|
259
|
+
end
|
130
260
|
|
131
|
-
|
261
|
+
def interval(i)
|
262
|
+
rex(:interval, i, INTERVAL_REX)
|
132
263
|
end
|
133
264
|
|
134
|
-
|
265
|
+
# every day
|
266
|
+
# every 1 minute
|
267
|
+
def every_interval(i)
|
268
|
+
seq(:every_interval, i, :count, '?', :interval)
|
269
|
+
end
|
135
270
|
|
136
|
-
|
137
|
-
|
271
|
+
def every_single_interval(i)
|
272
|
+
rex(:every_single_interval, i, /(1[ \t]+)?(week|year)/)
|
138
273
|
end
|
139
274
|
|
140
|
-
def
|
275
|
+
def to_weekday(i)
|
276
|
+
seq(:to_weekday, i, :weekday, :_to_or_dash, :weekday)
|
277
|
+
end
|
141
278
|
|
142
|
-
|
143
|
-
|
144
|
-
h[:sec] = [ value == 1 ? '*' : "*/#{value}" ]
|
279
|
+
def weekday_range(i)
|
280
|
+
alt(nil, i, :to_weekday, :weekdays)
|
145
281
|
end
|
146
|
-
end
|
147
282
|
|
148
|
-
|
283
|
+
def to_omonthday(i)
|
284
|
+
seq(:to_omonthday, i,
|
285
|
+
:_the, '?', :omonthday, :_to, :_the, '?', :omonthday)
|
286
|
+
end
|
149
287
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
ten eleven twelve ]
|
288
|
+
def to_hour(i)
|
289
|
+
seq(:to_hour, i, :at_object, :_to, :at_object)
|
290
|
+
end
|
154
291
|
|
155
|
-
|
156
|
-
|
292
|
+
def from_object(i)
|
293
|
+
alt(nil, i, :to_weekday, :to_omonthday, :to_hour)
|
294
|
+
end
|
295
|
+
def from_objects(i)
|
296
|
+
jseq(nil, i, :from_object, :_and_or_or)
|
297
|
+
end
|
298
|
+
def from(i)
|
299
|
+
seq(nil, i, :_from, '?', :from_objects)
|
300
|
+
end
|
157
301
|
|
158
|
-
|
159
|
-
|
302
|
+
# every monday
|
303
|
+
# every Fri-Sun
|
304
|
+
# every Monday and Tuesday
|
305
|
+
def every_weekday(i)
|
306
|
+
jseq(nil, i, :weekday_range, :_and_or_or)
|
307
|
+
end
|
160
308
|
|
161
|
-
|
309
|
+
def otm(i)
|
310
|
+
rex(nil, i, /[ \t]+of the month/)
|
311
|
+
end
|
162
312
|
|
163
|
-
|
164
|
-
|
313
|
+
# every 1st of the month
|
314
|
+
# every first of the month
|
315
|
+
# Every 2nd of the month
|
316
|
+
# Every second of the month
|
317
|
+
# every 15th of the month
|
318
|
+
def every_of_the_month(i)
|
319
|
+
seq(nil, i, :omonthdays, :otm)
|
165
320
|
end
|
166
321
|
|
167
|
-
def
|
168
|
-
rex(:
|
322
|
+
def every_named(i)
|
323
|
+
rex(:every_named, i, /weekday/i)
|
169
324
|
end
|
170
325
|
|
171
|
-
def
|
172
|
-
|
326
|
+
def every_object(i)
|
327
|
+
alt(
|
328
|
+
nil, i,
|
329
|
+
:every_weekday, :every_of_the_month,
|
330
|
+
:every_interval, :every_named, :every_single_interval)
|
173
331
|
end
|
174
|
-
def
|
175
|
-
|
332
|
+
def every_objects(i)
|
333
|
+
jseq(nil, i, :every_object, :_and_or_or)
|
334
|
+
end
|
335
|
+
|
336
|
+
def every(i)
|
337
|
+
seq(:every, i, :_every, :every_objects)
|
176
338
|
end
|
177
339
|
|
178
|
-
def
|
179
|
-
|
340
|
+
def nat_elt(i)
|
341
|
+
alt(nil, i, :every, :from, :at, :tzone, :on)
|
180
342
|
end
|
181
|
-
def
|
182
|
-
|
343
|
+
def nat(i)
|
344
|
+
jseq(:nat, i, :nat_elt, :_sep)
|
183
345
|
end
|
184
346
|
|
185
|
-
|
186
|
-
|
347
|
+
#
|
348
|
+
# rewrite parsed tree ###################################################
|
349
|
+
|
350
|
+
def slot(key, data0, data1=nil, opts=nil)
|
351
|
+
Slot.new(key, data0, data1, opts)
|
187
352
|
end
|
188
353
|
|
189
|
-
def
|
190
|
-
|
191
|
-
|
354
|
+
def _rewrite_subs(t, key=nil)
|
355
|
+
t.subgather(key).collect { |ct| rewrite(ct) }
|
356
|
+
end
|
357
|
+
def _rewrite_sub(t, key=nil)
|
358
|
+
st = t.sublookup(key)
|
359
|
+
st ? rewrite(st) : nil
|
360
|
+
end
|
361
|
+
|
362
|
+
def rewrite_dmin(t)
|
363
|
+
t.strinp
|
364
|
+
end
|
192
365
|
|
193
|
-
def
|
366
|
+
def rewrite_on_minutes(t)
|
367
|
+
#Raabro.pp(t, colours: true)
|
368
|
+
mins = t.subgather(:dmin).collect(&:strinp)
|
369
|
+
#slot(:m, mins.join(','))
|
370
|
+
slot(:hm, '*', mins.join(','), strong: 1)
|
371
|
+
end
|
194
372
|
|
195
|
-
def
|
196
|
-
|
373
|
+
def rewrite_on_thex(t)
|
374
|
+
case s = t.string
|
375
|
+
#when /hour/i then slot(:h, 0)
|
376
|
+
#else slot(:m, '*')
|
377
|
+
when /hour/i then slot(:hm, 0, '*', strong: 0)
|
378
|
+
else slot(:hm, '*', '*', strong: 1)
|
379
|
+
end
|
197
380
|
end
|
198
381
|
|
199
|
-
def
|
200
|
-
|
382
|
+
def rewrite_on_thes(t)
|
383
|
+
_rewrite_subs(t, :omonthday)
|
201
384
|
end
|
202
|
-
def
|
203
|
-
|
385
|
+
def rewrite_on_days(t)
|
386
|
+
_rewrite_subs(t, :monthday)
|
204
387
|
end
|
205
|
-
def _tz(i); alt(:tz, i, :_tz_delta, :_tz_name); end
|
206
388
|
|
207
|
-
def
|
208
|
-
|
209
|
-
:duration, i,
|
210
|
-
/
|
211
|
-
\d+
|
212
|
-
\s?
|
213
|
-
(mon(ths?)?|d(ays?)?|h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?)
|
214
|
-
/ix)
|
389
|
+
def rewrite_on(t)
|
390
|
+
_rewrite_subs(t)
|
215
391
|
end
|
216
392
|
|
217
|
-
def
|
393
|
+
def rewrite_monthday(t)
|
394
|
+
slot(:monthday, t.string.to_i)
|
395
|
+
end
|
218
396
|
|
219
|
-
def
|
220
|
-
|
221
|
-
:day_range,
|
222
|
-
:plain_day, :biz_day, :name_day,
|
223
|
-
:_tz,
|
224
|
-
:flag,
|
225
|
-
:duration,
|
226
|
-
:name_hour, :numeral_hour, :digital_hour, :simple_hour)
|
397
|
+
def rewrite_omonthday(t)
|
398
|
+
slot(:monthday, OMONTHDAYS[t.string.downcase])
|
227
399
|
end
|
228
400
|
|
229
|
-
def
|
401
|
+
def rewrite_at_p(t)
|
402
|
+
pt = t.sublookup(:point).strinpd
|
403
|
+
pt = pt.start_with?('mon') ? 'M' : pt[0, 1]
|
404
|
+
pts = t.subgather(:count).collect { |e| e.string.to_i }
|
405
|
+
#p [ pt, pts ]
|
406
|
+
case pt
|
407
|
+
#when 'm' then slot(:m, pts)
|
408
|
+
when 'm' then slot(:hm, '*', pts, strong: 1)
|
409
|
+
when 's' then slot(:second, pts)
|
410
|
+
else slot(pt.to_sym, pts)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def rewrite_every_single_interval(t)
|
415
|
+
case t.string
|
416
|
+
when /year/i then [ slot(:month, 1, :weak), slot(:monthday, 1, :weak) ]
|
417
|
+
#when /week/i then xxx...
|
418
|
+
else slot(:weekday, 0, :weak)
|
419
|
+
end
|
420
|
+
end
|
230
421
|
|
231
|
-
def
|
232
|
-
def nat(i); rep(:nat, i, :elt, 1); end
|
422
|
+
def rewrite_every_interval(t)
|
233
423
|
|
234
|
-
|
424
|
+
#Raabro.pp(t, colours: true)
|
425
|
+
ci = t.subgather(nil).collect(&:string)
|
426
|
+
i = ci.pop.strip[0, 3]
|
427
|
+
c = (ci.pop || '1').strip
|
428
|
+
i = (i == 'M' || i.downcase == 'mon') ? 'M' : i[0, 1].downcase
|
429
|
+
cc = c == '1' ? '*' : "*/#{c}"
|
430
|
+
|
431
|
+
case i
|
432
|
+
when 'M' then slot(:month, cc)
|
433
|
+
when 'd' then slot(:monthday, cc, :weak)
|
434
|
+
#when 'h' then slot(:hm, cc, 0, weak: :minute)
|
435
|
+
when 'h' then slot(:hm, cc, 0, weak: 1)
|
436
|
+
when 'm' then slot(:hm, '*', cc, strong: 1)
|
437
|
+
when 's' then slot(:second, cc)
|
438
|
+
else {}
|
439
|
+
end
|
440
|
+
end
|
235
441
|
|
236
|
-
def
|
442
|
+
def rewrite_every_named(t)
|
237
443
|
|
444
|
+
case s = t.string
|
445
|
+
when /weekday/i then slot(:weekday, '1-5', :weak)
|
446
|
+
when /week/i then slot(:weekday, '0', :weak)
|
447
|
+
else fail "cannot rewrite #{s.inspect}"
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def rewrite_tz(t)
|
452
|
+
slot(:tz, t.string)
|
453
|
+
end
|
454
|
+
|
455
|
+
def rewrite_weekday(t)
|
456
|
+
Fugit::Cron::Parser::WEEKDS.index(t.string[0, 3].downcase)
|
457
|
+
end
|
458
|
+
|
459
|
+
def rewrite_weekdays(t)
|
238
460
|
#Raabro.pp(t, colours: true)
|
239
|
-
t
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
461
|
+
slot(:weekday, _rewrite_subs(t, :weekday))
|
462
|
+
end
|
463
|
+
alias rewrite_on_weekdays rewrite_weekdays
|
464
|
+
|
465
|
+
def rewrite_to_weekday(t)
|
466
|
+
wd0, wd1 = _rewrite_subs(t, :weekday)
|
467
|
+
#wd1 = 7 if wd1 == 0
|
468
|
+
slot(:weekday, "#{wd0}-#{wd1}")
|
469
|
+
end
|
470
|
+
|
471
|
+
def rewrite_to_omonthday(t)
|
472
|
+
md0, md1 = _rewrite_subs(t, :omonthday).collect(&:_data0)
|
473
|
+
slot(:monthday, "#{md0}-#{md1}")
|
474
|
+
end
|
475
|
+
|
476
|
+
def adjust_h(h, ap)
|
477
|
+
h = h.to_i
|
478
|
+
ap = ap || ''
|
479
|
+
(h < 12 && ap == 'pm' || ap == 'midnight') ? h + 12 : h
|
480
|
+
end
|
481
|
+
|
482
|
+
def rewrite_digital_hour(t)
|
483
|
+
h, m, ap = t.strinpd.split(/[: \t]+/)
|
484
|
+
h, m = adjust_h(h, ap), m.to_i
|
485
|
+
slot(:hm, h, m)
|
486
|
+
end
|
487
|
+
|
488
|
+
def rewrite_simple_hour(t)
|
489
|
+
h, ap = t.subgather(nil).collect(&:strinpd)
|
490
|
+
h = adjust_h(h, ap)
|
491
|
+
slot(:hm, h, 0)
|
492
|
+
end
|
493
|
+
|
494
|
+
def rewrite_named_hour(t)
|
495
|
+
|
496
|
+
ht = t.sublookup(:named_h)
|
497
|
+
mt = t.sublookup(:named_m)
|
498
|
+
apt = t.sublookup(:ampm)
|
499
|
+
|
500
|
+
h = ht.strinp
|
501
|
+
m = mt ? mt.strinp : 0
|
502
|
+
#p [ 0, '-->', h, m ]
|
503
|
+
h = NHOURS[h]
|
504
|
+
m = NMINUTES[m] || m
|
505
|
+
#p [ 1, '-->', h, m ]
|
506
|
+
|
507
|
+
h = adjust_h(h, apt && apt.strinpd)
|
508
|
+
|
509
|
+
slot(:hm, h, m)
|
510
|
+
end
|
511
|
+
|
512
|
+
def rewrite_to_hour(t)
|
513
|
+
#Raabro.pp(t, colours: true)
|
514
|
+
ht0, ht1 = t.subgather(nil)
|
515
|
+
h0, h1 = rewrite(ht0), rewrite(ht1)
|
516
|
+
fail ArgumentError.new(
|
517
|
+
"cannot deal with #{ht0.strinp} to #{ht1.strinp}, minutes diverge"
|
518
|
+
) if h0.data1 != h1.data1
|
519
|
+
slot(:hm, "#{h0._data0}-#{h1._data0}", 0, strong: 0)
|
520
|
+
end
|
521
|
+
|
522
|
+
def rewrite_at(t)
|
523
|
+
_rewrite_subs(t)
|
524
|
+
end
|
525
|
+
|
526
|
+
def rewrite_every(t)
|
527
|
+
_rewrite_sub(t)
|
528
|
+
end
|
529
|
+
|
530
|
+
def rewrite_nat(t)
|
531
|
+
#Raabro.pp(t, colours: true)
|
532
|
+
Fugit::Nat::SlotGroup.new(_rewrite_subs(t).flatten)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
class Slot
|
537
|
+
attr_reader :key
|
538
|
+
attr_accessor :_data0, :_data1
|
539
|
+
def initialize(key, d0, d1=nil, opts=nil)
|
540
|
+
d1, opts = d1.is_a?(Symbol) ? [ nil, d1 ] : [ d1, opts ]
|
541
|
+
@key, @_data0, @_data1 = key, d0, d1
|
542
|
+
@opts = (opts.is_a?(Symbol) ? { opts => true } : opts) || {}
|
543
|
+
end
|
544
|
+
def data0; @data0 ||= Array(@_data0); end
|
545
|
+
def data1; @data1 ||= Array(@_data1); end
|
546
|
+
def weak; @opts[:weak]; end
|
547
|
+
def strong; @opts[:strong]; end
|
548
|
+
def graded?; weak || strong; end
|
549
|
+
def append(slot)
|
550
|
+
@_data0, @_data1 = conflate(0, slot), conflate(1, slot)
|
551
|
+
@opts.clear
|
552
|
+
self
|
553
|
+
end
|
554
|
+
def inspect
|
555
|
+
a = [ @key, @_data0 ]
|
556
|
+
a << @_data1 if @_data1 != nil
|
557
|
+
a << @opts if @opts && @opts.keys.any?
|
558
|
+
"(slot #{a.collect(&:inspect).join(' ')})"
|
559
|
+
end
|
560
|
+
def a; [ data0, data1 ]; end
|
561
|
+
protected
|
562
|
+
def to_a(x)
|
563
|
+
return [] if x == '*'
|
564
|
+
Array(x)
|
565
|
+
end
|
566
|
+
def conflate(index, slot)
|
567
|
+
a, b = index == 0 ? [ @_data0, slot._data0 ] : [ @_data1, slot._data1 ]
|
568
|
+
return a if b == nil
|
569
|
+
return b if a == nil
|
570
|
+
if ra = (index == 0 && slot.strong == 1 && hour_range)
|
571
|
+
h0, h1 = ra[0], ra[1] - 1; return h0 == h1 ? h0 : "#{h0}-#{h1}"
|
572
|
+
elsif rb = (index == 0 && strong == 1 && slot.hour_range)
|
573
|
+
h0, h1 = rb[0], rb[1] - 1; return h0 == h1 ? h0 : "#{h0}-#{h1}"
|
574
|
+
end
|
575
|
+
return a if strong == index || strong == true
|
576
|
+
return b if slot.strong == index || slot.strong == true
|
577
|
+
return a if slot.weak == index || slot.weak == true
|
578
|
+
return b if weak == index || weak == true
|
579
|
+
return [ '*' ] if a == '*' && b == '*'
|
580
|
+
to_a(a).concat(to_a(b))
|
581
|
+
end
|
582
|
+
def hour_range
|
583
|
+
m = (key == :hm && @_data1 == 0 && @_data0.match(/\A(\d+)-(\d+)\z/))
|
584
|
+
m ? [ m[1].to_i, m[2].to_i ] : nil
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
class SlotGroup
|
589
|
+
|
590
|
+
def initialize(slots)
|
591
|
+
|
592
|
+
#puts "SlotGroup.new " + slots.inspect
|
593
|
+
@slots = {}
|
594
|
+
@hms = []
|
595
|
+
|
596
|
+
slots.each do |s|
|
597
|
+
if s.key == :hm
|
598
|
+
#ls = @hms.last; @hms.pop if ls && ls.key == :hm && ls.weak == true
|
599
|
+
@hms << s
|
600
|
+
elsif hs = @slots[s.key]
|
601
|
+
hs.append(s)
|
602
|
+
else
|
603
|
+
@slots[s.key] = s
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
if @slots[:monthday] || @slots[:weekday]
|
608
|
+
@hms << make_slot(:hm, 0, 0) if @hms.empty?
|
609
|
+
elsif @slots[:month]
|
610
|
+
@hms << make_slot(:hm, 0, 0) if @hms.empty?
|
611
|
+
@slots[:monthday] ||= make_slot(:monthday, 1)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
def to_crons(opts)
|
616
|
+
|
617
|
+
multi = opts.has_key?(:multi) ? opts[:multi] : false
|
618
|
+
|
619
|
+
hms = determine_hms
|
620
|
+
|
621
|
+
if multi == :fail && hms.count > 1
|
622
|
+
fail(ArgumentError.new(
|
623
|
+
"multiple crons in #{opts[:_s].inspect} - #{@slots.inspect}"))
|
624
|
+
elsif multi == true
|
625
|
+
hms.collect { |hm| parse_cron(hm) }
|
626
|
+
else
|
627
|
+
parse_cron(hms.first)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
protected
|
632
|
+
|
633
|
+
def make_slot(key, data0, data1=nil)
|
634
|
+
|
635
|
+
Fugit::Nat::Slot.new(key, data0, data1)
|
636
|
+
end
|
637
|
+
|
638
|
+
def determine_hms
|
639
|
+
|
640
|
+
return [ [ [ '*' ], [ '*' ] ] ] if @hms.empty?
|
641
|
+
|
642
|
+
hms = @hms.dup
|
643
|
+
#
|
644
|
+
while ig = (hms.count > 1 && hms.index { |hm| hm.graded? }) do
|
645
|
+
sg = hms[ig]
|
646
|
+
so = hms.delete_at(ig == 0 ? 1 : ig - 1)
|
647
|
+
sg.append(so)
|
648
|
+
end
|
649
|
+
|
650
|
+
hms
|
651
|
+
.collect(&:a)
|
652
|
+
.inject({}) { |r, hm|
|
653
|
+
hm[1].each { |m| (r[m] ||= []).concat(hm[0]) }
|
654
|
+
r }
|
655
|
+
.inject({}) { |r, (m, hs)|
|
656
|
+
(r[hs.sort] ||= []) << m
|
657
|
+
r }
|
658
|
+
.to_a
|
659
|
+
end
|
660
|
+
|
661
|
+
def parse_cron(hm)
|
662
|
+
|
663
|
+
a = [
|
664
|
+
slot(:second, '0'),
|
665
|
+
hm[1],
|
666
|
+
hm[0],
|
667
|
+
slot(:monthday, '*'),
|
668
|
+
slot(:month, '*'),
|
669
|
+
slot(:weekday, '*') ]
|
670
|
+
tz = @slots[:tz]
|
671
|
+
a << tz.data0 if tz
|
672
|
+
a.shift if a.first == [ '0' ]
|
673
|
+
|
674
|
+
s = a
|
675
|
+
.collect { |e| e.uniq.sort.collect(&:to_s).join(',') }
|
676
|
+
.join(' ')
|
677
|
+
|
678
|
+
Fugit::Cron.parse(s)
|
679
|
+
end
|
680
|
+
|
681
|
+
def slot(key, default)
|
682
|
+
s = @slots[key]
|
683
|
+
s ? s.data0 : [ default ]
|
268
684
|
end
|
269
685
|
end
|
270
686
|
end
|