ice_cube 0.16.2 → 0.16.4

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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/config/locales/de.yml +1 -1
  3. data/config/locales/fr.yml +2 -2
  4. data/config/locales/it.yml +179 -0
  5. data/config/locales/nl.yml +133 -0
  6. data/config/locales/sv.yml +1 -1
  7. data/lib/ice_cube/i18n.rb +11 -12
  8. data/lib/ice_cube/input_alignment.rb +89 -0
  9. data/lib/ice_cube/null_i18n.rb +12 -6
  10. data/lib/ice_cube/occurrence.rb +25 -23
  11. data/lib/ice_cube/parsers/ical_parser.rb +8 -5
  12. data/lib/ice_cube/rule.rb +4 -11
  13. data/lib/ice_cube/rules/daily_rule.rb +9 -0
  14. data/lib/ice_cube/rules/hourly_rule.rb +9 -0
  15. data/lib/ice_cube/rules/minutely_rule.rb +9 -0
  16. data/lib/ice_cube/rules/monthly_rule.rb +9 -0
  17. data/lib/ice_cube/rules/secondly_rule.rb +9 -0
  18. data/lib/ice_cube/rules/weekly_rule.rb +10 -1
  19. data/lib/ice_cube/rules/yearly_rule.rb +9 -0
  20. data/lib/ice_cube/schedule.rb +10 -9
  21. data/lib/ice_cube/single_occurrence_rule.rb +4 -0
  22. data/lib/ice_cube/time_util.rb +26 -16
  23. data/lib/ice_cube/validated_rule.rb +10 -19
  24. data/lib/ice_cube/validations/count.rb +1 -2
  25. data/lib/ice_cube/validations/daily_interval.rb +5 -1
  26. data/lib/ice_cube/validations/day.rb +6 -2
  27. data/lib/ice_cube/validations/day_of_month.rb +5 -0
  28. data/lib/ice_cube/validations/hour_of_day.rb +23 -0
  29. data/lib/ice_cube/validations/hourly_interval.rb +2 -0
  30. data/lib/ice_cube/validations/minute_of_hour.rb +16 -0
  31. data/lib/ice_cube/validations/minutely_interval.rb +2 -0
  32. data/lib/ice_cube/validations/month_of_year.rb +5 -0
  33. data/lib/ice_cube/validations/monthly_interval.rb +4 -1
  34. data/lib/ice_cube/validations/schedule_lock.rb +4 -0
  35. data/lib/ice_cube/validations/second_of_minute.rb +19 -3
  36. data/lib/ice_cube/validations/secondly_interval.rb +2 -0
  37. data/lib/ice_cube/validations/until.rb +1 -2
  38. data/lib/ice_cube/validations/weekly_interval.rb +0 -2
  39. data/lib/ice_cube/version.rb +1 -1
  40. data/lib/ice_cube.rb +1 -3
  41. data/spec/spec_helper.rb +32 -9
  42. metadata +9 -7
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class MinutelyRule < ValidatedRule
4
4
 
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
+
5
14
  include Validations::MinutelyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class MonthlyRule < ValidatedRule
4
4
 
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
+
5
14
  include Validations::MonthlyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class SecondlyRule < ValidatedRule
4
4
 
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
+
5
14
  include Validations::SecondlyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class WeeklyRule < ValidatedRule
4
4
 
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
+
5
14
  include Validations::WeeklyInterval
6
15
 
7
16
  attr_reader :week_start
@@ -28,7 +37,7 @@ module IceCube
28
37
  time = TimeUtil::TimeWrapper.new(start_time)
29
38
  offset = wday_offset(step_time, start_time)
30
39
  time.add(:day, offset)
31
- time.to_time
40
+ super step_time, time.to_time
32
41
  end
33
42
 
34
43
  # Calculate how many days to the first wday validation in the correct
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class YearlyRule < ValidatedRule
4
4
 
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
+
5
14
  include Validations::YearlyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -340,9 +340,9 @@ module IceCube
340
340
  IcalParser.schedule_from_ical(ical, options)
341
341
  end
342
342
 
343
- # Convert the schedule to yaml
344
- def to_yaml(*args)
345
- YAML::dump(to_hash, *args)
343
+ # Hook for YAML.dump, enables to_yaml
344
+ def encode_with(coder)
345
+ coder.represent_object nil, to_hash
346
346
  end
347
347
 
348
348
  # Load the schedule from yaml
@@ -371,6 +371,7 @@ module IceCube
371
371
  end
372
372
  data
373
373
  end
374
+ alias_method :to_h, :to_hash
374
375
 
375
376
  # Load the schedule from a hash
376
377
  def self.from_hash(original_hash, options = {})
@@ -383,7 +384,7 @@ module IceCube
383
384
  # Determine if the schedule will end
384
385
  # @return [Boolean] true if ending, false if repeating forever
385
386
  def terminating?
386
- recurrence_rules.empty? || recurrence_rules.all?(&:terminating?)
387
+ @all_recurrence_rules.all?(&:terminating?)
387
388
  end
388
389
 
389
390
  def hash
@@ -423,7 +424,7 @@ module IceCube
423
424
  def enumerate_occurrences(opening_time, closing_time = nil, options = {})
424
425
  opening_time = TimeUtil.match_zone(opening_time, start_time)
425
426
  closing_time = TimeUtil.match_zone(closing_time, start_time)
426
- opening_time += start_time.subsec - opening_time.subsec rescue 0
427
+ opening_time += TimeUtil.subsec(start_time) - TimeUtil.subsec(opening_time)
427
428
  opening_time = start_time if opening_time < start_time
428
429
  spans = options[:spans] == true && duration != 0
429
430
  Enumerator.new do |yielder|
@@ -445,12 +446,12 @@ module IceCube
445
446
  # Get the next time after (or including) a specific time
446
447
  def next_time(time, closing_time)
447
448
  loop do
448
- min_time = recurrence_rules_with_implicit_start_occurrence.reduce(nil) do |min_time, rule|
449
+ min_time = recurrence_rules_with_implicit_start_occurrence.reduce(nil) do |best_time, rule|
449
450
  begin
450
- new_time = rule.next_time(time, start_time, min_time || closing_time)
451
- [min_time, new_time].compact.min
451
+ new_time = rule.next_time(time, start_time, best_time || closing_time)
452
+ [best_time, new_time].compact.min
452
453
  rescue StopIteration
453
- min_time
454
+ best_time
454
455
  end
455
456
  end
456
457
  break unless min_time
@@ -23,6 +23,10 @@ module IceCube
23
23
  { :time => time }
24
24
  end
25
25
 
26
+ def full_required?
27
+ false
28
+ end
29
+
26
30
  end
27
31
 
28
32
  end
@@ -170,20 +170,13 @@ module IceCube
170
170
 
171
171
  # Convert wday number to day symbol
172
172
  def self.wday_to_sym(wday)
173
- return sym = wday if DAYS.keys.include? wday
173
+ return wday if DAYS.keys.include? wday
174
174
  DAYS.invert.fetch(wday) do |i|
175
175
  raise ArgumentError, "Expecting Integer value for weekday. " \
176
176
  "No such wday number: #{i.inspect}"
177
177
  end
178
178
  end
179
179
 
180
- # Convert a symbol to an ical day (SU, MO)
181
- def self.week_start(sym)
182
- raise ArgumentError, "Invalid day: #{str}" unless DAYS.keys.include?(sym)
183
- day = sym.to_s.upcase[0..1]
184
- day
185
- end
186
-
187
180
  # Convert weekday from base sunday to the schedule's week start.
188
181
  def self.normalize_wday(wday, week_start)
189
182
  (wday - sym_to_wday(week_start)) % 7
@@ -260,8 +253,19 @@ module IceCube
260
253
  end
261
254
  end
262
255
 
263
- def self.same_clock?(t1, t2)
264
- CLOCK_VALUES.all? { |i| t1.send(i) == t2.send(i) }
256
+ # Handle discrepancies between various time types
257
+ # - Time has subsec
258
+ # - DateTime does not
259
+ # - ActiveSupport::TimeWithZone can wrap either type, depending on version
260
+ # or if `parse` or `now`/`local` was used to build it.
261
+ def self.subsec(time)
262
+ if time.respond_to?(:subsec)
263
+ time.subsec
264
+ elsif time.respond_to?(:sec_fraction)
265
+ time.sec_fraction
266
+ else
267
+ 0.0
268
+ end
265
269
  end
266
270
 
267
271
  # A utility class for safely moving time around
@@ -271,7 +275,7 @@ module IceCube
271
275
  @dst_adjust = dst_adjust
272
276
  @base = time
273
277
  if dst_adjust
274
- @time = Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
278
+ @time = Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + TimeUtil.subsec(time))
275
279
  else
276
280
  @time = time
277
281
  end
@@ -307,7 +311,17 @@ module IceCube
307
311
  end
308
312
  end
309
313
 
310
- 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
311
325
 
312
326
  def clear_sec
313
327
  @time.sec > 0 ? @time -= @time.sec : @time
@@ -335,10 +349,6 @@ module IceCube
335
349
  @time += ONE_DAY
336
350
  end
337
351
 
338
- def clear_year
339
- @time
340
- end
341
-
342
352
  end
343
353
 
344
354
  end
@@ -1,18 +1,11 @@
1
+ require 'ice_cube/input_alignment'
2
+
1
3
  module IceCube
2
4
 
3
5
  class ValidatedRule < Rule
4
6
 
5
7
  include Validations::ScheduleLock
6
8
 
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
-
16
9
  include Validations::Count
17
10
  include Validations::Until
18
11
 
@@ -51,10 +44,6 @@ module IceCube
51
44
  Array(@validations[base_interval_validation.type])
52
45
  end
53
46
 
54
- def base_interval_type
55
- base_interval_validation.type
56
- end
57
-
58
47
  # Compute the next time after (or including) the specified time in respect
59
48
  # to the given start time
60
49
  def next_time(time, start_time, closing_time)
@@ -74,12 +63,8 @@ module IceCube
74
63
  start_time
75
64
  end
76
65
 
77
- def skipped_for_dst
78
- @uses -= 1 if @uses > 0
79
- end
80
-
81
- def dst_adjust?
82
- @validations[:interval].any?(&:dst_adjust?)
66
+ def full_required?
67
+ !occurrence_count.nil?
83
68
  end
84
69
 
85
70
  def to_s
@@ -193,6 +178,12 @@ module IceCube
193
178
  VALIDATION_ORDER & @validations.keys
194
179
  end
195
180
 
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
+
196
187
  end
197
188
 
198
189
  end
@@ -4,14 +4,13 @@ module IceCube
4
4
 
5
5
  # Value reader for limit
6
6
  def occurrence_count
7
- @count
7
+ (arr = @validations[:count]) && (val = arr[0]) && val.count
8
8
  end
9
9
 
10
10
  def count(max)
11
11
  unless max.nil? || max.is_a?(Integer)
12
12
  raise ArgumentError, "Expecting Integer or nil value for count, got #{max.inspect}"
13
13
  end
14
- @count = max
15
14
  replace_validations_for(:count, max && [Validation.new(max, self)])
16
15
  self
17
16
  end
@@ -4,7 +4,11 @@ module IceCube
4
4
 
5
5
  # Add a new interval validation
6
6
  def interval(interval)
7
- @interval = normalized_interval(interval)
7
+ interval = normalized_interval(interval)
8
+ verify_alignment(interval, :wday, :interval) { |error| raise error }
9
+ verify_alignment(interval, :day, :interval) { |error| raise error }
10
+
11
+ @interval = interval
8
12
  replace_validations_for(:interval, [Validation.new(@interval)])
9
13
  clobber_base_validations(:wday, :day)
10
14
  self
@@ -1,5 +1,3 @@
1
- require 'date'
2
-
3
1
  module IceCube
4
2
 
5
3
  module Validations::Day
@@ -12,6 +10,8 @@ module IceCube
12
10
  raise ArgumentError, "expecting Integer or Symbol value for day, got #{day.inspect}"
13
11
  end
14
12
  day = TimeUtil.sym_to_wday(day)
13
+ verify_alignment(day, :wday, :day) { |error| raise error }
14
+
15
15
  validations_for(:day) << Validation.new(day)
16
16
  end
17
17
  clobber_base_validations(:wday, :day)
@@ -27,6 +27,10 @@ module IceCube
27
27
  @day = day
28
28
  end
29
29
 
30
+ def key
31
+ :day
32
+ end
33
+
30
34
  def type
31
35
  :wday
32
36
  end
@@ -7,6 +7,7 @@ module IceCube
7
7
  unless day.is_a?(Integer)
8
8
  raise ArgumentError, "expecting Integer value for day, got #{day.inspect}"
9
9
  end
10
+ verify_alignment(day, :day, :day_of_month) { |error| raise error }
10
11
  validations_for(:day_of_month) << Validation.new(day)
11
12
  end
12
13
  clobber_base_validations(:day, :wday)
@@ -22,6 +23,10 @@ module IceCube
22
23
  @day = day
23
24
  end
24
25
 
26
+ def key
27
+ :day_of_month
28
+ end
29
+
25
30
  def type
26
31
  :day
27
32
  end
@@ -8,12 +8,31 @@ module IceCube
8
8
  unless hour.is_a?(Integer)
9
9
  raise ArgumentError, "expecting Integer value for hour, got #{hour.inspect}"
10
10
  end
11
+
12
+ verify_alignment(hour, :hour, :hour_of_day) { |error| raise error }
13
+
11
14
  validations_for(:hour_of_day) << Validation.new(hour)
12
15
  end
13
16
  clobber_base_validations(:hour)
14
17
  self
15
18
  end
16
19
 
20
+ def realign(opening_time, start_time)
21
+ return super unless validations[:hour_of_day]
22
+ freq = base_interval_validation.interval
23
+
24
+ first_hour = Array(validations[:hour_of_day]).min_by(&:value)
25
+ time = TimeUtil::TimeWrapper.new(start_time, false)
26
+ if freq > 1 && base_interval_validation.type == :hour
27
+ offset = first_hour.validate(opening_time, start_time)
28
+ time.add(:hour, offset - freq)
29
+ else
30
+ time.hour = first_hour.value
31
+ end
32
+
33
+ super opening_time, time.to_time
34
+ end
35
+
17
36
  class Validation < Validations::FixedValue
18
37
 
19
38
  attr_reader :hour
@@ -23,6 +42,10 @@ module IceCube
23
42
  @hour = hour
24
43
  end
25
44
 
45
+ def key
46
+ :hour_of_day
47
+ end
48
+
26
49
  def type
27
50
  :hour
28
51
  end
@@ -3,6 +3,8 @@ module IceCube
3
3
  module Validations::HourlyInterval
4
4
 
5
5
  def interval(interval)
6
+ verify_alignment(interval, :hour, :interval) { |error| raise error }
7
+
6
8
  @interval = normalized_interval(interval)
7
9
  replace_validations_for(:interval, [Validation.new(@interval)])
8
10
  clobber_base_validations(:hour)
@@ -7,12 +7,24 @@ module IceCube
7
7
  unless minute.is_a?(Integer)
8
8
  raise ArgumentError, "expecting Integer value for minute, got #{minute.inspect}"
9
9
  end
10
+
11
+ verify_alignment(minute, :min, :minute_of_hour) { |error| raise error }
12
+
10
13
  validations_for(:minute_of_hour) << Validation.new(minute)
11
14
  end
12
15
  clobber_base_validations(:min)
13
16
  self
14
17
  end
15
18
 
19
+ def realign(opening_time, start_time)
20
+ return super unless validations[:minute_of_hour]
21
+
22
+ first_minute = validations[:minute_of_hour].min_by(&:value)
23
+ time = TimeUtil::TimeWrapper.new(start_time, false)
24
+ time.min = first_minute.value
25
+ super opening_time, time.to_time
26
+ end
27
+
16
28
  class Validation < Validations::FixedValue
17
29
 
18
30
  attr_reader :minute
@@ -22,6 +34,10 @@ module IceCube
22
34
  @minute = minute
23
35
  end
24
36
 
37
+ def key
38
+ :minute_of_hour
39
+ end
40
+
25
41
  def type
26
42
  :min
27
43
  end
@@ -3,6 +3,8 @@ module IceCube
3
3
  module Validations::MinutelyInterval
4
4
 
5
5
  def interval(interval)
6
+ verify_alignment(interval, :min, :interval) { |error| raise error }
7
+
6
8
  @interval = normalized_interval(interval)
7
9
  replace_validations_for(:interval, [Validation.new(@interval)])
8
10
  clobber_base_validations(:min)
@@ -8,6 +8,7 @@ module IceCube
8
8
  raise ArgumentError, "expecting Integer or Symbol value for month, got #{month.inspect}"
9
9
  end
10
10
  month = TimeUtil.sym_to_month(month)
11
+ verify_alignment(month, :month, :month_of_year) { |error| raise error }
11
12
  validations_for(:month_of_year) << Validation.new(month)
12
13
  end
13
14
  clobber_base_validations :month
@@ -23,6 +24,10 @@ module IceCube
23
24
  @month = month
24
25
  end
25
26
 
27
+ def key
28
+ :month_of_year
29
+ end
30
+
26
31
  def type
27
32
  :month
28
33
  end
@@ -3,7 +3,10 @@ module IceCube
3
3
  module Validations::MonthlyInterval
4
4
 
5
5
  def interval(interval)
6
- @interval = normalized_interval(interval)
6
+ interval = normalized_interval(interval)
7
+ verify_alignment(interval, :month, :interval) { |error| raise error }
8
+
9
+ @interval = interval
7
10
  replace_validations_for(:interval, [Validation.new(@interval)])
8
11
  clobber_base_validations(:month)
9
12
  self
@@ -20,6 +20,10 @@ module IceCube
20
20
  @type = type
21
21
  end
22
22
 
23
+ def key
24
+ :base
25
+ end
26
+
23
27
  def dst_adjust?
24
28
  case @type
25
29
  when :sec, :min then false
@@ -4,15 +4,27 @@ module IceCube
4
4
 
5
5
  def second_of_minute(*seconds)
6
6
  seconds.flatten.each do |second|
7
- unless second.is_a?(Integer)
8
- raise ArgumentError, "Expecting Integer value for second, got #{second.inspect}"
9
- end
7
+ unless second.is_a?(Integer)
8
+ raise ArgumentError, "Expecting Integer value for second, got #{second.inspect}"
9
+ end
10
+
11
+ verify_alignment(second, :sec, :second_of_minute) { |error| raise error }
12
+
10
13
  validations_for(:second_of_minute) << Validation.new(second)
11
14
  end
12
15
  clobber_base_validations :sec
13
16
  self
14
17
  end
15
18
 
19
+ def realign(opening_time, start_time)
20
+ return super unless validations[:second_of_minute]
21
+
22
+ first_second = Array(validations[:second_of_minute]).min_by(&:value)
23
+ time = TimeUtil::TimeWrapper.new(start_time, false)
24
+ time.sec = first_second.value
25
+ super opening_time, time.to_time
26
+ end
27
+
16
28
  class Validation < Validations::FixedValue
17
29
 
18
30
  attr_reader :second
@@ -22,6 +34,10 @@ module IceCube
22
34
  @second = second
23
35
  end
24
36
 
37
+ def key
38
+ :second_of_minute
39
+ end
40
+
25
41
  def type
26
42
  :sec
27
43
  end
@@ -3,6 +3,8 @@ module IceCube
3
3
  module Validations::SecondlyInterval
4
4
 
5
5
  def interval(interval)
6
+ verify_alignment(interval, :sec, :interval) { |error| raise error }
7
+
6
8
  @interval = normalized_interval(interval)
7
9
  replace_validations_for(:interval, [Validation.new(@interval)])
8
10
  clobber_base_validations(:sec)
@@ -6,12 +6,11 @@ module IceCube
6
6
 
7
7
  # Value reader for limit
8
8
  def until_time
9
- @until
9
+ (arr = @validations[:until]) && (val = arr[0]) && val.time
10
10
  end
11
11
  deprecated_alias :until_date, :until_time
12
12
 
13
13
  def until(time)
14
- @until = time
15
14
  replace_validations_for(:until, time.nil? ? nil : [Validation.new(time)])
16
15
  self
17
16
  end
@@ -1,5 +1,3 @@
1
- require 'date'
2
-
3
1
  module IceCube
4
2
 
5
3
  module Validations::WeeklyInterval
@@ -1,5 +1,5 @@
1
1
  module IceCube
2
2
 
3
- VERSION = '0.16.2'
3
+ VERSION = '0.16.4'
4
4
 
5
5
  end
data/lib/ice_cube.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  require 'date'
2
2
  require 'ice_cube/deprecated'
3
- require 'ice_cube/i18n'
4
-
5
- IceCube::I18n.detect_backend!
6
3
 
7
4
  module IceCube
8
5
 
@@ -10,6 +7,7 @@ module IceCube
10
7
 
11
8
  autoload :TimeUtil, 'ice_cube/time_util'
12
9
  autoload :FlexibleHash, 'ice_cube/flexible_hash'
10
+ autoload :I18n, 'ice_cube/i18n'
13
11
 
14
12
  autoload :Rule, 'ice_cube/rule'
15
13
  autoload :Schedule, 'ice_cube/schedule'