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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/config/locales/en.yml +178 -0
  3. data/config/locales/es.yml +176 -0
  4. data/config/locales/ja.yml +107 -0
  5. data/lib/ice_cube.rb +92 -0
  6. data/lib/ice_cube/builders/hash_builder.rb +27 -0
  7. data/lib/ice_cube/builders/ical_builder.rb +59 -0
  8. data/lib/ice_cube/builders/string_builder.rb +76 -0
  9. data/lib/ice_cube/deprecated.rb +36 -0
  10. data/lib/ice_cube/errors/count_exceeded.rb +7 -0
  11. data/lib/ice_cube/errors/until_exceeded.rb +7 -0
  12. data/lib/ice_cube/flexible_hash.rb +40 -0
  13. data/lib/ice_cube/i18n.rb +24 -0
  14. data/lib/ice_cube/null_i18n.rb +28 -0
  15. data/lib/ice_cube/occurrence.rb +101 -0
  16. data/lib/ice_cube/parsers/hash_parser.rb +91 -0
  17. data/lib/ice_cube/parsers/ical_parser.rb +91 -0
  18. data/lib/ice_cube/parsers/yaml_parser.rb +19 -0
  19. data/lib/ice_cube/rule.rb +123 -0
  20. data/lib/ice_cube/rules/daily_rule.rb +16 -0
  21. data/lib/ice_cube/rules/hourly_rule.rb +16 -0
  22. data/lib/ice_cube/rules/minutely_rule.rb +16 -0
  23. data/lib/ice_cube/rules/monthly_rule.rb +16 -0
  24. data/lib/ice_cube/rules/secondly_rule.rb +15 -0
  25. data/lib/ice_cube/rules/weekly_rule.rb +16 -0
  26. data/lib/ice_cube/rules/yearly_rule.rb +16 -0
  27. data/lib/ice_cube/schedule.rb +529 -0
  28. data/lib/ice_cube/single_occurrence_rule.rb +28 -0
  29. data/lib/ice_cube/time_util.rb +328 -0
  30. data/lib/ice_cube/validated_rule.rb +184 -0
  31. data/lib/ice_cube/validations/count.rb +61 -0
  32. data/lib/ice_cube/validations/daily_interval.rb +54 -0
  33. data/lib/ice_cube/validations/day.rb +71 -0
  34. data/lib/ice_cube/validations/day_of_month.rb +55 -0
  35. data/lib/ice_cube/validations/day_of_week.rb +77 -0
  36. data/lib/ice_cube/validations/day_of_year.rb +61 -0
  37. data/lib/ice_cube/validations/fixed_value.rb +95 -0
  38. data/lib/ice_cube/validations/hour_of_day.rb +55 -0
  39. data/lib/ice_cube/validations/hourly_interval.rb +54 -0
  40. data/lib/ice_cube/validations/lock.rb +95 -0
  41. data/lib/ice_cube/validations/minute_of_hour.rb +54 -0
  42. data/lib/ice_cube/validations/minutely_interval.rb +54 -0
  43. data/lib/ice_cube/validations/month_of_year.rb +54 -0
  44. data/lib/ice_cube/validations/monthly_interval.rb +53 -0
  45. data/lib/ice_cube/validations/schedule_lock.rb +46 -0
  46. data/lib/ice_cube/validations/second_of_minute.rb +54 -0
  47. data/lib/ice_cube/validations/secondly_interval.rb +51 -0
  48. data/lib/ice_cube/validations/until.rb +57 -0
  49. data/lib/ice_cube/validations/weekly_interval.rb +67 -0
  50. data/lib/ice_cube/validations/yearly_interval.rb +53 -0
  51. data/lib/ice_cube/version.rb +5 -0
  52. data/spec/spec_helper.rb +64 -0
  53. 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