ice_cube_chosko 0.1.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/config/locales/en.yml +178 -0
- data/config/locales/es.yml +176 -0
- data/config/locales/ja.yml +107 -0
- data/lib/ice_cube.rb +92 -0
- data/lib/ice_cube/builders/hash_builder.rb +27 -0
- data/lib/ice_cube/builders/ical_builder.rb +59 -0
- data/lib/ice_cube/builders/string_builder.rb +76 -0
- data/lib/ice_cube/deprecated.rb +36 -0
- data/lib/ice_cube/errors/count_exceeded.rb +7 -0
- data/lib/ice_cube/errors/until_exceeded.rb +7 -0
- data/lib/ice_cube/flexible_hash.rb +40 -0
- data/lib/ice_cube/i18n.rb +24 -0
- data/lib/ice_cube/null_i18n.rb +28 -0
- data/lib/ice_cube/occurrence.rb +101 -0
- data/lib/ice_cube/parsers/hash_parser.rb +91 -0
- data/lib/ice_cube/parsers/ical_parser.rb +91 -0
- data/lib/ice_cube/parsers/yaml_parser.rb +19 -0
- data/lib/ice_cube/rule.rb +123 -0
- data/lib/ice_cube/rules/daily_rule.rb +16 -0
- data/lib/ice_cube/rules/hourly_rule.rb +16 -0
- data/lib/ice_cube/rules/minutely_rule.rb +16 -0
- data/lib/ice_cube/rules/monthly_rule.rb +16 -0
- data/lib/ice_cube/rules/secondly_rule.rb +15 -0
- data/lib/ice_cube/rules/weekly_rule.rb +16 -0
- data/lib/ice_cube/rules/yearly_rule.rb +16 -0
- data/lib/ice_cube/schedule.rb +529 -0
- data/lib/ice_cube/single_occurrence_rule.rb +28 -0
- data/lib/ice_cube/time_util.rb +328 -0
- data/lib/ice_cube/validated_rule.rb +184 -0
- data/lib/ice_cube/validations/count.rb +61 -0
- data/lib/ice_cube/validations/daily_interval.rb +54 -0
- data/lib/ice_cube/validations/day.rb +71 -0
- data/lib/ice_cube/validations/day_of_month.rb +55 -0
- data/lib/ice_cube/validations/day_of_week.rb +77 -0
- data/lib/ice_cube/validations/day_of_year.rb +61 -0
- data/lib/ice_cube/validations/fixed_value.rb +95 -0
- data/lib/ice_cube/validations/hour_of_day.rb +55 -0
- data/lib/ice_cube/validations/hourly_interval.rb +54 -0
- data/lib/ice_cube/validations/lock.rb +95 -0
- data/lib/ice_cube/validations/minute_of_hour.rb +54 -0
- data/lib/ice_cube/validations/minutely_interval.rb +54 -0
- data/lib/ice_cube/validations/month_of_year.rb +54 -0
- data/lib/ice_cube/validations/monthly_interval.rb +53 -0
- data/lib/ice_cube/validations/schedule_lock.rb +46 -0
- data/lib/ice_cube/validations/second_of_minute.rb +54 -0
- data/lib/ice_cube/validations/secondly_interval.rb +51 -0
- data/lib/ice_cube/validations/until.rb +57 -0
- data/lib/ice_cube/validations/weekly_interval.rb +67 -0
- data/lib/ice_cube/validations/yearly_interval.rb +53 -0
- data/lib/ice_cube/version.rb +5 -0
- data/spec/spec_helper.rb +64 -0
- metadata +166 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::HourOfDay
|
4
|
+
|
5
|
+
# Add hour of day validations
|
6
|
+
def hour_of_day(*hours)
|
7
|
+
hours.flatten.each do |hour|
|
8
|
+
unless hour.is_a?(Fixnum)
|
9
|
+
raise ArgumentError, "expecting Fixnum value for hour, got #{hour.inspect}"
|
10
|
+
end
|
11
|
+
validations_for(:hour_of_day) << Validation.new(hour)
|
12
|
+
end
|
13
|
+
clobber_base_validations(:hour)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
class Validation < Validations::FixedValue
|
18
|
+
|
19
|
+
attr_reader :hour
|
20
|
+
alias :value :hour
|
21
|
+
|
22
|
+
def initialize(hour)
|
23
|
+
@hour = hour
|
24
|
+
end
|
25
|
+
|
26
|
+
def type
|
27
|
+
:hour
|
28
|
+
end
|
29
|
+
|
30
|
+
def dst_adjust?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_s(builder)
|
35
|
+
builder.piece(:hour_of_day) << StringBuilder.nice_number(hour)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_hash(builder)
|
39
|
+
builder.validations_array(:hour_of_day) << hour
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_ical(builder)
|
43
|
+
builder['BYHOUR'] << hour
|
44
|
+
end
|
45
|
+
|
46
|
+
StringBuilder.register_formatter(:hour_of_day) do |segments|
|
47
|
+
str = StringBuilder.sentence(segments)
|
48
|
+
IceCube::I18n.t('ice_cube.at_hours_of_the_day', count: segments.size, segments: str)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::HourlyInterval
|
4
|
+
|
5
|
+
def interval(interval)
|
6
|
+
@interval = normalized_interval(interval)
|
7
|
+
replace_validations_for(:interval, [Validation.new(@interval)])
|
8
|
+
clobber_base_validations(:hour)
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
class Validation
|
13
|
+
|
14
|
+
attr_reader :interval
|
15
|
+
|
16
|
+
def initialize(interval)
|
17
|
+
@interval = interval
|
18
|
+
end
|
19
|
+
|
20
|
+
def type
|
21
|
+
:hour
|
22
|
+
end
|
23
|
+
|
24
|
+
def dst_adjust?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate(step_time, schedule)
|
29
|
+
t0, t1 = schedule.start_time.to_i, step_time.to_i
|
30
|
+
sec = (t1 - t1 % ONE_HOUR) -
|
31
|
+
(t0 - t0 % ONE_HOUR)
|
32
|
+
hours = sec / ONE_HOUR
|
33
|
+
offset = (hours % interval).nonzero?
|
34
|
+
interval - offset if offset
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_s(builder)
|
38
|
+
builder.base = IceCube::I18n.t("ice_cube.each_hour", count: interval)
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_hash(builder)
|
42
|
+
builder[:interval] = interval
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_ical(builder)
|
46
|
+
builder['FREQ'] << 'HOURLY'
|
47
|
+
builder['INTERVAL'] << interval unless interval == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
# This validation mixin is used by the various "fixed-time" (e.g. day,
|
4
|
+
# day_of_month, hour_of_day) Validation and ScheduleLock::Validation modules.
|
5
|
+
# It is not a standalone rule validation like the others.
|
6
|
+
#
|
7
|
+
# Given the including Validation's defined +type+ field, it will lock
|
8
|
+
# to the specified +value+ or else the corresponding time unit from the
|
9
|
+
# schedule's start_time
|
10
|
+
#
|
11
|
+
module Validations::Lock
|
12
|
+
|
13
|
+
INTERVALS = {:min => 60, :sec => 60, :hour => 24, :month => 12, :wday => 7}
|
14
|
+
|
15
|
+
def validate(time, schedule)
|
16
|
+
case type
|
17
|
+
when :day then validate_day_lock(time, schedule)
|
18
|
+
when :hour then validate_hour_lock(time, schedule)
|
19
|
+
else validate_interval_lock(time, schedule)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Validate if the current time unit matches the same unit from the schedule
|
26
|
+
# start time, returning the difference to the interval
|
27
|
+
#
|
28
|
+
def validate_interval_lock(time, schedule)
|
29
|
+
t0 = starting_unit(schedule.start_time)
|
30
|
+
t1 = time.send(type)
|
31
|
+
t0 >= t1 ? t0 - t1 : INTERVALS[type] - t1 + t0
|
32
|
+
end
|
33
|
+
|
34
|
+
# Lock the hour if explicitly set by hour_of_day, but allow for the nearest
|
35
|
+
# hour during DST start to keep the correct interval.
|
36
|
+
#
|
37
|
+
def validate_hour_lock(time, schedule)
|
38
|
+
h0 = starting_unit(schedule.start_time)
|
39
|
+
h1 = time.hour
|
40
|
+
if h0 >= h1
|
41
|
+
h0 - h1
|
42
|
+
else
|
43
|
+
if dst_offset = TimeUtil.dst_change(time)
|
44
|
+
h0 - h1 + dst_offset
|
45
|
+
else
|
46
|
+
24 - h1 + h0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# For monthly rules that have no specified day value, the validation relies
|
52
|
+
# on the schedule start time and jumps to include every month even if it
|
53
|
+
# has fewer days than the schedule's start day.
|
54
|
+
#
|
55
|
+
# Negative day values (from month end) also include all months.
|
56
|
+
#
|
57
|
+
# Positive day values are taken literally so months with fewer days will
|
58
|
+
# be skipped.
|
59
|
+
#
|
60
|
+
def validate_day_lock(time, schedule)
|
61
|
+
days_in_month = TimeUtil.days_in_month(time)
|
62
|
+
date = Date.new(time.year, time.month, time.day)
|
63
|
+
|
64
|
+
if value && value < 0
|
65
|
+
start = TimeUtil.day_of_month(value, date)
|
66
|
+
month_overflow = days_in_month - TimeUtil.days_in_next_month(time)
|
67
|
+
elsif value && value > 0
|
68
|
+
start = value
|
69
|
+
month_overflow = 0
|
70
|
+
else
|
71
|
+
start = TimeUtil.day_of_month(schedule.start_time.day, date)
|
72
|
+
month_overflow = 0
|
73
|
+
end
|
74
|
+
|
75
|
+
sleeps = start - date.day
|
76
|
+
|
77
|
+
if value && value > 0
|
78
|
+
until_next_month = days_in_month + sleeps
|
79
|
+
else
|
80
|
+
until_next_month = start < 28 ? days_in_month : TimeUtil.days_to_next_month(date)
|
81
|
+
until_next_month += sleeps - month_overflow
|
82
|
+
end
|
83
|
+
|
84
|
+
sleeps >= 0 ? sleeps : until_next_month
|
85
|
+
end
|
86
|
+
|
87
|
+
def starting_unit(start_time)
|
88
|
+
start = value || start_time.send(type)
|
89
|
+
start += INTERVALS[type] while start < 0
|
90
|
+
start
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::MinuteOfHour
|
4
|
+
|
5
|
+
def minute_of_hour(*minutes)
|
6
|
+
minutes.flatten.each do |minute|
|
7
|
+
unless minute.is_a?(Fixnum)
|
8
|
+
raise ArgumentError, "expecting Fixnum value for minute, got #{minute.inspect}"
|
9
|
+
end
|
10
|
+
validations_for(:minute_of_hour) << Validation.new(minute)
|
11
|
+
end
|
12
|
+
clobber_base_validations(:min)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
class Validation < Validations::FixedValue
|
17
|
+
|
18
|
+
attr_reader :minute
|
19
|
+
alias :value :minute
|
20
|
+
|
21
|
+
def initialize(minute)
|
22
|
+
@minute = minute
|
23
|
+
end
|
24
|
+
|
25
|
+
def type
|
26
|
+
:min
|
27
|
+
end
|
28
|
+
|
29
|
+
def dst_adjust?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_s(builder)
|
34
|
+
builder.piece(:minute_of_hour) << StringBuilder.nice_number(minute)
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_hash(builder)
|
38
|
+
builder.validations_array(:minute_of_hour) << minute
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_ical(builder)
|
42
|
+
builder['BYMINUTE'] << minute
|
43
|
+
end
|
44
|
+
|
45
|
+
StringBuilder.register_formatter(:minute_of_hour) do |segments|
|
46
|
+
str = StringBuilder.sentence(segments)
|
47
|
+
IceCube::I18n.t('ice_cube.on_minutes_of_hour', count: segments.size, segments: str)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::MinutelyInterval
|
4
|
+
|
5
|
+
def interval(interval)
|
6
|
+
@interval = normalized_interval(interval)
|
7
|
+
replace_validations_for(:interval, [Validation.new(@interval)])
|
8
|
+
clobber_base_validations(:min)
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
class Validation
|
13
|
+
|
14
|
+
attr_reader :interval
|
15
|
+
|
16
|
+
def initialize(interval)
|
17
|
+
@interval = interval
|
18
|
+
end
|
19
|
+
|
20
|
+
def type
|
21
|
+
:min
|
22
|
+
end
|
23
|
+
|
24
|
+
def dst_adjust?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate(step_time, schedule)
|
29
|
+
t0, t1 = schedule.start_time.to_i, step_time.to_i
|
30
|
+
sec = (t1 - t1 % ONE_MINUTE) -
|
31
|
+
(t0 - t0 % ONE_MINUTE)
|
32
|
+
minutes = sec / ONE_MINUTE
|
33
|
+
offset = (minutes % interval).nonzero?
|
34
|
+
interval - offset if offset
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_s(builder)
|
38
|
+
builder.base = IceCube::I18n.t('ice_cube.each_minute', count: interval)
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_hash(builder)
|
42
|
+
builder[:interval] = interval
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_ical(builder)
|
46
|
+
builder['FREQ'] << 'MINUTELY'
|
47
|
+
builder['INTERVAL'] << interval unless interval == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::MonthOfYear
|
4
|
+
|
5
|
+
def month_of_year(*months)
|
6
|
+
months.flatten.each do |month|
|
7
|
+
unless month.is_a?(Fixnum) || month.is_a?(Symbol)
|
8
|
+
raise ArgumentError, "expecting Fixnum or Symbol value for month, got #{month.inspect}"
|
9
|
+
end
|
10
|
+
month = TimeUtil.sym_to_month(month)
|
11
|
+
validations_for(:month_of_year) << Validation.new(month)
|
12
|
+
end
|
13
|
+
clobber_base_validations :month
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
class Validation < Validations::FixedValue
|
18
|
+
|
19
|
+
attr_reader :month
|
20
|
+
alias :value :month
|
21
|
+
|
22
|
+
def initialize(month)
|
23
|
+
@month = month
|
24
|
+
end
|
25
|
+
|
26
|
+
def type
|
27
|
+
:month
|
28
|
+
end
|
29
|
+
|
30
|
+
def dst_adjust?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_s(builder)
|
35
|
+
builder.piece(:month_of_year) << IceCube::I18n.t("ice_cube.date.month_names")[month]
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_hash(builder)
|
39
|
+
builder.validations_array(:month_of_year) << month
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_ical(builder)
|
43
|
+
builder['BYMONTH'] << month
|
44
|
+
end
|
45
|
+
|
46
|
+
StringBuilder.register_formatter(:month_of_year) do |segments|
|
47
|
+
IceCube::I18n.t("ice_cube.in", target: StringBuilder.sentence(segments))
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::MonthlyInterval
|
4
|
+
|
5
|
+
def interval(interval)
|
6
|
+
@interval = normalized_interval(interval)
|
7
|
+
replace_validations_for(:interval, [Validation.new(@interval)])
|
8
|
+
clobber_base_validations(:month)
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
class Validation
|
13
|
+
|
14
|
+
attr_reader :interval
|
15
|
+
|
16
|
+
def initialize(interval)
|
17
|
+
@interval = interval
|
18
|
+
end
|
19
|
+
|
20
|
+
def type
|
21
|
+
:month
|
22
|
+
end
|
23
|
+
|
24
|
+
def dst_adjust?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate(step_time, schedule)
|
29
|
+
t0, t1 = schedule.start_time, step_time
|
30
|
+
months = (t1.month - t0.month) +
|
31
|
+
(t1.year - t0.year) * 12
|
32
|
+
offset = (months % interval).nonzero?
|
33
|
+
interval - offset if offset
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_s(builder)
|
37
|
+
builder.base = IceCube::I18n.t('ice_cube.each_month', count: interval)
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_hash(builder)
|
41
|
+
builder[:interval] = interval
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_ical(builder)
|
45
|
+
builder['FREQ'] << 'MONTHLY'
|
46
|
+
builder['INTERVAL'] << interval unless interval == 1
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
module Validations::ScheduleLock
|
4
|
+
|
5
|
+
# Lock the given time units to the units from schedule's +start_time+
|
6
|
+
# These locks are all clobberable by other rules of the same #type
|
7
|
+
# using +clobber_base_validation+
|
8
|
+
#
|
9
|
+
def schedule_lock(*types)
|
10
|
+
types.each do |type|
|
11
|
+
validations_for(:"base_#{type}") << Validation.new(type)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Validation < Validations::FixedValue
|
16
|
+
|
17
|
+
attr_reader :type, :value
|
18
|
+
|
19
|
+
def initialize(type)
|
20
|
+
@type = type
|
21
|
+
end
|
22
|
+
|
23
|
+
def dst_adjust?
|
24
|
+
case @type
|
25
|
+
when :sec, :min then false
|
26
|
+
else true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# no -op
|
31
|
+
def build_s(builder)
|
32
|
+
end
|
33
|
+
|
34
|
+
# no -op
|
35
|
+
def build_hash(builder)
|
36
|
+
end
|
37
|
+
|
38
|
+
# no -op
|
39
|
+
def build_ical(builder)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|