ice_cube 0.9.1 → 0.9.2

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.
@@ -1,7 +1,7 @@
1
1
  module IceCube
2
2
 
3
3
  class HashBuilder
4
-
4
+
5
5
  def initialize(rule = nil)
6
6
  @hash = { :validations => {}, :rule_type => rule.class.name }
7
7
  end
@@ -41,8 +41,8 @@ module IceCube
41
41
  class << self
42
42
 
43
43
  NUMBER_SUFFIX = ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th']
44
- SPECIAL_SUFFIX = { 11 => 'th', 12 => 'th', 13 => 'th', 14 => 'th' }
45
-
44
+ SPECIAL_SUFFIX = { 11 => 'th', 12 => 'th', 13 => 'th', 14 => 'th' }
45
+
46
46
  # influenced by ActiveSupport's to_sentence
47
47
  def sentence(array)
48
48
  case array.length
@@ -63,12 +63,12 @@ module IceCube
63
63
  else
64
64
  suffix = SPECIAL_SUFFIX.include?(number) ?
65
65
  SPECIAL_SUFFIX[number] : NUMBER_SUFFIX[number.abs % 10]
66
- number.to_s << suffix
66
+ number.to_s << suffix
67
67
  end
68
68
  end
69
69
 
70
70
  end
71
-
71
+
72
72
  end
73
73
 
74
74
  end
data/lib/ice_cube/rule.rb CHANGED
@@ -113,7 +113,7 @@ module IceCube
113
113
  end
114
114
 
115
115
  end
116
-
116
+
117
117
  end
118
118
 
119
119
  end
@@ -1,7 +1,7 @@
1
1
  module IceCube
2
2
 
3
3
  class SecondlyRule < ValidatedRule
4
-
4
+
5
5
  include Validations::SecondlyInterval
6
6
 
7
7
  def initialize(interval = 1)
@@ -7,27 +7,37 @@ module IceCube
7
7
  extend ::Deprecated
8
8
 
9
9
  # Get the start time
10
- attr_accessor :start_time
10
+ attr_reader :start_time
11
11
  deprecated_alias :start_date, :start_time
12
- deprecated_alias :start_date=, :start_time=
13
12
 
14
13
  # Get the duration
15
14
  attr_accessor :duration
16
15
 
17
16
  # Get the end time
18
- attr_accessor :end_time
17
+ attr_reader :end_time
19
18
  deprecated_alias :end_date, :end_time
20
- deprecated_alias :end_date=, :end_time=
21
19
 
22
20
  # Create a new schedule
23
21
  def initialize(start_time = nil, options = {})
24
- @start_time = start_time || TimeUtil.now
25
- @end_time = options[:end_time]
22
+ self.start_time = start_time || TimeUtil.now
23
+ self.end_time = options[:end_time]
26
24
  @duration = options[:duration]
27
25
  @all_recurrence_rules = []
28
26
  @all_exception_rules = []
29
27
  end
30
28
 
29
+ # Set start_time
30
+ def start_time=(start_time)
31
+ @start_time = TimeUtil.ensure_time start_time
32
+ end
33
+ deprecated_alias :start_date=, :start_time=
34
+
35
+ # Set end_time
36
+ def end_time=(end_time)
37
+ @end_time = TimeUtil.ensure_time end_time
38
+ end
39
+ deprecated_alias :end_date=, :end_time=
40
+
31
41
  # Add a recurrence time to the schedule
32
42
  def add_recurrence_time(time)
33
43
  return nil if time.nil?
@@ -145,17 +155,20 @@ module IceCube
145
155
  end
146
156
 
147
157
  # The next n occurrences after now
148
- def next_occurrences(num, from = TimeUtil.now)
158
+ def next_occurrences(num, from = nil)
159
+ from ||= TimeUtil.now(@start_time.utc?)
149
160
  find_occurrences(from + 1, nil, num)
150
161
  end
151
162
 
152
163
  # The next occurrence after now (overridable)
153
- def next_occurrence(from = TimeUtil.now)
164
+ def next_occurrence(from = nil)
165
+ from ||= TimeUtil.now(@start_time.utc?)
154
166
  find_occurrences(from + 1, nil, 1).first
155
167
  end
156
168
 
157
169
  # The remaining occurrences (same requirements as all_occurrences)
158
- def remaining_occurrences(from = TimeUtil.now)
170
+ def remaining_occurrences(from = nil)
171
+ from ||= TimeUtil.now(@start_time.utc?)
159
172
  find_occurrences(from)
160
173
  end
161
174
 
@@ -179,6 +192,7 @@ module IceCube
179
192
 
180
193
  # Return a boolean indicating if an occurrence falls on a certain date
181
194
  def occurs_on?(date)
195
+ date = TimeUtil.ensure_date date
182
196
  begin_time = TimeUtil.beginning_of_date(date)
183
197
  closing_time = TimeUtil.end_of_date(date)
184
198
  occurs_between?(begin_time, closing_time)
@@ -199,6 +213,7 @@ module IceCube
199
213
  # @param [Time] closing_time - the last time to consider
200
214
  # @return [Boolean] whether or not the schedules conflict at all
201
215
  def conflicts_with?(other_schedule, closing_time = nil)
216
+ closing_time = TimeUtil.ensure_time closing_time
202
217
  unless terminating? || other_schedule.terminating? || closing_time
203
218
  raise ArgumentError.new 'At least one schedule must be terminating to use #conflicts_with?'
204
219
  end
@@ -302,7 +317,7 @@ module IceCube
302
317
  schedule = IceCube::Schedule.new TimeUtil.deserialize_time(data[:start_date])
303
318
  schedule.duration = data[:duration] if data[:duration]
304
319
  schedule.end_time = TimeUtil.deserialize_time(data[:end_time]) if data[:end_time]
305
- data[:rrules] && data[:rrules].each { |h| schedule.rrule(IceCube::Rule.from_hash(h)) }
320
+ data[:rrules] && data[:rrules].each { |h| schedule.rrule(IceCube::Rule.from_hash(h)) }
306
321
  data[:exrules] && data[:exrules].each { |h| schedule.exrule(IceCube::Rule.from_hash(h)) }
307
322
  data[:rtimes] && data[:rtimes].each do |t|
308
323
  schedule.add_recurrence_time TimeUtil.deserialize_time(t)
@@ -345,6 +360,8 @@ module IceCube
345
360
  # Find all of the occurrences for the schedule between opening_time
346
361
  # and closing_time
347
362
  def find_occurrences(opening_time, closing_time = nil, limit = nil, &block)
363
+ opening_time = TimeUtil.ensure_time opening_time
364
+ closing_time = TimeUtil.ensure_time closing_time
348
365
  reset
349
366
  answers = []
350
367
  opening_time = start_time if opening_time < start_time
@@ -5,7 +5,7 @@ module IceCube
5
5
  attr_reader :time
6
6
 
7
7
  def initialize(time)
8
- @time = time
8
+ @time = TimeUtil.ensure_time time
9
9
  end
10
10
 
11
11
  # Always terminating
@@ -19,8 +19,27 @@ module IceCube
19
19
  }
20
20
 
21
21
  # Provides a Time.now without the usec
22
- def self.now
23
- Time.at Time.now.to_i
22
+ def self.now(utc = false)
23
+ time = Time.at(Time.now.to_i)
24
+ time = time.utc if utc
25
+ time
26
+ end
27
+
28
+ # Ensure that this is either nil, or a time
29
+ def self.ensure_time(time, date_eod = false)
30
+ case time
31
+ when DateTime then time.to_time
32
+ when Date then date_eod ? time.to_time.end_of_day : time.to_time
33
+ else time
34
+ end
35
+ end
36
+
37
+ # Ensure that this is either nil, or a date
38
+ def self.ensure_date(date)
39
+ case date
40
+ when Date then date
41
+ else date.to_time
42
+ end
24
43
  end
25
44
 
26
45
  # Serialize a time appropriate for storing
@@ -18,49 +18,21 @@ module IceCube
18
18
 
19
19
  # Compute the next time after (or including) the specified time in respect
20
20
  # to the given schedule
21
- # NOTE: optimization target, sort the rules by their type, year first
22
- # so we can make bigger jumps more often
23
21
  def next_time(time, schedule, closing_time)
24
- loop do
25
- break if @validations.all? do |name, vals|
26
- # Execute each validation
27
- res = vals.map do |validation|
28
- validation.validate(time, schedule)
29
- end
30
- # If there is any nil, then we're set - otherwise choose the lowest
31
- if res.any? { |r| r.nil? || r == 0 }
32
- true
33
- else
34
- return nil if res.all? { |r| r === true } # allow quick escaping
35
- res.reject! { |r| r.nil? || r == 0 || r === true }
36
- if fwd = res.min
37
- type = vals.first.type # get the jump type
38
- dst_adjust = !vals.first.respond_to?(:dst_adjust?) || vals.first.dst_adjust?
39
- wrapper = TimeUtil::TimeWrapper.new(time, dst_adjust)
40
- wrapper.add(type, fwd)
41
- wrapper.clear_below(type)
42
- # Move over DST if blocked, no adjustments
43
- if wrapper.to_time <= time
44
- wrapper = TimeUtil::TimeWrapper.new(wrapper.to_time, false)
45
- until wrapper.to_time > time
46
- wrapper.add(:min, 10) # smallest interval
47
- end
48
- end
49
- # And then get the correct time out
50
- time = wrapper.to_time
51
- end
52
- false
53
- end
54
- end
22
+ @time = time
23
+ @schedule = schedule
24
+
25
+ until finds_acceptable_time?
55
26
  # Prevent a non-matching infinite loop
56
- return nil if closing_time && time > closing_time
27
+ return nil if closing_time && @time > closing_time
57
28
  end
29
+
58
30
  # NOTE Uses may be 1 higher than proper here since end_time isn't
59
31
  # validated in this class. This is okay now, since we never expose it -
60
32
  # but if we ever do - we should check that above this line, and return
61
33
  # nil if end_time is past
62
- @uses += 1 if time
63
- time
34
+ @uses += 1 if @time
35
+ @time
64
36
  end
65
37
 
66
38
  def to_s
@@ -115,6 +87,55 @@ module IceCube
115
87
  end
116
88
  end
117
89
 
90
+ private
91
+
92
+ # NOTE: optimization target, sort the rules by their type, year first
93
+ # so we can make bigger jumps more often
94
+ def finds_acceptable_time?
95
+ @validations.all? do |name, validations_for_type|
96
+ validation_accepts_or_updates_time?(validations_for_type)
97
+ end
98
+ end
99
+
100
+ def validation_accepts_or_updates_time?(validations_for_type)
101
+ res = validated_results(validations_for_type)
102
+ # If there is any nil, then we're set - otherwise choose the lowest
103
+ if res.any? { |r| r.nil? || r == 0 }
104
+ true
105
+ else
106
+ return nil if res.all? { |r| r === true } # allow quick escaping
107
+ res.reject! { |r| r.nil? || r == 0 || r === true }
108
+ shift_time_by_validation(res, validations_for_type)
109
+ false
110
+ end
111
+ end
112
+
113
+ def validated_results(validations_for_type)
114
+ validations_for_type.map do |validation|
115
+ validation.validate(@time, @schedule)
116
+ end
117
+ end
118
+
119
+ def shift_time_by_validation(res, vals)
120
+ return unless res.min
121
+ type = vals.first.type # get the jump type
122
+ dst_adjust = !vals.first.respond_to?(:dst_adjust?) || vals.first.dst_adjust?
123
+ wrapper = TimeUtil::TimeWrapper.new(@time, dst_adjust)
124
+ wrapper.add(type, res.min)
125
+ wrapper.clear_below(type)
126
+
127
+ # Move over DST if blocked, no adjustments
128
+ if wrapper.to_time <= @time
129
+ wrapper = TimeUtil::TimeWrapper.new(wrapper.to_time, false)
130
+ until wrapper.to_time > @time
131
+ wrapper.add(:min, 10) # smallest interval
132
+ end
133
+ end
134
+
135
+ # And then get the correct time out
136
+ @time = wrapper.to_time
137
+ end
138
+
118
139
  end
119
140
 
120
141
  end
@@ -45,7 +45,7 @@ module IceCube
45
45
  end
46
46
 
47
47
  StringBuilder.register_formatter(:count) do |segments|
48
- count = segments.first
48
+ count = segments.first
49
49
  "#{count} #{count == 1 ? 'time' : 'times'}"
50
50
  end
51
51
 
@@ -3,7 +3,7 @@ require 'date'
3
3
  module IceCube
4
4
 
5
5
  module Validations::Day
6
-
6
+
7
7
  def day(*days)
8
8
  days.each do |day|
9
9
  day = TimeUtil.symbol_to_day(day) if day.is_a?(Symbol)
@@ -42,7 +42,7 @@ module IceCube
42
42
 
43
43
  def validate(time, schedule)
44
44
  start_time = schedule.start_time
45
- sec = (time.to_i - time.to_i % ONE_HOUR) -
45
+ sec = (time.to_i - time.to_i % ONE_HOUR) -
46
46
  (start_time.to_i - start_time.to_i % ONE_HOUR)
47
47
  hours = sec / ONE_HOUR
48
48
  unless hours % interval == 0
@@ -23,11 +23,11 @@ module IceCube
23
23
  # If this number is positive, then follow our normal procedure
24
24
  if start > 0
25
25
  return start >= time.day ? start - time.day : days_in_this_month - time.day + start
26
- end
26
+ end
27
27
  # If the number is negative, and it resolved against the current month
28
28
  # puts it in the future, just return the difference
29
29
  days_in_this_month = TimeUtil.days_in_month(time)
30
- start_one = days_in_this_month + start + 1
30
+ start_one = days_in_this_month + start + 1
31
31
  if start_one >= time.day
32
32
  return start_one - time.day
33
33
  end
@@ -11,6 +11,7 @@ module IceCube
11
11
  deprecated_alias :until_date, :until_time
12
12
 
13
13
  def until(time)
14
+ time = TimeUtil.ensure_time time, true
14
15
  @until = time
15
16
  replace_validations_for(:until, time.nil? ? nil : [Validation.new(time)])
16
17
  self
@@ -45,7 +45,7 @@ module IceCube
45
45
  st = schedule.start_time
46
46
  start_date = Date.new(st.year, st.month, st.day)
47
47
  weeks = (
48
- (date - TimeUtil.normalize_weekday(date.wday, week_start)) -
48
+ (date - TimeUtil.normalize_weekday(date.wday, week_start)) -
49
49
  (start_date - TimeUtil.normalize_weekday(start_date.wday, week_start))
50
50
  ) / 7
51
51
  unless weeks % interval == 0
@@ -1,5 +1,5 @@
1
1
  module IceCube
2
2
 
3
- VERSION = '0.9.1'
3
+ VERSION = '0.9.2'
4
4
 
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ice_cube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-19 00:00:00.000000000 Z
12
+ date: 2012-12-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec