Skip to content

Commit d0a6b6a

Browse files
authored
Prevent illogical rule combinations (#420)
2 parents 31b0e71 + 7f7d5a7 commit d0a6b6a

33 files changed

+409
-34
lines changed

lib/ice_cube/input_alignment.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
module IceCube
2+
class InputAlignment
3+
4+
def initialize(rule, value, rule_part)
5+
@rule = rule
6+
@value = value
7+
@rule_part = rule_part
8+
end
9+
10+
attr_reader :rule, :value, :rule_part
11+
12+
def verify(freq, options={}, &block)
13+
@rule.validations[:interval] or return
14+
15+
case @rule
16+
when DailyRule
17+
verify_wday_alignment(freq, &block)
18+
when MonthlyRule
19+
verify_month_alignment(freq, &block)
20+
else
21+
verify_freq_alignment(freq, &block)
22+
end
23+
end
24+
25+
private
26+
27+
def interval_validation
28+
@interval_validation ||= @rule.validations[:interval].first
29+
end
30+
31+
def interval_value
32+
@interval_value ||= (rule_part == :interval) ? value : interval_validation.interval
33+
end
34+
35+
def fixed_validations
36+
@fixed_validations ||= @rule.validations.values.flatten.select { |v|
37+
interval_type = (v.type == :wday ? :day : v.type)
38+
v.class < Validations::FixedValue &&
39+
interval_type == rule.base_interval_validation.type
40+
}
41+
end
42+
43+
def verify_freq_alignment(freq)
44+
interval_validation.type == freq or return
45+
(last_validation = fixed_validations.min_by(&:value)) or return
46+
47+
alignment = (value - last_validation.value) % interval_validation.interval
48+
return if alignment.zero?
49+
50+
validation_values = fixed_validations.map(&:value).join(', ')
51+
if rule_part == :interval
52+
message = "interval(#{value}) " \
53+
"must be a multiple of " \
54+
"intervals in #{last_validation.key}(#{validation_values})"
55+
else
56+
message = "intervals in #{last_validation.key}(#{validation_values}, #{value}) " \
57+
"must be multiples of " \
58+
"interval(#{interval_validation.interval})"
59+
end
60+
61+
yield ArgumentError.new(message)
62+
end
63+
64+
def verify_month_alignment(_freq)
65+
return if interval_value == 1 || (interval_value % 12).zero?
66+
return if fixed_validations.empty?
67+
68+
message = "month_of_year can only be used with interval(1) or multiples of interval(12)"
69+
70+
yield ArgumentError.new(message)
71+
end
72+
73+
def verify_wday_alignment(freq)
74+
return if interval_value == 1
75+
76+
if freq == :wday
77+
return if (interval_value % 7).zero?
78+
return if Array(@rule.validations[:day]).empty?
79+
message = "day can only be used with multiples of interval(7)"
80+
else
81+
(fixed_validation = fixed_validations.first) or return
82+
message = "#{fixed_validation.key} can only be used with interval(1)"
83+
end
84+
85+
yield ArgumentError.new(message)
86+
end
87+
88+
end
89+
end

lib/ice_cube/rules/daily_rule.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class DailyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
include Validations::DayOfMonth
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
# include Validations::DayOfYear # n/a
13+
514
include Validations::DailyInterval
615

716
def initialize(interval = 1)

lib/ice_cube/rules/hourly_rule.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class HourlyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
include Validations::DayOfMonth
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
include Validations::DayOfYear
13+
514
include Validations::HourlyInterval
615

716
def initialize(interval = 1)

lib/ice_cube/rules/minutely_rule.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class MinutelyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
include Validations::DayOfMonth
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
include Validations::DayOfYear
13+
514
include Validations::MinutelyInterval
615

716
def initialize(interval = 1)

lib/ice_cube/rules/monthly_rule.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class MonthlyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
include Validations::DayOfMonth
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
# include Validations::DayOfYear # n/a
13+
514
include Validations::MonthlyInterval
615

716
def initialize(interval = 1)

lib/ice_cube/rules/secondly_rule.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class SecondlyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
include Validations::DayOfMonth
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
include Validations::DayOfYear
13+
514
include Validations::SecondlyInterval
615

716
def initialize(interval = 1)

lib/ice_cube/rules/weekly_rule.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class WeeklyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
# include Validations::DayOfMonth # n/a
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
# include Validations::DayOfYear # n/a
13+
514
include Validations::WeeklyInterval
615

716
attr_reader :week_start
@@ -28,7 +37,7 @@ def realign(step_time, start_time)
2837
time = TimeUtil::TimeWrapper.new(start_time)
2938
offset = wday_offset(step_time, start_time)
3039
time.add(:day, offset)
31-
time.to_time
40+
super step_time, time.to_time
3241
end
3342

3443
# Calculate how many days to the first wday validation in the correct

lib/ice_cube/rules/yearly_rule.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ module IceCube
22

33
class YearlyRule < ValidatedRule
44

5+
include Validations::HourOfDay
6+
include Validations::MinuteOfHour
7+
include Validations::SecondOfMinute
8+
include Validations::DayOfMonth
9+
include Validations::DayOfWeek
10+
include Validations::Day
11+
include Validations::MonthOfYear
12+
include Validations::DayOfYear
13+
514
include Validations::YearlyInterval
615

716
def initialize(interval = 1)

lib/ice_cube/time_util.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,17 @@ def clear_below(type)
311311
end
312312
end
313313

314-
private
314+
def hour=(value)
315+
@time += (value * ONE_HOUR) - (@time.hour * ONE_HOUR)
316+
end
317+
318+
def min=(value)
319+
@time += (value * ONE_MINUTE) - (@time.min * ONE_MINUTE)
320+
end
321+
322+
def sec=(value)
323+
@time += (value) - (@time.sec)
324+
end
315325

316326
def clear_sec
317327
@time.sec > 0 ? @time -= @time.sec : @time

lib/ice_cube/validated_rule.rb

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
1+
require 'ice_cube/input_alignment'
2+
13
module IceCube
24

35
class ValidatedRule < Rule
46

57
include Validations::ScheduleLock
68

7-
include Validations::HourOfDay
8-
include Validations::MinuteOfHour
9-
include Validations::SecondOfMinute
10-
include Validations::DayOfMonth
11-
include Validations::DayOfWeek
12-
include Validations::Day
13-
include Validations::MonthOfYear
14-
include Validations::DayOfYear
15-
169
include Validations::Count
1710
include Validations::Until
1811

@@ -185,6 +178,12 @@ def validation_names
185178
VALIDATION_ORDER & @validations.keys
186179
end
187180

181+
def verify_alignment(value, freq, rule_part)
182+
InputAlignment.new(self, value, rule_part).verify(freq) do |error|
183+
yield error
184+
end
185+
end
186+
188187
end
189188

190189
end

0 commit comments

Comments
 (0)