fugit 1.3.9 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +20 -4
- data/fugit.gemspec +2 -2
- data/lib/fugit.rb +2 -1
- data/lib/fugit/at.rb +1 -0
- data/lib/fugit/cron.rb +18 -11
- data/lib/fugit/duration.rb +3 -3
- data/lib/fugit/misc.rb +1 -0
- data/lib/fugit/nat.rb +504 -355
- data/lib/fugit/parse.rb +1 -0
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 123f788edd6b7510be158be7ffdca00424eea5755aff767ae643180689947636
|
4
|
+
data.tar.gz: c789eaf6ecf0ae363faafa8536b11b97e6c6e02af35983ae13f9c8649fe63ab3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7af25bbc69dcf5f891900d1620f171451a38ea269cb888ff440061ba0d8f1357a3789bcc446b71beba27a5029ad3214a65197d32165c4ed8e7fcc98fd25ad7e
|
7
|
+
data.tar.gz: 89dad0285342e14e9ba1e346a1aae742775048c87bd569b1b8ec5a5edde914435cc3ffb414baa52b948b7af948e5c2cff9ea75cf61503133454eebf780eaebe3
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.4.0 released 2020-10-27
|
6
|
+
|
7
|
+
* Ensure cron accepts "25-L" for monthday, gh-45
|
8
|
+
* Allow for "every weekday 8am to 5pm", gh-44
|
9
|
+
* Allow "every day from the 25th to the last", gh-45
|
10
|
+
* Rework nat parser
|
11
|
+
|
12
|
+
|
5
13
|
## fugit 1.3.9 released 2020-09-17
|
6
14
|
|
7
15
|
* Prevent "New York skip", gh-43, thanks @honglooker
|
data/README.md
CHANGED
@@ -321,19 +321,35 @@ Fugit.parse('every day at five') # ==> Fugit::Cron instance '0 5 * * *'
|
|
321
321
|
|
322
322
|
### Ambiguous nats
|
323
323
|
|
324
|
-
Not all strings result in a clean, single, cron expression.
|
324
|
+
Not all strings result in a clean, single, cron expression. The `multi: false|true|:fail` argument to `Fugit::Nat.parse` could help.
|
325
325
|
|
326
326
|
```ruby
|
327
|
+
Fugit::Nat.parse('every day at 16:00 and 18:00')
|
328
|
+
.to_cron_s
|
329
|
+
# ==> '0 16,18 * * *' (a single Fugit::Cron instances)
|
327
330
|
Fugit::Nat.parse('every day at 16:00 and 18:00', multi: true)
|
328
|
-
|
331
|
+
.collect(&:to_cron_s)
|
332
|
+
# ==> [ '0 16,18 * * *' ] (array of Fugit::Cron instances, here only one)
|
333
|
+
|
329
334
|
Fugit::Nat.parse('every day at 16:15 and 18:30')
|
330
|
-
|
335
|
+
.to_cron_s
|
336
|
+
# ==> '15 16 * * *' (a single of Fugit::Cron instances)
|
331
337
|
Fugit::Nat.parse('every day at 16:15 and 18:30', multi: true)
|
332
|
-
|
338
|
+
.collect(&:to_cron_s)
|
339
|
+
# ==> [ '15 16 * * *', '30 18 * * *' ] (two Fugit::Cron instances)
|
340
|
+
|
333
341
|
Fugit::Nat.parse('every day at 16:15 and 18:30', multi: :fail)
|
334
342
|
# ==> ArgumentError: multiple crons in "every day at 16:15 and 18:30" (15 16 * * * | 30 18 * * *)
|
343
|
+
Fugit::Nat.parse('every day at 16:15 nada 18:30', multi: true)
|
344
|
+
# ==> nil
|
335
345
|
```
|
336
346
|
|
347
|
+
`multi: true` indicates to `Fugit::Nat` that an array of `Fugit::Cron` instances is expected as a result.
|
348
|
+
|
349
|
+
`multi: :fail` tells `Fugit::Nat.parse` to fail if the result is more than 1 `Fugit::Cron` instances.
|
350
|
+
|
351
|
+
`multi: false` is the default behaviour, return a single `Fugit::Cron` instance or nil when it cannot parse.
|
352
|
+
|
337
353
|
|
338
354
|
## LICENSE
|
339
355
|
|
data/fugit.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.authors = [ 'John Mettraux' ]
|
12
12
|
s.email = [ 'jmettraux+flor@gmail.com' ]
|
13
|
-
s.homepage = '
|
13
|
+
s.homepage = 'https://github.com/floraison/fugit'
|
14
14
|
s.license = 'MIT'
|
15
15
|
s.summary = 'time tools for flor'
|
16
16
|
|
@@ -40,7 +40,7 @@ Time tools for flor and the floraison project. Cron parsing and occurrence compu
|
|
40
40
|
#s.add_runtime_dependency 'tzinfo'
|
41
41
|
# this dependency appears in 'et-orbi'
|
42
42
|
|
43
|
-
s.add_runtime_dependency 'raabro', '~> 1.
|
43
|
+
s.add_runtime_dependency 'raabro', '~> 1.4'
|
44
44
|
s.add_runtime_dependency 'et-orbi', '~> 1.1', '>= 1.1.8'
|
45
45
|
|
46
46
|
s.add_development_dependency 'rspec', '~> 3.8'
|
data/lib/fugit.rb
CHANGED
data/lib/fugit/at.rb
CHANGED
data/lib/fugit/cron.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -12,9 +13,9 @@ module Fugit
|
|
12
13
|
'@daily' => '0 0 * * *',
|
13
14
|
'@midnight' => '0 0 * * *',
|
14
15
|
'@noon' => '0 12 * * *',
|
15
|
-
'@hourly' => '0 * * * *' }
|
16
|
+
'@hourly' => '0 * * * *' }.freeze
|
16
17
|
MAXDAYS = [
|
17
|
-
nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
|
18
|
+
nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ].freeze
|
18
19
|
|
19
20
|
attr_reader(
|
20
21
|
:original, :zone)
|
@@ -336,7 +337,7 @@ module Fugit
|
|
336
337
|
[ :seconds, 1, 60 ],
|
337
338
|
[ :minutes, 60, 60 ],
|
338
339
|
[ :hours, 3600, 24 ],
|
339
|
-
[ :days, 24 * 3600, 365 ] ]
|
340
|
+
[ :days, 24 * 3600, 365 ] ].freeze
|
340
341
|
|
341
342
|
def rough_frequency
|
342
343
|
|
@@ -495,7 +496,7 @@ module Fugit
|
|
495
496
|
|
496
497
|
sla = 1 if sla == nil
|
497
498
|
sta = min if sta == nil
|
498
|
-
edn = max if edn == nil
|
499
|
+
edn = max if edn == nil || edn < 0 && sta > 0
|
499
500
|
|
500
501
|
range(min, max, sta, edn, sla)
|
501
502
|
end
|
@@ -507,12 +508,10 @@ module Fugit
|
|
507
508
|
{ min: min, max: max, sta: sta, edn: edn, sla: sla }.inspect
|
508
509
|
) if (sta < 0 && edn > 0) || (edn < 0 && sta > 0)
|
509
510
|
|
510
|
-
#p({ min: min, max: max, sta: sta, edn: edn, sla: sla })
|
511
511
|
a = []
|
512
512
|
|
513
513
|
omin, omax = min, max
|
514
514
|
min, max = -max, -1 if sta < 0
|
515
|
-
#p({ min: min, max: max })
|
516
515
|
|
517
516
|
cur = sta
|
518
517
|
|
@@ -612,10 +611,18 @@ module Fugit
|
|
612
611
|
|
613
612
|
module Parser include Raabro
|
614
613
|
|
615
|
-
WEEKDAYS =
|
616
|
-
|
614
|
+
WEEKDAYS =
|
615
|
+
%w[ sunday monday tuesday wednesday thursday friday saturday ].freeze
|
617
616
|
|
618
|
-
|
617
|
+
WEEKDS =
|
618
|
+
WEEKDAYS.collect { |d| d[0, 3] }.freeze
|
619
|
+
DOW_REX =
|
620
|
+
/([0-7]|#{WEEKDS.join('|')})/i.freeze
|
621
|
+
|
622
|
+
MONTHS =
|
623
|
+
%w[ - jan feb mar apr may jun jul aug sep oct nov dec ].freeze
|
624
|
+
MONTH_REX =
|
625
|
+
/(1[0-2]|0?[1-9]|#{MONTHS[1..-1].join('|')})/i.freeze
|
619
626
|
|
620
627
|
# piece parsers bottom to top
|
621
628
|
|
@@ -629,8 +636,8 @@ module Fugit
|
|
629
636
|
def mos(i); rex(:mos, i, /[0-5]?\d/); end # min or sec
|
630
637
|
def hou(i); rex(:hou, i, /(2[0-4]|[01]?[0-9])/); end
|
631
638
|
def dom(i); rex(:dom, i, /(-?(3[01]|[12][0-9]|0?[1-9])|last|l)/i); end
|
632
|
-
def mon(i); rex(:mon, i,
|
633
|
-
def dow(i); rex(:dow, i,
|
639
|
+
def mon(i); rex(:mon, i, MONTH_REX); end
|
640
|
+
def dow(i); rex(:dow, i, DOW_REX); end
|
634
641
|
|
635
642
|
def dow_hash(i); rex(:hash, i, /#(-?[1-5]|last|l)/i); end
|
636
643
|
|
data/lib/fugit/duration.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -66,10 +67,9 @@ module Fugit
|
|
66
67
|
day: { a: 'D', r: 'd', i: 'D', s: 24 * 3600, I: true, l: 'day' },
|
67
68
|
hou: { a: 'h', r: 'h', i: 'H', s: 3600, I: true, l: 'hour' },
|
68
69
|
min: { a: 'm', r: 'm', i: 'M', s: 60, I: true, l: 'minute' },
|
69
|
-
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' }
|
70
|
-
}
|
70
|
+
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' } }.freeze
|
71
71
|
INFLA_KEYS, NON_INFLA_KEYS =
|
72
|
-
KEYS.partition { |k, v| v[:I] }
|
72
|
+
KEYS.partition { |k, v| v[:I] }.freeze
|
73
73
|
|
74
74
|
def _to_s(key)
|
75
75
|
|
data/lib/fugit/misc.rb
CHANGED
data/lib/fugit/nat.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -16,7 +17,12 @@ module Fugit
|
|
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
|
+
|
21
|
+
if slots = Parser.parse(s)
|
22
|
+
slots.to_crons(opts.merge(_s: s))
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
def do_parse(s, opts={})
|
@@ -24,510 +30,653 @@ module Fugit
|
|
24
30
|
parse(s, opts) ||
|
25
31
|
fail(ArgumentError.new("could not parse a nat #{s.inspect}"))
|
26
32
|
end
|
33
|
+
end
|
27
34
|
|
28
|
-
|
35
|
+
module Parser include Raabro
|
29
36
|
|
30
|
-
|
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
|
31
73
|
|
32
|
-
|
33
|
-
|
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
|
34
88
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
#
|
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
|
43
97
|
|
44
|
-
|
45
|
-
|
46
|
-
return nil
|
47
|
-
end
|
98
|
+
#
|
99
|
+
# parsers bottom to top #################################################
|
48
100
|
|
49
|
-
|
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
|
50
106
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
.inject({}) { |r, (m, hs)| (r[hs.sort] ||= []) << m; r }
|
55
|
-
.to_a
|
56
|
-
.sort_by { |hs, ms| -hs.size }
|
57
|
-
if hours.empty?
|
58
|
-
hours << (h[:dom] ? [ [ '0' ], [ '0' ] ] : [ [ '*' ], [ '*' ] ])
|
59
|
-
end
|
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
|
60
110
|
|
61
|
-
|
62
|
-
|
111
|
+
def _and_or_or_or_comma(i)
|
112
|
+
rex(nil, i, /[ \t]*(,[ \t]*)?((and|or)[ \t]+|,[ \t]*)/i); end
|
63
113
|
|
64
|
-
|
65
|
-
|
66
|
-
"(#{crons.collect(&:original).join(' | ')})"
|
67
|
-
) if opts[:multi] == :fail && crons.size > 1
|
114
|
+
def _to_or_dash(i);
|
115
|
+
rex(nil, i, /[ \t]*-[ \t]*|[ \t]+(to|through)[ \t]+/i); end
|
68
116
|
|
69
|
-
|
70
|
-
|
71
|
-
else
|
72
|
-
crons.first
|
73
|
-
end
|
74
|
-
end
|
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
|
75
119
|
|
76
|
-
def
|
120
|
+
def _space(i); rex(nil, i, /[ \t]+/); end
|
121
|
+
def _sep(i); rex(nil, i, /([ \t]+|[ \t]*,[ \t]*)/); end
|
77
122
|
|
78
|
-
|
79
|
-
s = []
|
80
|
-
s << h[:sec] if h[:sec]
|
81
|
-
s << h[:hms][1].join(',')
|
82
|
-
s << h[:hms][0].join(',')
|
83
|
-
s << (h[:dom] || '*') << (h[:mon] || '*') << (h[:dow] || '*')
|
84
|
-
s << h[:tz] if h[:tz]
|
123
|
+
def count(i); rex(:count, i, /\d+/); end
|
85
124
|
|
86
|
-
|
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)
|
87
133
|
end
|
88
134
|
|
89
|
-
def
|
90
|
-
|
91
|
-
def parse_interval_elt(e, opts, h)
|
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
|
92
137
|
|
93
|
-
|
138
|
+
def weekdays(i); jseq(:weekdays, i, :weekday, :_and_or_or_or_comma); end
|
94
139
|
|
95
|
-
|
96
|
-
when 's', 'sec', 'second', 'seconds'
|
97
|
-
h[:sec] = eone(e)
|
98
|
-
when 'm', 'min', 'mins', 'minute', 'minutes'
|
99
|
-
h[:hms] ||= [ [ '*', eone(e) ] ]
|
100
|
-
when 'h', 'hour', 'hours'
|
101
|
-
hms = h[:hms]
|
102
|
-
if hms && hms.size == 1 && hms.first.first == '*'
|
103
|
-
hms.first[0] = eone(e)
|
104
|
-
elsif ! hms
|
105
|
-
h[:hms] = [ [ eone(e), 0 ] ]
|
106
|
-
end
|
107
|
-
when 'd', 'day', 'days'
|
108
|
-
h[:dom] = "*/#{e1}" if e1 > 1
|
109
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
110
|
-
when 'w', 'week', 'weeks'
|
111
|
-
h[:_fail] = "cannot have crons for \"every #{e1} weeks\"" if e1 > 1
|
112
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
113
|
-
h[:dow] ||= 0
|
114
|
-
when 'M', 'month', 'months'
|
115
|
-
h[:_fail] = "cannot have crons for \"every #{e1} months\"" if e1 > 12
|
116
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
117
|
-
h[:dom] = 1
|
118
|
-
h[:mon] = eone(e)
|
119
|
-
when 'Y', 'y', 'year', 'years'
|
120
|
-
h[:_fail] = "cannot have crons for \"every #{e1} years\"" if e1 > 1
|
121
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
122
|
-
h[:dom] = 1
|
123
|
-
h[:mon] = 1
|
124
|
-
end
|
125
|
-
end
|
140
|
+
def on_the(i); seq(nil, i, :_the, :omonthdays); end
|
126
141
|
|
127
|
-
def
|
142
|
+
def _minute(i); rex(nil, i, /[ \t]*minute[ \t]+/i) end
|
128
143
|
|
129
|
-
|
130
|
-
|
144
|
+
def _dmin(i)
|
145
|
+
rex(:dmin, i, /[0-5]?[0-9]/)
|
131
146
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
136
|
-
h[:dow] = e[1] == e[2] ? e[1] : "#{e[1]}-#{e[2]}"
|
147
|
+
def and_dmin(i)
|
148
|
+
seq(nil, i, :_and_or_or_or_comma, :_minute, '?', :_dmin)
|
137
149
|
end
|
138
150
|
|
139
|
-
def
|
140
|
-
|
141
|
-
h[:dom] = e[1..-1].join(',')
|
151
|
+
def on_minutes(i)
|
152
|
+
seq(:on_minutes, i, :_minute, :_dmin, :and_dmin, '*')
|
142
153
|
end
|
143
154
|
|
144
|
-
def
|
145
|
-
|
146
|
-
(h[:hms] ||= []).concat(e[1])
|
147
|
-
|
148
|
-
l = h[:hms].last
|
149
|
-
h[:sec] = l.pop if l.size > 2
|
155
|
+
def on_thex(i);
|
156
|
+
rex(:on_thex, i, /[ \t]*the[ \t]+(hour|minute)[ \t]*/i);
|
150
157
|
end
|
151
158
|
|
152
|
-
def
|
153
|
-
|
154
|
-
|
155
|
-
h[:dow] = e1[0]
|
156
|
-
h[:hms] = [ e1[1] ]
|
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
|
157
162
|
|
158
|
-
|
159
|
-
|
163
|
+
def on_object(i)
|
164
|
+
alt(nil, i, :on_days, :on_weekdays, :on_minutes, :on_thes, :on_thex)
|
160
165
|
end
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
h[:tz] = e[1]
|
166
|
+
def on_objects(i)
|
167
|
+
jseq(nil, i, :on_object, :_and)
|
165
168
|
end
|
166
|
-
end
|
167
|
-
|
168
|
-
module Parser include Raabro
|
169
|
-
|
170
|
-
NUMS = %w[
|
171
|
-
zero one two three four five six seven eight nine ten eleven twelve ]
|
172
|
-
|
173
|
-
WEEKDAYS =
|
174
|
-
Fugit::Cron::Parser::WEEKDS + Fugit::Cron::Parser::WEEKDAYS
|
175
169
|
|
176
|
-
|
177
|
-
'
|
178
|
-
'
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
'fifty' => 50, 'fifty-five' => 55 }
|
186
|
-
|
187
|
-
oh = {
|
188
|
-
'1st' => 1, '2nd' => 2, '3rd' => 3, '21st' => 21, '22nd' => 22,
|
189
|
-
'23rd' => 23, '31st' => 31 }
|
190
|
-
%w[ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 24 25 26 27 28 29 30 ]
|
191
|
-
.each { |i| oh["#{i}th"] = i.to_i }
|
192
|
-
%w[
|
193
|
-
first second third fourth fifth sixth seventh eighth ninth tenth
|
194
|
-
eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth
|
195
|
-
eighteenth nineteenth twentieth twenty-first twenty-second twenty-third
|
196
|
-
twenty-fourth twenty-fifth twenty-fifth twenty-sixth twenty-seventh
|
197
|
-
twenty-eighth twenty-ninth thirtieth thirty-first ]
|
198
|
-
.each_with_index { |e, i| oh[e] = i + 1 }
|
199
|
-
ORDINALS = oh
|
200
|
-
|
201
|
-
# piece parsers bottom to top
|
202
|
-
|
203
|
-
def _from(i); rex(nil, i, /\s*from\s+/i); end
|
204
|
-
def _every(i); rex(nil, i, /\s*(every)\s+/i); end
|
205
|
-
def _at(i); rex(nil, i, /\s*at\s+/i); end
|
206
|
-
def _in(i); rex(nil, i, /\s*(in|on)\s+/i); end
|
207
|
-
def _to(i); rex(nil, i, /\s*to\s+/i); end
|
208
|
-
def _dash(i); rex(nil, i, /-\s*/i); end
|
209
|
-
def _and(i); rex(nil, i, /\s*and\s+/i); end
|
210
|
-
def _on(i); rex(nil, i, /\s*on\s+/i); end
|
211
|
-
|
212
|
-
def _and_or_comma(i)
|
213
|
-
rex(nil, i, /\s*(,?\s*and\s|,?\s*or\s|,)\s*/i)
|
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)
|
214
179
|
end
|
215
|
-
|
216
|
-
|
180
|
+
|
181
|
+
def city_tz(i)
|
182
|
+
rex(nil, i, /[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}/)
|
217
183
|
end
|
218
|
-
def
|
219
|
-
rex(nil, i,
|
184
|
+
def named_tz(i)
|
185
|
+
rex(nil, i, /Z|UTC/)
|
220
186
|
end
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
def tz_name(i)
|
225
|
-
rex(nil, i,
|
226
|
-
/\s*[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}(\s+|$)/)
|
187
|
+
def delta_tz(i)
|
188
|
+
rex(nil, i, /[-+]([01][0-9]|2[0-4])(:?(00|15|30|45))?/)
|
227
189
|
end
|
228
|
-
def
|
229
|
-
|
230
|
-
/\s*[-+]([01][0-9]|2[0-4]):?(00|15|30|45)(\s+|$)/)
|
190
|
+
def tz(i)
|
191
|
+
alt(:tz, i, :city_tz, :named_tz, :delta_tz)
|
231
192
|
end
|
232
193
|
def tzone(i)
|
233
|
-
|
194
|
+
seq(nil, i, :_in_or_on, '?', :tz)
|
234
195
|
end
|
235
196
|
|
236
|
-
def
|
237
|
-
rex(
|
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)
|
238
201
|
end
|
239
202
|
|
240
|
-
def
|
241
|
-
rex(:
|
203
|
+
def ampm(i)
|
204
|
+
rex(:ampm, i, /[ \t]*(am|pm)/i)
|
242
205
|
end
|
243
|
-
def
|
244
|
-
|
245
|
-
end
|
246
|
-
def named_digits(i)
|
247
|
-
seq(nil, i, :named_digit, '+', :and_named_digits, '*')
|
206
|
+
def dark(i)
|
207
|
+
rex(:dark, i, /[ \t]*dark/i)
|
248
208
|
end
|
249
209
|
|
250
|
-
def
|
251
|
-
|
210
|
+
def simple_h(i)
|
211
|
+
rex(:simple_h, i, /#{(0..24).to_a.reverse.join('|')}/)
|
252
212
|
end
|
253
|
-
|
254
|
-
|
255
|
-
rex(:nminute, i, /(#{NMINUTES.keys.join('|')})\s*/i)
|
213
|
+
def simple_hour(i)
|
214
|
+
seq(:simple_hour, i, :simple_h, :ampm, '?')
|
256
215
|
end
|
257
|
-
|
258
|
-
|
216
|
+
|
217
|
+
def named_m(i)
|
218
|
+
rex(:named_m, i, NAMED_M_REX)
|
259
219
|
end
|
260
|
-
def
|
261
|
-
seq(
|
220
|
+
def named_min(i)
|
221
|
+
seq(nil, i, :_space, :named_m)
|
262
222
|
end
|
263
223
|
|
224
|
+
def named_h(i)
|
225
|
+
rex(:named_h, i, NAMED_H_REX)
|
226
|
+
end
|
264
227
|
def named_hour(i)
|
265
|
-
|
228
|
+
seq(:named_hour, i, :named_h, :dark, '?', :named_min, '?', :ampm, '?')
|
266
229
|
end
|
267
230
|
|
268
|
-
def
|
269
|
-
rex(:shour, i, /(2[0-4]|[01]?[0-9])/)
|
270
|
-
end
|
271
|
-
def simple_hour(i)
|
272
|
-
seq(:simple_hour, i, :shour, :am_pm, '?')
|
273
|
-
end
|
231
|
+
def _point(i); rex(:point, i, POINT_REX); end
|
274
232
|
|
275
|
-
def
|
276
|
-
|
277
|
-
def dig_hour(i); alt(nil, i, :dig_hour_a, :dig_hour_b); end
|
278
|
-
#
|
279
|
-
def digital_hour(i)
|
280
|
-
seq(:digital_hour, i, :dig_hour, :am_pm, '?')
|
233
|
+
def counts(i)
|
234
|
+
jseq(nil, i, :count, :_and_or_or_or_comma)
|
281
235
|
end
|
282
236
|
|
237
|
+
def at_p(i)
|
238
|
+
seq(:at_p, i, :_point, :counts)
|
239
|
+
end
|
283
240
|
def at_point(i)
|
284
|
-
|
285
|
-
:digital_hour, :simple_hour, :named_hour, :numeral_hour,
|
286
|
-
:named_digits)
|
241
|
+
jseq(nil, i, :at_p, :_and_or_or)
|
287
242
|
end
|
288
243
|
|
289
|
-
|
290
|
-
|
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)
|
291
252
|
end
|
292
|
-
|
293
|
-
|
294
|
-
seq(nil, i, :_and_or_comma, :at_point)
|
253
|
+
def at_objects(i)
|
254
|
+
jseq(nil, i, :at_object, :_and_or_or_or_comma)
|
295
255
|
end
|
296
256
|
|
297
|
-
def
|
298
|
-
|
299
|
-
/(
|
300
|
-
y(ears?)?|months?|w(eeks?)?|d(ays?)?|
|
301
|
-
h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
|
302
|
-
)(\s+|$)/ix)
|
257
|
+
def at(i)
|
258
|
+
seq(:at, i, :_at, '?', :at_objects)
|
303
259
|
end
|
304
260
|
|
305
|
-
def
|
306
|
-
rex(:
|
307
|
-
/(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
|
261
|
+
def interval(i)
|
262
|
+
rex(:interval, i, INTERVAL_REX)
|
308
263
|
end
|
309
|
-
|
310
|
-
|
264
|
+
|
265
|
+
# every day
|
266
|
+
# every 1 minute
|
267
|
+
def every_interval(i)
|
268
|
+
seq(:every_interval, i, :count, '?', :interval)
|
311
269
|
end
|
312
270
|
|
313
|
-
def
|
314
|
-
rex(:
|
271
|
+
def every_single_interval(i)
|
272
|
+
rex(:every_single_interval, i, /(1[ \t]+)?(week|year)/)
|
315
273
|
end
|
316
274
|
|
317
|
-
def
|
318
|
-
|
275
|
+
def to_weekday(i)
|
276
|
+
seq(:to_weekday, i, :weekday, :_to_or_dash, :weekday)
|
277
|
+
end
|
319
278
|
|
320
|
-
def
|
321
|
-
|
279
|
+
def weekday_range(i)
|
280
|
+
alt(nil, i, :to_weekday, :weekdays)
|
322
281
|
end
|
323
|
-
|
324
|
-
|
282
|
+
|
283
|
+
def to_omonthday(i)
|
284
|
+
seq(:to_omonthday, i,
|
285
|
+
:_the, '?', :omonthday, :_to, :_the, '?', :omonthday)
|
325
286
|
end
|
326
|
-
|
327
|
-
|
287
|
+
|
288
|
+
def to_hour(i)
|
289
|
+
seq(:to_hour, i, :at_object, :_to, :at_object)
|
328
290
|
end
|
329
291
|
|
330
|
-
def
|
331
|
-
|
292
|
+
def from_object(i)
|
293
|
+
alt(nil, i, :to_weekday, :to_omonthday, :to_hour)
|
332
294
|
end
|
333
|
-
def
|
334
|
-
|
295
|
+
def from_objects(i)
|
296
|
+
jseq(nil, i, :from_object, :_and_or_or)
|
335
297
|
end
|
336
|
-
def
|
337
|
-
|
298
|
+
def from(i)
|
299
|
+
seq(nil, i, :_from, '?', :from_objects)
|
338
300
|
end
|
339
301
|
|
340
|
-
|
341
|
-
|
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)
|
342
307
|
end
|
343
308
|
|
344
|
-
def
|
345
|
-
|
309
|
+
def otm(i)
|
310
|
+
rex(nil, i, /[ \t]+of the month/)
|
346
311
|
end
|
347
|
-
|
348
|
-
|
312
|
+
|
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)
|
349
320
|
end
|
350
|
-
|
351
|
-
|
321
|
+
|
322
|
+
def every_named(i)
|
323
|
+
rex(:every_named, i, /weekday/i)
|
352
324
|
end
|
353
325
|
|
354
|
-
def
|
355
|
-
|
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)
|
356
331
|
end
|
357
|
-
def
|
358
|
-
|
332
|
+
def every_objects(i)
|
333
|
+
jseq(nil, i, :every_object, :_and_or_or)
|
359
334
|
end
|
360
|
-
|
361
|
-
|
335
|
+
|
336
|
+
def every(i)
|
337
|
+
seq(:every, i, :_every, :every_objects)
|
362
338
|
end
|
363
339
|
|
364
|
-
def
|
365
|
-
alt(nil, i, :
|
340
|
+
def nat_elt(i)
|
341
|
+
alt(nil, i, :every, :from, :at, :tzone, :on)
|
342
|
+
end
|
343
|
+
def nat(i)
|
344
|
+
jseq(:nat, i, :nat_elt, :_sep)
|
366
345
|
end
|
367
346
|
|
368
|
-
|
369
|
-
|
347
|
+
#
|
348
|
+
# rewrite parsed tree ###################################################
|
349
|
+
|
350
|
+
def slot(key, data0, data1=nil, opts=nil)
|
351
|
+
Slot.new(key, data0, data1, opts)
|
370
352
|
end
|
371
353
|
|
372
|
-
def
|
373
|
-
|
354
|
+
def _rewrite_subs(t, key=nil)
|
355
|
+
t.subgather(key).collect { |ct| rewrite(ct) }
|
374
356
|
end
|
375
|
-
def
|
376
|
-
|
357
|
+
def _rewrite_sub(t, key=nil)
|
358
|
+
st = t.sublookup(key)
|
359
|
+
st ? rewrite(st) : nil
|
377
360
|
end
|
378
361
|
|
379
|
-
def
|
380
|
-
|
362
|
+
def rewrite_dmin(t)
|
363
|
+
t.strinp
|
381
364
|
end
|
382
|
-
|
383
|
-
|
365
|
+
|
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)
|
384
371
|
end
|
385
|
-
|
386
|
-
|
372
|
+
|
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
|
387
380
|
end
|
388
|
-
|
389
|
-
|
381
|
+
|
382
|
+
def rewrite_on_thes(t)
|
383
|
+
_rewrite_subs(t, :omonthday)
|
390
384
|
end
|
391
|
-
def
|
392
|
-
|
385
|
+
def rewrite_on_days(t)
|
386
|
+
_rewrite_subs(t, :monthday)
|
393
387
|
end
|
394
388
|
|
395
|
-
def
|
396
|
-
|
397
|
-
end
|
398
|
-
def at_every(i)
|
399
|
-
seq(nil, i, :at, :every, :tz, '?')
|
389
|
+
def rewrite_on(t)
|
390
|
+
_rewrite_subs(t)
|
400
391
|
end
|
401
392
|
|
402
|
-
def
|
403
|
-
|
393
|
+
def rewrite_monthday(t)
|
394
|
+
slot(:monthday, t.string.to_i)
|
404
395
|
end
|
405
396
|
|
406
|
-
def
|
407
|
-
|
397
|
+
def rewrite_omonthday(t)
|
398
|
+
slot(:monthday, OMONTHDAYS[t.string.downcase])
|
408
399
|
end
|
409
|
-
|
410
|
-
|
400
|
+
|
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
|
411
412
|
end
|
412
|
-
|
413
|
-
|
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
|
414
420
|
end
|
415
421
|
|
416
|
-
def
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
422
|
+
def rewrite_every_interval(t)
|
423
|
+
|
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
|
421
440
|
end
|
422
441
|
|
423
|
-
|
442
|
+
def rewrite_every_named(t)
|
424
443
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
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
|
430
449
|
end
|
431
|
-
|
432
|
-
|
450
|
+
|
451
|
+
def rewrite_tz(t)
|
452
|
+
slot(:tz, t.string)
|
433
453
|
end
|
434
|
-
|
435
|
-
|
454
|
+
|
455
|
+
def rewrite_weekday(t)
|
456
|
+
Fugit::Cron::Parser::WEEKDS.index(t.string[0, 3].downcase)
|
436
457
|
end
|
437
458
|
|
438
|
-
def
|
459
|
+
def rewrite_weekdays(t)
|
460
|
+
#Raabro.pp(t, colours: true)
|
461
|
+
slot(:weekday, _rewrite_subs(t, :weekday))
|
462
|
+
end
|
463
|
+
alias rewrite_on_weekdays rewrite_weekdays
|
439
464
|
|
440
|
-
def
|
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
|
441
470
|
|
442
|
-
|
471
|
+
def rewrite_to_omonthday(t)
|
472
|
+
md0, md1 = _rewrite_subs(t, :omonthday).collect(&:_data0)
|
473
|
+
slot(:monthday, "#{md0}-#{md1}")
|
443
474
|
end
|
444
475
|
|
445
|
-
def
|
476
|
+
def rewrite_digital_hour(t)
|
477
|
+
h, m, ap = t.strinpd.split(/[: \t]+/)
|
478
|
+
h, m = h.to_i, m.to_i
|
479
|
+
h += 12 if ap && ap == 'pm'
|
480
|
+
slot(:hm, h.to_i, m.to_i)
|
481
|
+
end
|
446
482
|
|
447
|
-
|
483
|
+
def rewrite_simple_hour(t)
|
484
|
+
h, ap = t.subgather(nil).collect(&:strinpd)
|
485
|
+
h = h.to_i
|
486
|
+
h = h + 12 if ap == 'pm'
|
487
|
+
slot(:hm, h, 0)
|
448
488
|
end
|
449
489
|
|
450
|
-
def
|
490
|
+
def rewrite_named_hour(t)
|
451
491
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
end
|
492
|
+
ht = t.sublookup(:named_h)
|
493
|
+
mt = t.sublookup(:named_m)
|
494
|
+
apt = t.sublookup(:ampm)
|
456
495
|
|
457
|
-
|
496
|
+
h = ht.strinp
|
497
|
+
m = mt ? mt.strinp : 0
|
498
|
+
#p [ 0, '-->', h, m ]
|
499
|
+
h = NHOURS[h]
|
500
|
+
m = NMINUTES[m] || m
|
501
|
+
#p [ 1, '-->', h, m ]
|
458
502
|
|
459
|
-
|
503
|
+
h += 12 if h < 13 && apt && apt.strinpd == 'pm'
|
460
504
|
|
461
|
-
|
462
|
-
when /^s/ then [ '*', '*', i ]
|
463
|
-
when /^m/ then [ '*', i ]
|
464
|
-
end
|
505
|
+
slot(:hm, h, m)
|
465
506
|
end
|
466
507
|
|
467
|
-
def
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
[ v, m ]
|
508
|
+
def rewrite_to_hour(t)
|
509
|
+
#Raabro.pp(t, colours: true)
|
510
|
+
ht0, ht1 = t.subgather(nil)
|
511
|
+
h0, h1 = rewrite(ht0), rewrite(ht1)
|
512
|
+
fail ArgumentError.new(
|
513
|
+
"cannot deal with #{ht0.strinp} to #{ht1.strinp}, minutes diverge"
|
514
|
+
) if h0.data1 != h1.data1
|
515
|
+
slot(:hm, "#{h0._data0}-#{h1._data0}", 0, strong: 0)
|
476
516
|
end
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
v += 12 if vs[1] == 'pm'
|
481
|
-
[ v, 0 ]
|
517
|
+
|
518
|
+
def rewrite_at(t)
|
519
|
+
_rewrite_subs(t)
|
482
520
|
end
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
min = m[2].to_i
|
487
|
-
[ hou, min ]
|
521
|
+
|
522
|
+
def rewrite_every(t)
|
523
|
+
_rewrite_sub(t)
|
488
524
|
end
|
489
525
|
|
490
|
-
def
|
526
|
+
def rewrite_nat(t)
|
527
|
+
#Raabro.pp(t, colours: true)
|
528
|
+
Fugit::Nat::SlotGroup.new(_rewrite_subs(t).flatten)
|
529
|
+
end
|
530
|
+
end
|
491
531
|
|
492
|
-
|
532
|
+
class Slot
|
533
|
+
attr_reader :key
|
534
|
+
attr_accessor :_data0, :_data1
|
535
|
+
def initialize(key, d0, d1=nil, opts=nil)
|
536
|
+
d1, opts = d1.is_a?(Symbol) ? [ nil, d1 ] : [ d1, opts ]
|
537
|
+
@key, @_data0, @_data1 = key, d0, d1
|
538
|
+
@opts = (opts.is_a?(Symbol) ? { opts => true } : opts) || {}
|
539
|
+
end
|
540
|
+
def data0; @data0 ||= Array(@_data0); end
|
541
|
+
def data1; @data1 ||= Array(@_data1); end
|
542
|
+
def weak; @opts[:weak]; end
|
543
|
+
def strong; @opts[:strong]; end
|
544
|
+
def graded?; weak || strong; end
|
545
|
+
def append(slot)
|
546
|
+
@_data0, @_data1 = conflate(0, slot), conflate(1, slot)
|
547
|
+
@opts.clear
|
548
|
+
self
|
549
|
+
end
|
550
|
+
def inspect
|
551
|
+
a = [ @key, @_data0 ]
|
552
|
+
a << @_data1 if @_data1 != nil
|
553
|
+
a << @opts if @opts && @opts.keys.any?
|
554
|
+
"(slot #{a.collect(&:inspect).join(' ')})"
|
555
|
+
end
|
556
|
+
def a; [ data0, data1 ]; end
|
557
|
+
protected
|
558
|
+
def to_a(x)
|
559
|
+
return [] if x == '*'
|
560
|
+
Array(x)
|
561
|
+
end
|
562
|
+
def conflate(index, slot)
|
563
|
+
a, b = index == 0 ? [ @_data0, slot._data0 ] : [ @_data1, slot._data1 ]
|
564
|
+
return a if b == nil
|
565
|
+
return b if a == nil
|
566
|
+
if ra = (index == 0 && slot.strong == 1 && hour_range)
|
567
|
+
h0, h1 = ra[0], ra[1] - 1; return h0 == h1 ? h0 : "#{h0}-#{h1}"
|
568
|
+
elsif rb = (index == 0 && strong == 1 && slot.hour_range)
|
569
|
+
h0, h1 = rb[0], rb[1] - 1; return h0 == h1 ? h0 : "#{h0}-#{h1}"
|
570
|
+
end
|
571
|
+
return a if strong == index || strong == true
|
572
|
+
return b if slot.strong == index || slot.strong == true
|
573
|
+
return a if slot.weak == index || slot.weak == true
|
574
|
+
return b if weak == index || weak == true
|
575
|
+
return [ '*' ] if a == '*' && b == '*'
|
576
|
+
to_a(a).concat(to_a(b))
|
577
|
+
end
|
578
|
+
def hour_range
|
579
|
+
m = (key == :hm && @_data1 == 0 && @_data0.match(/\A(\d+)-(\d+)\z/))
|
580
|
+
m ? [ m[1].to_i, m[2].to_i ] : nil
|
493
581
|
end
|
582
|
+
end
|
494
583
|
|
495
|
-
|
584
|
+
class SlotGroup
|
496
585
|
|
497
|
-
def
|
586
|
+
def initialize(slots)
|
498
587
|
|
499
|
-
#
|
500
|
-
|
501
|
-
|
588
|
+
#puts "SlotGroup.new " + slots.inspect
|
589
|
+
@slots = {}
|
590
|
+
@hms = []
|
591
|
+
|
592
|
+
slots.each do |s|
|
593
|
+
if s.key == :hm
|
594
|
+
#ls = @hms.last; @hms.pop if ls && ls.key == :hm && ls.weak == true
|
595
|
+
@hms << s
|
596
|
+
elsif hs = @slots[s.key]
|
597
|
+
hs.append(s)
|
598
|
+
else
|
599
|
+
@slots[s.key] = s
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
if @slots[:monthday] || @slots[:weekday]
|
604
|
+
@hms << make_slot(:hm, 0, 0) if @hms.empty?
|
605
|
+
elsif @slots[:month]
|
606
|
+
@hms << make_slot(:hm, 0, 0) if @hms.empty?
|
607
|
+
@slots[:monthday] ||= make_slot(:monthday, 1)
|
608
|
+
end
|
502
609
|
end
|
503
610
|
|
504
|
-
|
611
|
+
def to_crons(opts)
|
612
|
+
|
613
|
+
multi = opts.has_key?(:multi) ? opts[:multi] : false
|
614
|
+
|
615
|
+
hms = determine_hms
|
616
|
+
|
617
|
+
if multi == :fail && hms.count > 1
|
618
|
+
fail(ArgumentError.new(
|
619
|
+
"multiple crons in #{opts[:_s].inspect} - #{@slots.inspect}"))
|
620
|
+
elsif multi == true
|
621
|
+
hms.collect { |hm| parse_cron(hm) }
|
622
|
+
else
|
623
|
+
parse_cron(hms.first)
|
624
|
+
end
|
625
|
+
end
|
505
626
|
|
506
|
-
|
507
|
-
def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
|
627
|
+
protected
|
508
628
|
|
509
|
-
def
|
629
|
+
def make_slot(key, data0, data1=nil)
|
510
630
|
|
511
|
-
|
631
|
+
Fugit::Nat::Slot.new(key, data0, data1)
|
512
632
|
end
|
513
633
|
|
514
|
-
def
|
634
|
+
def determine_hms
|
635
|
+
|
636
|
+
return [ [ [ '*' ], [ '*' ] ] ] if @hms.empty?
|
515
637
|
|
516
|
-
|
638
|
+
hms = @hms.dup
|
639
|
+
#
|
640
|
+
while ig = (hms.count > 1 && hms.index { |hm| hm.graded? }) do
|
641
|
+
sg = hms[ig]
|
642
|
+
so = hms.delete_at(ig == 0 ? 1 : ig - 1)
|
643
|
+
sg.append(so)
|
644
|
+
end
|
517
645
|
|
518
|
-
|
646
|
+
hms
|
647
|
+
.collect(&:a)
|
648
|
+
.inject({}) { |r, hm|
|
649
|
+
hm[1].each { |m| (r[m] ||= []).concat(hm[0]) }
|
650
|
+
r }
|
651
|
+
.inject({}) { |r, (m, hs)|
|
652
|
+
(r[hs.sort] ||= []) << m
|
653
|
+
r }
|
654
|
+
.to_a
|
519
655
|
end
|
520
656
|
|
521
|
-
|
522
|
-
alias rewrite_at _rewrite_multiple
|
657
|
+
def parse_cron(hm)
|
523
658
|
|
524
|
-
|
525
|
-
|
659
|
+
a = [
|
660
|
+
slot(:second, '0'),
|
661
|
+
hm[1],
|
662
|
+
hm[0],
|
663
|
+
slot(:monthday, '*'),
|
664
|
+
slot(:month, '*'),
|
665
|
+
slot(:weekday, '*') ]
|
666
|
+
tz = @slots[:tz]
|
667
|
+
a << tz.data0 if tz
|
668
|
+
a.shift if a.first == [ '0' ]
|
526
669
|
|
527
|
-
|
670
|
+
s = a
|
671
|
+
.collect { |e| e.uniq.sort.collect(&:to_s).join(',') }
|
672
|
+
.join(' ')
|
673
|
+
|
674
|
+
Fugit::Cron.parse(s)
|
675
|
+
end
|
528
676
|
|
529
|
-
|
530
|
-
|
677
|
+
def slot(key, default)
|
678
|
+
s = @slots[key]
|
679
|
+
s ? s.data0 : [ default ]
|
531
680
|
end
|
532
681
|
end
|
533
682
|
end
|