ice_cube 0.13.2 → 0.13.3
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.
- checksums.yaml +4 -4
- data/lib/ice_cube.rb +1 -1
- data/lib/ice_cube/enumerator.rb +69 -0
- data/lib/ice_cube/hash_input.rb +71 -0
- data/lib/ice_cube/time_step.rb +30 -0
- data/lib/ice_cube/time_util.rb +2 -2
- data/lib/ice_cube/validated_rule.rb +8 -10
- data/lib/ice_cube/validations/interval.rb +19 -0
- data/lib/ice_cube/version.rb +1 -1
- data/spec/manual_regression.rb +50 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1cd6fca3c4607f135ad7b4628cc08c627cb437c9
|
4
|
+
data.tar.gz: 5018a7cf1feb4910571ac5695dcb3b051cad71d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c5446069c5e5c45819e7247114661aec92cdb0dff6a5dd2a8259aa12f7423df9bdc9dae671d9fad21180d478528ce08a846762742154f334f8b2791af34d85b
|
7
|
+
data.tar.gz: 7bbb442e21950216fb329699446ab0466848c97e50d4765623a5888d6c66f5e601d6166932df62d55e4cfe14c27cef759fafa835f7761ba31f82bb6b16011327
|
data/lib/ice_cube.rb
CHANGED
@@ -83,7 +83,7 @@ module IceCube
|
|
83
83
|
# Retain backwards compatibility for schedules exported from older versions
|
84
84
|
# This represents the version number, 11 = 0.11, 1.0 will be 100
|
85
85
|
def self.compatibility
|
86
|
-
@compatibility ||=
|
86
|
+
@compatibility ||= IceCube::VERSION.scan(/\d+/)[0..1].join.to_i
|
87
87
|
end
|
88
88
|
|
89
89
|
def self.compatibility=(version)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module IceCube
|
2
|
+
class Enumerator < ::Enumerator
|
3
|
+
|
4
|
+
def initialize(schedule, opening_time, closing_time)
|
5
|
+
@schedule = schedule
|
6
|
+
@opening_time = TimeUtil.ensure_time(opening_time)
|
7
|
+
@closing_time = TimeUtil.ensure_time(closing_time)
|
8
|
+
align_opening_time
|
9
|
+
@cursor = @opening_time
|
10
|
+
end
|
11
|
+
|
12
|
+
def each
|
13
|
+
return to_enum unless block_given?
|
14
|
+
while res = self.next && @closing_time.nil? || res <= @closing_time
|
15
|
+
yield Occurrence.new(res, res + schedule.duration)
|
16
|
+
end
|
17
|
+
raise StopIteration
|
18
|
+
end
|
19
|
+
|
20
|
+
def next
|
21
|
+
t1 = full_required? ? schedule.start_time : opening_time
|
22
|
+
Enumerator.new do |yielder|
|
23
|
+
loop do
|
24
|
+
t1.tap { |t| puts "LDA #{t}" + " #{File.basename(__FILE__)}:#{__LINE__}" }
|
25
|
+
break unless (t0 = next_time(t1, closing_time).tap { |t| puts "LDB #{t}" + " #{File.basename(__FILE__)}:#{__LINE__}" })
|
26
|
+
break if closing_time && t0 > closing_time
|
27
|
+
yielder << (block_given? ? block.call(t0) : t0) if t0 >= opening_time
|
28
|
+
break unless (t1 = next_time(t0 + 1, closing_time))
|
29
|
+
break if closing_time && t1 > closing_time
|
30
|
+
if TimeUtil.same_clock?(t0, t1) && recurrence_rules.any?(&:dst_adjust?)
|
31
|
+
wind_back_dst
|
32
|
+
next (t1 += 1)
|
33
|
+
end
|
34
|
+
yielder << (block_given? ? block.call(t1) : t1) if t1 >= opening_time
|
35
|
+
next (t1 += 1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def align_start_time
|
43
|
+
if @opening_time <= schedule.start_time || full_required?
|
44
|
+
@opening_time = schedule.start_time
|
45
|
+
else
|
46
|
+
@opening_time += @schedule.start_time.subsec - @opening_time.subsec rescue 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return a boolean indicating if any rule needs to be run from the start of time
|
51
|
+
def full_required?
|
52
|
+
recurrence_rules.any?(&:full_required?) ||
|
53
|
+
exception_rules.any?(&:full_required?)
|
54
|
+
end
|
55
|
+
|
56
|
+
def exception_rules
|
57
|
+
schedule.instance_variable_get(:@all_exception_rules)
|
58
|
+
end
|
59
|
+
|
60
|
+
def recurrence_rules
|
61
|
+
@recurrence_rules ||= if recurrence_rules.empty?
|
62
|
+
[SingleOccurrenceRule.new(schedule.start_time)].concat schedule.instance_variable_get(:@all_recurrence_rules)
|
63
|
+
else
|
64
|
+
schedule.instance_variable_get(:@all_recurrence_rules)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module IceCube
|
2
|
+
class HashInput
|
3
|
+
|
4
|
+
class Mash
|
5
|
+
def initialize(hash)
|
6
|
+
@hash = hash
|
7
|
+
end
|
8
|
+
|
9
|
+
# Fetch values indifferently by symbol or string key without symbolizing
|
10
|
+
# arbitrary string input
|
11
|
+
#
|
12
|
+
def [](key)
|
13
|
+
@hash.fetch(key) do |key|
|
14
|
+
str_key = key.to_s
|
15
|
+
@hash.each_pair.detect do |sym_key, value|
|
16
|
+
return value if sym_key.to_s == str_key
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(hash)
|
23
|
+
@input = Mash.new(hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](key)
|
27
|
+
@input[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_rule
|
31
|
+
return nil unless rule_class
|
32
|
+
rule = rule_class.new(interval, week_start)
|
33
|
+
rule.until(limit_time) if limit_time
|
34
|
+
rule.count(limit_count) if limit_count
|
35
|
+
validations.each do |validation, args|
|
36
|
+
rule.send(validation, *args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def rule_class
|
41
|
+
return @rule_class if @rule_class
|
42
|
+
match = @input[:rule_type].match(/::(\w+Rule)$/)
|
43
|
+
@rule_class = IceCube.const_get(match[1]) if match
|
44
|
+
end
|
45
|
+
|
46
|
+
def interval
|
47
|
+
@input[:interval] || 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def week_start
|
51
|
+
@input[:week_start] || :sunday
|
52
|
+
end
|
53
|
+
|
54
|
+
def limit_time
|
55
|
+
@limit_time ||= TimeUtil.deserialize_time(@input[:until])
|
56
|
+
end
|
57
|
+
|
58
|
+
def limit_count
|
59
|
+
@input[:count]
|
60
|
+
end
|
61
|
+
|
62
|
+
def validations
|
63
|
+
input_validations = Mash.new(@input[:validations] || {})
|
64
|
+
ValidatedRule::VALIDATION_ORDER.each_with_object({}) do |key, output_validations|
|
65
|
+
args = input_validations[key]
|
66
|
+
output_validations[key] = Array(args) if args
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module IceCube
|
2
|
+
class TimeStep
|
3
|
+
SECS = 1
|
4
|
+
MINS = 60
|
5
|
+
HOURS = MINS * 60
|
6
|
+
DAYS = HOURS * 24
|
7
|
+
WEEKS = DAYS * 7
|
8
|
+
MONTHS = {
|
9
|
+
1 => [ 28, 29, 30, 31].map { |m| m * DAYS },
|
10
|
+
2 => [ 59, 60, 61, 62].map { |m| m * DAYS },
|
11
|
+
3 => [ 89, 90, 91, 92].map { |m| m * DAYS },
|
12
|
+
4 => [120, 121, 122, 123].map { |m| m * DAYS },
|
13
|
+
5 => [150, 151, 152, 153].map { |m| m * DAYS },
|
14
|
+
6 => [181, 182, 183, 184].map { |m| m * DAYS },
|
15
|
+
7 => [212, 213, 214, 215].map { |m| m * DAYS },
|
16
|
+
8 => [242, 243, 244, 245].map { |m| m * DAYS },
|
17
|
+
9 => [273, 274, 275, 276].map { |m| m * DAYS },
|
18
|
+
10 => [303, 304, 305, 306].map { |m| m * DAYS },
|
19
|
+
11 => [334, 335, 336, 337].map { |m| m * DAYS },
|
20
|
+
12 => [365, 366] .map { |m| m * DAYS }
|
21
|
+
}
|
22
|
+
YEARS = [365, 366]
|
23
|
+
|
24
|
+
def next(base, validations)
|
25
|
+
end
|
26
|
+
|
27
|
+
def prev(base, validations)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/ice_cube/time_util.rb
CHANGED
@@ -126,7 +126,7 @@ module IceCube
|
|
126
126
|
# Convert a symbol to a numeric month
|
127
127
|
def self.sym_to_month(sym)
|
128
128
|
MONTHS.fetch(sym) do |k|
|
129
|
-
|
129
|
+
MONTHS.values.detect { |i| i.to_s == k.to_s } or
|
130
130
|
raise ArgumentError, "Expecting Fixnum or Symbol value for month. " \
|
131
131
|
"No such month: #{k.inspect}"
|
132
132
|
end
|
@@ -136,7 +136,7 @@ module IceCube
|
|
136
136
|
# Convert a symbol to a wday number
|
137
137
|
def self.sym_to_wday(sym)
|
138
138
|
DAYS.fetch(sym) do |k|
|
139
|
-
|
139
|
+
DAYS.values.detect { |i| i.to_s == k.to_s } or
|
140
140
|
raise ArgumentError, "Expecting Fixnum or Symbol value for weekday. " \
|
141
141
|
"No such weekday: #{k.inspect}"
|
142
142
|
end
|
@@ -140,21 +140,19 @@ module IceCube
|
|
140
140
|
true
|
141
141
|
end
|
142
142
|
|
143
|
+
# Returns true if all validations for the current rule match
|
144
|
+
# otherwise false and shifts to the first (largest) unmatched offset
|
145
|
+
#
|
143
146
|
def validation_accepts_or_updates_time?(validations_for_type)
|
144
|
-
res =
|
145
|
-
|
146
|
-
|
147
|
-
|
147
|
+
res = validations_for_type.each_with_object([]) do |validation, offsets|
|
148
|
+
r = validation.validate(@time, @schedule)
|
149
|
+
return true if r.nil? || r == 0
|
150
|
+
offsets << r
|
151
|
+
end
|
148
152
|
shift_time_by_validation(res, validations_for_type.first)
|
149
153
|
false
|
150
154
|
end
|
151
155
|
|
152
|
-
def validated_results(validations_for_type)
|
153
|
-
validations_for_type.map do |validation|
|
154
|
-
validation.validate(@time, @schedule)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
156
|
def shift_time_by_validation(res, validation)
|
159
157
|
return unless (interval = res.min)
|
160
158
|
wrapper = TimeUtil::TimeWrapper.new(@time, validation.dst_adjust?)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module IceCube
|
2
|
+
class Validations::Interval
|
3
|
+
|
4
|
+
def initialize(interval)
|
5
|
+
self.interval = interval
|
6
|
+
end
|
7
|
+
|
8
|
+
def interval=(number)
|
9
|
+
i = number.to_i
|
10
|
+
raise unless i > 0
|
11
|
+
@interval = i
|
12
|
+
rescue
|
13
|
+
raise ArgumentError.new "#{number.inspect} is not a valid input for interval, expecting positive integer"
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :interval
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/ice_cube/version.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rbkit'
|
2
|
+
Rbkit.start_profiling
|
3
|
+
require 'ice_cube'
|
4
|
+
|
5
|
+
module Kwip
|
6
|
+
def self.womp
|
7
|
+
threads = []
|
8
|
+
start = Time.new(2104,1,1)
|
9
|
+
daily = IceCube::Rule.daily.hour_of_day(6, 12, 18, 23)
|
10
|
+
|
11
|
+
# ObjectSpace.dump_all(output: File.open('heap1.json','w'))
|
12
|
+
3.times do
|
13
|
+
threads << Thread.new do
|
14
|
+
schedule = IceCube::Schedule.new(start)
|
15
|
+
schedule.add_recurrence_rule(daily)
|
16
|
+
schedule.next_occurrence
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
threads.each &:join
|
21
|
+
threads.clear
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# GC::Tracer.start_logging('gc.log') do
|
27
|
+
# GC.disable
|
28
|
+
10000.times do
|
29
|
+
Kwip.womp
|
30
|
+
Kwip.womp
|
31
|
+
Kwip.womp
|
32
|
+
end
|
33
|
+
# GC.enable
|
34
|
+
# GC.start
|
35
|
+
|
36
|
+
# str = 'asdf'
|
37
|
+
# str = nil
|
38
|
+
# GC.start
|
39
|
+
# end
|
40
|
+
|
41
|
+
# ObjectSpace.each_object do |o|
|
42
|
+
# if o.is_a?(IceCube::Occurrence) || o.is_a?(IceCube::Schedule) || o.is_a?(IceCube::Rule)
|
43
|
+
# puts "#{o.object_id} => #{o.class} (#{ObjectSpace.memsize_of(o)})"
|
44
|
+
# # puts ObjectSpace.allocation_sourcefile(o), #=> "example.rb"
|
45
|
+
# # ObjectSpace.allocation_sourceline(o), #=> 6
|
46
|
+
# # ObjectSpace.allocation_generation(o), #=> 1
|
47
|
+
# # ObjectSpace.allocation_class_path(o), #=> "MyApp"
|
48
|
+
# # ObjectSpace.allocation_method_id(o) #=> :perform
|
49
|
+
# end
|
50
|
+
# end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ice_cube
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.13.
|
4
|
+
version: 0.13.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Crepezzi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -95,9 +95,11 @@ files:
|
|
95
95
|
- lib/ice_cube/builders/ical_builder.rb
|
96
96
|
- lib/ice_cube/builders/string_builder.rb
|
97
97
|
- lib/ice_cube/deprecated.rb
|
98
|
+
- lib/ice_cube/enumerator.rb
|
98
99
|
- lib/ice_cube/errors/count_exceeded.rb
|
99
100
|
- lib/ice_cube/errors/until_exceeded.rb
|
100
101
|
- lib/ice_cube/flexible_hash.rb
|
102
|
+
- lib/ice_cube/hash_input.rb
|
101
103
|
- lib/ice_cube/i18n.rb
|
102
104
|
- lib/ice_cube/null_i18n.rb
|
103
105
|
- lib/ice_cube/occurrence.rb
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- lib/ice_cube/rules/yearly_rule.rb
|
115
117
|
- lib/ice_cube/schedule.rb
|
116
118
|
- lib/ice_cube/single_occurrence_rule.rb
|
119
|
+
- lib/ice_cube/time_step.rb
|
117
120
|
- lib/ice_cube/time_util.rb
|
118
121
|
- lib/ice_cube/validated_rule.rb
|
119
122
|
- lib/ice_cube/validations/count.rb
|
@@ -125,6 +128,7 @@ files:
|
|
125
128
|
- lib/ice_cube/validations/fixed_value.rb
|
126
129
|
- lib/ice_cube/validations/hour_of_day.rb
|
127
130
|
- lib/ice_cube/validations/hourly_interval.rb
|
131
|
+
- lib/ice_cube/validations/interval.rb
|
128
132
|
- lib/ice_cube/validations/lock.rb
|
129
133
|
- lib/ice_cube/validations/minute_of_hour.rb
|
130
134
|
- lib/ice_cube/validations/minutely_interval.rb
|
@@ -137,6 +141,7 @@ files:
|
|
137
141
|
- lib/ice_cube/validations/weekly_interval.rb
|
138
142
|
- lib/ice_cube/validations/yearly_interval.rb
|
139
143
|
- lib/ice_cube/version.rb
|
144
|
+
- spec/manual_regression.rb
|
140
145
|
- spec/spec_helper.rb
|
141
146
|
homepage: http://seejohnrun.github.com/ice_cube/
|
142
147
|
licenses:
|
@@ -158,9 +163,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
163
|
version: '0'
|
159
164
|
requirements: []
|
160
165
|
rubyforge_project: ice-cube
|
161
|
-
rubygems_version: 2.
|
166
|
+
rubygems_version: 2.2.2
|
162
167
|
signing_key:
|
163
168
|
specification_version: 4
|
164
169
|
summary: Ruby Date Recurrence Library
|
165
170
|
test_files:
|
171
|
+
- spec/manual_regression.rb
|
166
172
|
- spec/spec_helper.rb
|