ice_cube_chosko 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
1
+ sv:
2
+ ice_cube:
3
+ pieces_connector: ' / '
4
+ not: 'inte %{target}'
5
+ not_on: 'inte i %{target}'
6
+ date:
7
+ formats:
8
+ default: '%-d %B %Y'
9
+ month_names:
10
+ -
11
+ - januari
12
+ - februari
13
+ - mars
14
+ - april
15
+ - maj
16
+ - juni
17
+ - juli
18
+ - augusti
19
+ - september
20
+ - oktober
21
+ - november
22
+ - december
23
+ day_names:
24
+ - söndag
25
+ - måndag
26
+ - tisdag
27
+ - onsdag
28
+ - torsdag
29
+ - fredag
30
+ - lördag
31
+ times:
32
+ other: '%{count} gånger'
33
+ one: '%{count} gång'
34
+ until: 'tills %{date}'
35
+ days_of_week: '%{segments} %{day}'
36
+ days_of_month:
37
+ other: '%{segments} dagen i månaden'
38
+ one: '%{segments} dagen i månaden'
39
+ days_of_year:
40
+ other: '%{segments} dagen på året'
41
+ one: '%{segments} dagen på året'
42
+ at_hours_of_the_day:
43
+ other: i %{segments} timme på dygnet
44
+ one: i %{segments} timme på dygnet
45
+ on_minutes_of_hour:
46
+ other: i %{segments} minuter av timmen
47
+ one: i %{segments} minuten av timmen
48
+ on_seconds_of_minute:
49
+ other: på %{segments} sekunden av minuten
50
+ one: på %{segments} sekunden av minuten
51
+ each_second:
52
+ one: Varje sekund
53
+ other: Varje %{count} sekund
54
+ each_minute:
55
+ one: Varje minut
56
+ other: Varje %{count} minut
57
+ each_hour:
58
+ one: Varje timme
59
+ other: Varje %{count} timme
60
+ each_day:
61
+ one: Varje dag
62
+ other: Varje %{count} dag
63
+ each_week:
64
+ one: Varje vecka
65
+ other: Varje %{count} vecka
66
+ each_month:
67
+ one: Varje månad
68
+ other: Varje %{count} månad
69
+ each_year:
70
+ one: Varje år
71
+ other: Varje %{count} år
72
+ 'on': i %{sentence}
73
+ in: 'efter %{target}'
74
+ integer:
75
+ negative: '%{ordinal} från slutet'
76
+ literal_ordinals:
77
+ -1: sista
78
+ -2: nästsista
79
+ ordinal: '%{number}%{ordinal}'
80
+ ordinals:
81
+ default: ''
82
+ on_weekends: på helger
83
+ on_weekdays: på vardagar
84
+ days_on:
85
+ - söndagar
86
+ - måndagar
87
+ - tisdagar
88
+ - onsdagar
89
+ - torsdagar
90
+ - fredagar
91
+ - lördagar
92
+ on_days: på %{days}
93
+ array:
94
+ last_word_connector: ' och '
95
+ two_words_connector: ' och '
96
+ words_connector: ', '
97
+ string:
98
+ format:
99
+ day: '%{rest} %{current}'
100
+ day_of_week: '%{rest} %{current}'
101
+ day_of_month: '%{rest} %{current}'
102
+ day_of_year: '%{rest} %{current}'
103
+ hour_of_day: '%{rest} %{current}'
104
+ minute_of_hour: '%{rest} %{current}'
105
+ until: '%{rest} %{current}'
106
+ count: '%{rest} %{current}'
107
+ default: '%{rest} %{current}'
108
+
109
+ date:
110
+ abbr_day_names:
111
+ - Sön
112
+ - Mån
113
+ - Tis
114
+ - Ons
115
+ - Tor
116
+ - Fre
117
+ - Lör
118
+ abbr_month_names:
119
+ -
120
+ - Jan
121
+ - Feb
122
+ - Mar
123
+ - Apr
124
+ - Maj
125
+ - Jun
126
+ - Jul
127
+ - Aug
128
+ - Sep
129
+ - Okt
130
+ - Mov
131
+ - Dec
132
+ day_names:
133
+ - Söndag
134
+ - Måndag
135
+ - Tisdag
136
+ - Onsdag
137
+ - Torsdag
138
+ - Fredag
139
+ - Lördag
140
+ formats:
141
+ default: "%Y-%m-%d"
142
+ long: "%e %B %Y"
143
+ short: "%e %b"
144
+ month_names:
145
+ -
146
+ - Januari
147
+ - Februari
148
+ - Mars
149
+ - April
150
+ - Maj
151
+ - Juni
152
+ - Juli
153
+ - Augusti
154
+ - September
155
+ - Oktober
156
+ - November
157
+ - December
158
+ order:
159
+ - :day
160
+ - :month
161
+ - :year
162
+
163
+ time:
164
+ am: ''
165
+ formats:
166
+ default: "%a, %e %b %Y %H:%M:%S %z"
167
+ long: "%e %B %Y %H:%M"
168
+ short: "%e %b %H:%M"
169
+ pm: ''
data/lib/ice_cube.rb CHANGED
@@ -43,6 +43,7 @@ module IceCube
43
43
 
44
44
  autoload :Count, 'ice_cube/validations/count'
45
45
  autoload :Until, 'ice_cube/validations/until'
46
+ autoload :OverrideDuration, 'ice_cube/validations/override_duration'
46
47
 
47
48
  autoload :SecondlyInterval, 'ice_cube/validations/secondly_interval'
48
49
  autoload :MinutelyInterval, 'ice_cube/validations/minutely_interval'
@@ -24,14 +24,18 @@ module IceCube
24
24
  end
25
25
 
26
26
  def self.rule_from_ical(ical)
27
- params = { validations: { } }
27
+ raise ArgumentError, 'empty ical rule' if ical.nil?
28
+
29
+ validations = {}
30
+ params = {validations: validations, interval: 1}
28
31
 
29
32
  ical.split(';').each do |rule|
30
33
  (name, value) = rule.split('=')
34
+ raise ArgumentError, "Invalid iCal rule component" if value.nil?
31
35
  value.strip!
32
36
  case name
33
37
  when 'FREQ'
34
- params[:freq] = value.downcase
38
+ params[:rule_type] = "IceCube::#{value[0]}#{value.downcase[1..-1]}Rule"
35
39
  when 'INTERVAL'
36
40
  params[:interval] = value.to_i
37
41
  when 'COUNT'
@@ -39,13 +43,13 @@ module IceCube
39
43
  when 'UNTIL'
40
44
  params[:until] = Time.parse(value).utc
41
45
  when 'WKST'
42
- params[:wkst] = TimeUtil.ical_day_to_symbol(value)
46
+ params[:week_start] = TimeUtil.ical_day_to_symbol(value)
43
47
  when 'BYSECOND'
44
- params[:validations][:second_of_minute] = value.split(',').collect(&:to_i)
48
+ validations[:second_of_minute] = value.split(',').map(&:to_i)
45
49
  when 'BYMINUTE'
46
- params[:validations][:minute_of_hour] = value.split(',').collect(&:to_i)
50
+ validations[:minute_of_hour] = value.split(',').map(&:to_i)
47
51
  when 'BYHOUR'
48
- params[:validations][:hour_of_day] = value.split(',').collect(&:to_i)
52
+ validations[:hour_of_day] = value.split(',').map(&:to_i)
49
53
  when 'BYDAY'
50
54
  dows = {}
51
55
  days = []
@@ -59,33 +63,21 @@ module IceCube
59
63
  days.push TimeUtil.sym_to_wday(day) if dows[day].nil?
60
64
  end
61
65
  end
62
- params[:validations][:day_of_week] = dows unless dows.empty?
63
- params[:validations][:day] = days unless days.empty?
66
+ validations[:day_of_week] = dows unless dows.empty?
67
+ validations[:day] = days unless days.empty?
64
68
  when 'BYMONTHDAY'
65
- params[:validations][:day_of_month] = value.split(',').collect(&:to_i)
69
+ validations[:day_of_month] = value.split(',').map(&:to_i)
66
70
  when 'BYMONTH'
67
- params[:validations][:month_of_year] = value.split(',').collect(&:to_i)
71
+ validations[:month_of_year] = value.split(',').map(&:to_i)
68
72
  when 'BYYEARDAY'
69
- params[:validations][:day_of_year] = value.split(',').collect(&:to_i)
73
+ validations[:day_of_year] = value.split(',').map(&:to_i)
70
74
  when 'BYSETPOS'
71
75
  else
72
- raise "Invalid or unsupported rrule command: #{name}"
76
+ validations[name] = nil # invalid type
73
77
  end
74
78
  end
75
79
 
76
- params[:interval] ||= 1
77
-
78
- # WKST only valid for weekly rules
79
- params.delete(:wkst) unless params[:freq] == 'weekly'
80
-
81
- rule = Rule.send(*params.values_at(:freq, :interval, :wkst).compact)
82
- rule.count(params[:count]) if params[:count]
83
- rule.until(params[:until]) if params[:until]
84
- params[:validations].each do |key, value|
85
- value.is_a?(Array) ? rule.send(key, *value) : rule.send(key, value)
86
- end
87
-
88
- rule
80
+ Rule.from_hash(params)
89
81
  end
90
82
  end
91
83
  end
data/lib/ice_cube/rule.rb CHANGED
@@ -4,6 +4,11 @@ module IceCube
4
4
 
5
5
  class Rule
6
6
 
7
+ INTERVAL_TYPES = [
8
+ :secondly, :minutely, :hourly,
9
+ :daily, :weekly, :monthly, :yearly
10
+ ]
11
+
7
12
  attr_reader :uses
8
13
 
9
14
  # Is this a terminating schedule?
@@ -11,6 +16,11 @@ module IceCube
11
16
  until_time || occurrence_count
12
17
  end
13
18
 
19
+ # Does this rule override the schedule's duration?
20
+ def overrides_duration?
21
+ !duration.nil?
22
+ end
23
+
14
24
  def ==(rule)
15
25
  if rule.is_a? Rule
16
26
  hash = to_hash
@@ -46,21 +56,6 @@ module IceCube
46
56
  raise MethodNotImplemented, "Expected to be overridden by subclasses"
47
57
  end
48
58
 
49
- # Convert from a hash and create a rule
50
- def self.from_hash(original_hash)
51
- hash = IceCube::FlexibleHash.new original_hash
52
- return nil unless match = hash[:rule_type].match(/\:\:(.+?)Rule/)
53
- rule = IceCube::Rule.send(match[1].downcase.to_sym, hash[:interval] || 1)
54
- rule.interval(hash[:interval] || 1, TimeUtil.wday_to_sym(hash[:week_start] || 0)) if match[1] == "Weekly"
55
- rule.until(TimeUtil.deserialize_time(hash[:until])) if hash[:until]
56
- rule.count(hash[:count]) if hash[:count]
57
- hash[:validations] && hash[:validations].each do |key, value|
58
- key = key.to_sym unless key.is_a?(Symbol)
59
- value.is_a?(Array) ? rule.send(key, *value) : rule.send(key, value)
60
- end
61
- rule
62
- end
63
-
64
59
  # Reset the uses on the rule to 0
65
60
  def reset
66
61
  @uses = 0
@@ -78,6 +73,52 @@ module IceCube
78
73
  !@count.nil?
79
74
  end
80
75
 
76
+ class << self
77
+
78
+ # Convert from a hash and create a rule
79
+ def from_hash(original_hash)
80
+ hash = IceCube::FlexibleHash.new original_hash
81
+
82
+ unless hash[:rule_type] && match = hash[:rule_type].match(/\:\:(.+?)Rule/)
83
+ raise ArgumentError, 'Invalid rule type'
84
+ end
85
+
86
+ interval_type = match[1].downcase.to_sym
87
+
88
+ unless INTERVAL_TYPES.include?(interval_type)
89
+ raise ArgumentError, "Invalid rule frequency type: #{match[1]}"
90
+ end
91
+
92
+ rule = IceCube::Rule.send(interval_type, hash[:interval] || 1)
93
+
94
+ if match[1] == "Weekly"
95
+ rule.interval(hash[:interval] || 1, TimeUtil.wday_to_sym(hash[:week_start] || 0))
96
+ end
97
+
98
+ rule.until(TimeUtil.deserialize_time(hash[:until])) if hash[:until]
99
+ rule.count(hash[:count]) if hash[:count]
100
+
101
+ hash[:validations] && hash[:validations].each do |name, args|
102
+ apply_validation(rule, name, args)
103
+ end
104
+
105
+ rule
106
+ end
107
+
108
+ private
109
+
110
+ def apply_validation(rule, name, args)
111
+ name = name.to_sym
112
+
113
+ unless ValidatedRule::VALIDATION_ORDER.include?(name)
114
+ raise ArgumentError, "Invalid rule validation type: #{name}"
115
+ end
116
+
117
+ args.is_a?(Array) ? rule.send(name, *args) : rule.send(name, args)
118
+ end
119
+
120
+ end
121
+
81
122
  # Convenience methods for creating Rules
82
123
  class << self
83
124
 
@@ -44,6 +44,16 @@ module IceCube
44
44
  @end_time = start_time + seconds
45
45
  end
46
46
 
47
+ # Return the maximum value between schedule's duration and
48
+ # the durations of all the recurrence and exception rules included
49
+ # in this schedule
50
+ def max_duration
51
+ all_durations = [duration]
52
+ all_durations += @all_recurrence_rules.map { |r| r.duration }.compact
53
+ all_durations += @all_exception_rules.map { |r| r.duration }.compact
54
+ all_durations.max
55
+ end
56
+
47
57
  # Add a recurrence time to the schedule
48
58
  def add_recurrence_time(time)
49
59
  return nil if time.nil?
@@ -166,55 +176,55 @@ module IceCube
166
176
  end
167
177
 
168
178
  # The next n occurrences after now
169
- def next_occurrences(num, from = nil, spans = false)
179
+ def next_occurrences(num, from = nil, options = {})
170
180
  from = TimeUtil.match_zone(from, start_time) || TimeUtil.now(start_time)
171
- enumerate_occurrences(from + 1, nil, spans).take(num)
181
+ enumerate_occurrences(from + 1, nil, options).take(num)
172
182
  end
173
183
 
174
184
  # The next occurrence after now (overridable)
175
- def next_occurrence(from = nil, spans = false)
185
+ def next_occurrence(from = nil, options = {})
176
186
  from = TimeUtil.match_zone(from, start_time) || TimeUtil.now(start_time)
177
- enumerate_occurrences(from + 1, nil, spans).next
187
+ enumerate_occurrences(from + 1, nil, options).next
178
188
  rescue StopIteration
179
189
  nil
180
190
  end
181
191
 
182
192
  # The previous occurrence from a given time
183
193
  def previous_occurrence(from)
184
- from = TimeUtil.match_zone(from, start_time) or raise ArgumentError, "Time required, got #{time.inspect}"
194
+ from = TimeUtil.match_zone(from, start_time) or raise ArgumentError, "Time required, got #{from.inspect}"
185
195
  return nil if from <= start_time
186
196
  enumerate_occurrences(start_time, from - 1).to_a.last
187
197
  end
188
198
 
189
199
  # The previous n occurrences before a given time
190
200
  def previous_occurrences(num, from)
191
- from = TimeUtil.match_zone(from, start_time) or raise ArgumentError, "Time required, got #{time.inspect}"
201
+ from = TimeUtil.match_zone(from, start_time) or raise ArgumentError, "Time required, got #{from.inspect}"
192
202
  return [] if from <= start_time
193
203
  a = enumerate_occurrences(start_time, from - 1).to_a
194
204
  a.size > num ? a[-1*num,a.size] : a
195
205
  end
196
206
 
197
207
  # The remaining occurrences (same requirements as all_occurrences)
198
- def remaining_occurrences(from = nil, spans = false)
208
+ def remaining_occurrences(from = nil, options = {})
199
209
  require_terminating_rules
200
210
  from ||= TimeUtil.now(@start_time)
201
- enumerate_occurrences(from, nil, spans).to_a
211
+ enumerate_occurrences(from, nil, options).to_a
202
212
  end
203
213
 
204
214
  # Returns an enumerator for all remaining occurrences
205
- def remaining_occurrences_enumerator(from = nil, spans = false)
215
+ def remaining_occurrences_enumerator(from = nil, options = {})
206
216
  from ||= TimeUtil.now(@start_time)
207
- enumerate_occurrences(from, nil, spans)
217
+ enumerate_occurrences(from, nil, options)
208
218
  end
209
219
 
210
220
  # Occurrences between two times
211
- def occurrences_between(begin_time, closing_time, spans = false)
212
- enumerate_occurrences(begin_time, closing_time, spans).to_a
221
+ def occurrences_between(begin_time, closing_time, options = {})
222
+ enumerate_occurrences(begin_time, closing_time, options).to_a
213
223
  end
214
224
 
215
225
  # Return a boolean indicating if an occurrence falls between two times
216
- def occurs_between?(begin_time, closing_time, spans = false)
217
- enumerate_occurrences(begin_time, closing_time, spans).next
226
+ def occurs_between?(begin_time, closing_time, options = {})
227
+ enumerate_occurrences(begin_time, closing_time, options).next
218
228
  true
219
229
  rescue StopIteration
220
230
  false
@@ -226,7 +236,7 @@ module IceCube
226
236
  # occurrences at the end of the range since none of their duration
227
237
  # intersects the range.
228
238
  def occurring_between?(opening_time, closing_time)
229
- occurs_between?(opening_time, closing_time, true)
239
+ occurs_between?(opening_time, closing_time, :spans => true)
230
240
  end
231
241
 
232
242
  # Return a boolean indicating if an occurrence falls on a certain date
@@ -240,9 +250,14 @@ module IceCube
240
250
  # Determine if the schedule is occurring at a given time
241
251
  def occurring_at?(time)
242
252
  time = TimeUtil.match_zone(time, start_time) or raise ArgumentError, "Time required, got #{time.inspect}"
243
- if duration > 0
253
+ if max_duration > 0
244
254
  return false if exception_time?(time)
245
- occurs_between?(time - duration + 1, time)
255
+ begin
256
+ enumerate_occurrences(time, time, :spans => true).next
257
+ true
258
+ rescue StopIteration
259
+ false
260
+ end
246
261
  else
247
262
  occurs_at?(time)
248
263
  end
@@ -405,15 +420,15 @@ module IceCube
405
420
  # Find all of the occurrences for the schedule between opening_time
406
421
  # and closing_time
407
422
  # Iteration is unrolled in pairs to skip duplicate times in end of DST
408
- def enumerate_occurrences(opening_time, closing_time = nil, spans = false, &block)
423
+ def enumerate_occurrences(opening_time, closing_time = nil, options = {}, &block)
409
424
  opening_time = TimeUtil.match_zone(opening_time, start_time)
410
425
  closing_time = TimeUtil.match_zone(closing_time, start_time)
411
426
  opening_time += start_time.subsec - opening_time.subsec rescue 0
412
427
  opening_time = start_time if opening_time < start_time
413
- spans = false if duration == 0
428
+ spans = options[:spans] == true && max_duration != 0
414
429
  Enumerator.new do |yielder|
415
430
  reset
416
- t1 = full_required? ? start_time : realign((spans ? opening_time - duration : opening_time))
431
+ t1 = full_required? ? start_time : realign(opening_time) - (spans ? max_duration : 0)
417
432
  loop do
418
433
  break unless (t0 = next_time(t1, closing_time))
419
434
  break if closing_time && t0 > closing_time
@@ -437,17 +452,30 @@ module IceCube
437
452
  # Get the next time after (or including) a specific time
438
453
  def next_time(time, closing_time)
439
454
  loop do
440
- min_time = recurrence_rules_with_implicit_start_occurrence.reduce(nil) do |min_time, rule|
455
+ min_time_with_duration = recurrence_rules_with_implicit_start_occurrence.reduce(nil) do |min_time_with_duration, rule|
456
+ min_time = (min_time_with_duration && min_time_with_duration[0]) || nil
441
457
  begin
442
458
  new_time = rule.next_time(time, self, min_time || closing_time)
443
- [min_time, new_time].compact.min
459
+ new_time_with_duration = [new_time, rule.duration]
460
+
461
+ if new_time.nil?
462
+ min_time_with_duration
463
+ elsif min_time.nil?
464
+ new_time_with_duration
465
+ elsif new_time < min_time
466
+ new_time_with_duration
467
+ else
468
+ min_time_with_duration
469
+ end
444
470
  rescue StopIteration
445
- min_time
471
+ (min_time && [min_time, rule.duration]) || nil
446
472
  end
447
473
  end
474
+ min_time = (min_time_with_duration && min_time_with_duration[0]) || nil
475
+ rule_duration = (min_time_with_duration && min_time_with_duration[1]) || nil
448
476
  break nil unless min_time
449
477
  next (time = min_time + 1) if exception_time?(min_time)
450
- break Occurrence.new(min_time, min_time + duration)
478
+ break Occurrence.new(min_time, min_time + (rule_duration || duration))
451
479
  end
452
480
  end
453
481