ice_cube 0.9.1 → 0.9.2

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