ice_cube 0.16.2 → 0.16.4

Sign up to get free protection for your applications and to get access to all the features.
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'