ice_cube_cron 0.0.5 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +2 -0
- data/README.md +5 -3
- data/lib/ice_cube_cron/expression_parser.rb +85 -53
- data/lib/ice_cube_cron/parser_attribute.rb +14 -4
- data/lib/ice_cube_cron/rule_builder.rb +38 -15
- data/lib/ice_cube_cron/version.rb +1 -1
- data/spec/lib/ice_cube_cron/base_spec.rb +120 -62
- data/spec/lib/ice_cube_cron/expression_parser_spec.rb +190 -69
- data/spec/lib/ice_cube_cron/rule_builder_spec.rb +104 -12
- metadata +2 -3
- data/Gemfile.lock +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b14af35504a36135e5f349fa1daa0436c608848
|
4
|
+
data.tar.gz: a5344d84ce8b858875732e5c594c93444a8afc22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb37fb552f54bfacf7cd4c05ad2ad24682732dc0804b9e7d43abc67187c86f811ab289bab932109c49d85252f7fa571e7c8cb5d37e114ba623fa7645d3419940
|
7
|
+
data.tar.gz: 8d1e857a18f5510ec8f88a23bbaf11a2371414b8f6d96950325f06413999e3a6ba186c253f4a9492a4e7ce869da0467575cee2c40b08cc5b1def62d4e9c6386a
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -45,7 +45,7 @@ require 'ice_cube_cron'
|
|
45
45
|
schedule = ::IceCube::Schedule.from_cron(::Date.current, "* * * * 5")
|
46
46
|
|
47
47
|
# using hash
|
48
|
-
schedule = ::IceCube::Schedule.from_cron(::Date.new(2015, 1, 5), :
|
48
|
+
schedule = ::IceCube::Schedule.from_cron(::Date.new(2015, 1, 5), :day_of_month => 5)
|
49
49
|
|
50
50
|
schedule.occurrences_between(::Date.new(2015, 3, 5), ::Date.new(2015, 6, 5))
|
51
51
|
# => [2015-03-05 00:00:00 UTC, 2015-04-05 00:00:00 UTC, 2015-05-05 00:00:00 UTC, 2015-06-05 00:00:00 UTC]
|
@@ -53,7 +53,7 @@ schedule.occurrences_between(::Date.new(2015, 3, 5), ::Date.new(2015, 6, 5))
|
|
53
53
|
|
54
54
|
## recurrence rule examples (date)
|
55
55
|
|
56
|
-
|desc|interval|year|month|
|
56
|
+
|desc|interval|year|month|day_of_month|day_of_week|
|
57
57
|
|----|-------:|---:|----:|--:|------:|
|
58
58
|
|1st of every month||||1||
|
59
59
|
|1st and 15th of every month||||1,15||
|
@@ -69,4 +69,6 @@ schedule.occurrences_between(::Date.new(2015, 3, 5), ::Date.new(2015, 6, 5))
|
|
69
69
|
- Does not yet support all recurrence options. More coming.
|
70
70
|
|
71
71
|
## todo
|
72
|
-
- Add support for
|
72
|
+
- Add support for W special character
|
73
|
+
- Change hash param `day` to `day_of_month`
|
74
|
+
- Improve expression validation
|
@@ -6,21 +6,23 @@ module IceCubeCron # :nodoc:
|
|
6
6
|
include ::IceCubeCron::ParserAttribute
|
7
7
|
|
8
8
|
EXPRESSION_PART_DEFAULTS = {
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
9
|
+
:interval => 1,
|
10
|
+
:year => nil,
|
11
|
+
:month => nil,
|
12
|
+
:day_of_month => nil,
|
13
|
+
:day_of_week => nil,
|
14
|
+
:hour => nil,
|
15
|
+
:minute => nil,
|
16
|
+
:until => nil
|
15
17
|
}
|
16
18
|
|
17
19
|
EXPRESSION_PART_KEYS = [
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
20
|
+
:minute,
|
21
|
+
:hour,
|
22
|
+
:day_of_month,
|
23
|
+
:month,
|
24
|
+
:day_of_week,
|
25
|
+
:year
|
24
26
|
]
|
25
27
|
|
26
28
|
##
|
@@ -43,7 +45,11 @@ module IceCubeCron # :nodoc:
|
|
43
45
|
self.expression_hash = EXPRESSION_PART_DEFAULTS.dup
|
44
46
|
|
45
47
|
expression_parts.each do |name, val|
|
46
|
-
|
48
|
+
begin
|
49
|
+
send("#{name}=", val)
|
50
|
+
rescue NoMethodError
|
51
|
+
raise ArgumentError, "Invalid parameter: #{name}"
|
52
|
+
end
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
@@ -51,41 +57,47 @@ module IceCubeCron # :nodoc:
|
|
51
57
|
ExpressionParser.sanitize_integer_param(val, 1)
|
52
58
|
end
|
53
59
|
|
54
|
-
parser_attribute_accessor :
|
60
|
+
parser_attribute_accessor :day_of_month do |val|
|
55
61
|
ExpressionParser.sanitize_day_param(val)
|
56
62
|
end
|
63
|
+
_define_parser_attribute_aliases(:day, :day_of_month, :day_of_month=)
|
57
64
|
|
58
65
|
parser_attribute_accessor :month do |val|
|
59
66
|
ExpressionParser.sanitize_integer_array_param(val)
|
60
67
|
end
|
61
68
|
|
62
|
-
parser_attribute_accessor :until
|
63
|
-
|
64
69
|
parser_attribute_accessor :year do |val|
|
65
70
|
ExpressionParser.sanitize_integer_array_param(val)
|
66
71
|
end
|
67
72
|
|
68
|
-
parser_attribute_accessor :
|
73
|
+
parser_attribute_accessor :day_of_week do |val|
|
69
74
|
ExpressionParser.sanitize_week_day_param(val)
|
70
75
|
end
|
76
|
+
_define_parser_attribute_aliases(:weekday, :day_of_week, :day_of_week=)
|
71
77
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
def string_to_expression_parts(expression_str)
|
76
|
-
return {} if expression_str.nil?
|
78
|
+
parser_attribute_accessor :hour do |val|
|
79
|
+
ExpressionParser.sanitize_integer_array_param(val)
|
80
|
+
end
|
77
81
|
|
78
|
-
|
82
|
+
parser_attribute_accessor :minute do |val|
|
83
|
+
ExpressionParser.sanitize_integer_array_param(val)
|
84
|
+
end
|
79
85
|
|
80
|
-
|
81
|
-
expression_parts.select! do |_key, value|
|
82
|
-
next false if value.nil?
|
83
|
-
next false if value == '*'
|
86
|
+
parser_attribute_accessor :until
|
84
87
|
|
85
|
-
|
86
|
-
|
88
|
+
##
|
89
|
+
# Sanitize given value to a valid day parameter
|
90
|
+
#
|
91
|
+
def self.sanitize_day_param(param)
|
92
|
+
return nil if param.blank?
|
93
|
+
return param if param.is_a?(::Array)
|
94
|
+
return [param] if param.is_a?(::Integer)
|
87
95
|
|
88
|
-
|
96
|
+
param.to_s.split(',').map do |element|
|
97
|
+
next -1 if element == 'L'
|
98
|
+
|
99
|
+
ExpressionParser.sanitize_integer_array_param(element)
|
100
|
+
end.flatten.uniq
|
89
101
|
end
|
90
102
|
|
91
103
|
##
|
@@ -98,19 +110,26 @@ module IceCubeCron # :nodoc:
|
|
98
110
|
end
|
99
111
|
|
100
112
|
##
|
101
|
-
# Sanitize given value to
|
113
|
+
# Sanitize given value to an integer array
|
102
114
|
#
|
103
|
-
|
115
|
+
#--
|
116
|
+
# rubocop:disable Metrics/AbcSize
|
117
|
+
#++
|
118
|
+
def self.sanitize_integer_array_param(param)
|
104
119
|
return nil if param.blank?
|
105
120
|
return param if param.is_a?(::Array)
|
106
121
|
return [param] if param.is_a?(::Integer)
|
107
122
|
|
108
|
-
param.
|
109
|
-
|
110
|
-
|
111
|
-
|
123
|
+
param.split(',').map do |element|
|
124
|
+
if element =~ /[0-9]+\-[0-9]+/
|
125
|
+
parts = element.split('-')
|
126
|
+
(parts[0].to_i..parts[1].to_i).to_a
|
127
|
+
else
|
128
|
+
element.to_i
|
129
|
+
end
|
112
130
|
end.flatten.uniq
|
113
131
|
end
|
132
|
+
# rubocop:enable Metrics/AbcSize
|
114
133
|
|
115
134
|
##
|
116
135
|
# Sanitize given value to a valid weekday parameter
|
@@ -129,26 +148,39 @@ module IceCubeCron # :nodoc:
|
|
129
148
|
end
|
130
149
|
end
|
131
150
|
|
151
|
+
private
|
152
|
+
|
132
153
|
##
|
133
|
-
#
|
154
|
+
# Split a cron string and extract the LAST interval that appears
|
134
155
|
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
156
|
+
def split_parts_and_interval(expression_str)
|
157
|
+
interval = nil
|
158
|
+
parts = expression_str.split(/ +/).map do |part|
|
159
|
+
part, part_interval = part.split('/')
|
160
|
+
interval = part_interval unless part_interval.blank?
|
161
|
+
next nil if part.blank? || part == '*'
|
162
|
+
|
163
|
+
part
|
164
|
+
end
|
142
165
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
166
|
+
[parts, interval]
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Split string expression into parts
|
171
|
+
#
|
172
|
+
def string_to_expression_parts(expression_str)
|
173
|
+
return {} if expression_str.nil?
|
174
|
+
|
175
|
+
parts, interval = split_parts_and_interval(expression_str)
|
176
|
+
|
177
|
+
expression_parts = ::Hash[EXPRESSION_PART_KEYS.zip(parts)]
|
178
|
+
expression_parts.select! do |_key, value|
|
179
|
+
!value.nil?
|
180
|
+
end
|
181
|
+
expression_parts.merge!(:interval => interval) unless interval.nil?
|
182
|
+
|
183
|
+
expression_parts
|
151
184
|
end
|
152
|
-
# rubocop:enable Metrics/AbcSize
|
153
185
|
end
|
154
186
|
end
|
@@ -4,12 +4,14 @@ module IceCubeCron # :nodoc: all
|
|
4
4
|
base.extend(::IceCubeCron::ParserAttribute::ClassMethods)
|
5
5
|
end
|
6
6
|
|
7
|
+
private
|
8
|
+
|
7
9
|
attr_accessor :expression_hash
|
8
10
|
|
9
11
|
module ClassMethods # :nodoc:
|
10
12
|
def parser_attribute_accessor(attr_name, &cleanser)
|
11
|
-
getter = "
|
12
|
-
setter = "
|
13
|
+
getter = "#{attr_name}".to_sym
|
14
|
+
setter = "#{attr_name}=".to_sym
|
13
15
|
|
14
16
|
define_method(getter) do
|
15
17
|
expression_hash[getter]
|
@@ -20,8 +22,16 @@ module IceCubeCron # :nodoc: all
|
|
20
22
|
expression_hash[getter] = val
|
21
23
|
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
_define_parser_attribute_aliases(attr_name, getter, setter)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _define_parser_attribute_aliases(attr_name, getter, setter)
|
31
|
+
alias_method "repeat_#{attr_name}=".to_sym, setter
|
32
|
+
alias_method "repeat_#{attr_name}".to_sym, getter
|
33
|
+
alias_method "cron_#{attr_name}=".to_sym, setter
|
34
|
+
alias_method "cron_#{attr_name}".to_sym, getter
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
@@ -8,9 +8,10 @@ module IceCubeCron # :nodoc:
|
|
8
8
|
#
|
9
9
|
def build_rule(expression)
|
10
10
|
rule = build_root_recurrence_rule(expression)
|
11
|
-
rule =
|
11
|
+
rule = build_year_rules(rule, expression)
|
12
12
|
rule = build_weekday_rule(rule, expression)
|
13
|
-
rule = rule
|
13
|
+
rule = build_day_rules(rule, expression)
|
14
|
+
rule = build_time_rules(rule, expression)
|
14
15
|
rule = rule.until(expression.until) unless expression.until.blank?
|
15
16
|
|
16
17
|
rule
|
@@ -24,31 +25,53 @@ module IceCubeCron # :nodoc:
|
|
24
25
|
|
25
26
|
private
|
26
27
|
|
28
|
+
# rubocop:disable Metrics/AbcSize
|
29
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
30
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
31
|
+
def build_root_recurrence_rule(expression) # :nodoc:
|
32
|
+
return ::IceCube::Rule.minutely(expression.interval) if expression.minute.blank?
|
33
|
+
return ::IceCube::Rule.hourly(expression.interval) if expression.hour.blank?
|
34
|
+
unless nth_day?(expression.day_of_week)
|
35
|
+
return ::IceCube::Rule.weekly(expression.interval) if expression.day_of_month.blank? && !expression.day_of_week.blank?
|
36
|
+
return ::IceCube::Rule.daily(expression.interval) if expression.day_of_month.blank?
|
37
|
+
end
|
38
|
+
return ::IceCube::Rule.monthly(expression.interval) if expression.month.blank?
|
39
|
+
|
40
|
+
::IceCube::Rule.yearly(expression.interval)
|
41
|
+
end
|
42
|
+
# rubocop:enable Metrics/AbcSize
|
43
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
44
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
45
|
+
|
46
|
+
# :nodoc:
|
47
|
+
def build_year_rules(rule, expression)
|
48
|
+
rule = rule.year(*expression.year) unless expression.year.blank?
|
49
|
+
rule = rule.month_of_year(*expression.month) unless expression.month.blank?
|
50
|
+
|
51
|
+
rule
|
52
|
+
end
|
53
|
+
|
27
54
|
# :nodoc:
|
28
55
|
def build_weekday_rule(rule, expression)
|
29
|
-
return rule.day_of_week(*expression.
|
30
|
-
return rule.day(*expression.
|
56
|
+
return rule.day_of_week(*expression.day_of_week) if !expression.day_of_week.blank? && nth_day?(expression.day_of_week)
|
57
|
+
return rule.day(*expression.day_of_week) unless expression.day_of_week.blank?
|
31
58
|
|
32
59
|
rule
|
33
60
|
end
|
34
61
|
|
35
62
|
# :nodoc:
|
36
|
-
def
|
37
|
-
rule = rule.
|
38
|
-
rule = rule.month_of_year(*expression.month) unless expression.month.blank?
|
63
|
+
def build_day_rules(rule, expression)
|
64
|
+
rule = rule.day_of_month(*expression.day_of_month) unless expression.day_of_month.blank?
|
39
65
|
|
40
66
|
rule
|
41
67
|
end
|
42
68
|
|
43
|
-
#
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
return ::IceCube::Rule.monthly(expression.interval) unless expression.day.blank?
|
48
|
-
return ::IceCube::Rule.weekly(expression.interval) unless expression.weekday.blank?
|
69
|
+
# :nodoc:
|
70
|
+
def build_time_rules(rule, expression)
|
71
|
+
rule = rule.hour_of_day(*expression.hour) unless expression.hour.blank? || expression.hour == [0]
|
72
|
+
rule = rule.minute_of_hour(*expression.minute) unless expression.minute.blank? || expression.minute == [0]
|
49
73
|
|
50
|
-
|
74
|
+
rule
|
51
75
|
end
|
52
|
-
# rubocop:enable Metrics/AbcSize
|
53
76
|
end
|
54
77
|
end
|