time_frame 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 956de9d650db7ad301e9b4b903cd330b0630d866
4
- data.tar.gz: 32c2a3d2249e19ee5ac6ad6335d15952c9278816
3
+ metadata.gz: efbf94fb0f87fe1ae9932c5adff2d7d2e0eecceb
4
+ data.tar.gz: 6333edbdc572720acfd58530eb9dc8d6cce0641c
5
5
  SHA512:
6
- metadata.gz: 0b1fe690605128abd552bb893a6e4176343c453738f92efa9c935b8e7876d89ed358ca8ecf155f0af8ddd2d1e6b23ec712a5a778b0fd3c3700645eeab2cf8845
7
- data.tar.gz: 92c452c7bcd63b6651d545a3d41ea15b11e48d575b02899a54ebe0726d0668fee25d54a0ecb6c2e79fbbd6f06b277fd26818ee4eff896f521f573c2d048308b0
6
+ metadata.gz: d7d02280bee7697e3403816914747f1881e0eabc328bccfbac33e1fe4789eaf43c058c51772589d2d9e888a956eb9fc8ebcf737f39582cd790dfb050424c2138
7
+ data.tar.gz: b361470e27cb987f9e653325a29db4e11489632a425e52dfaa140a29dac74a9b31a118811f4e0d9315d3e344cd2f9477336e3da3b30c820d2883c2df2b27d00a
@@ -0,0 +1,68 @@
1
+ # Encoding: utf-8
2
+
3
+ # This collection supports the concept of interval trees to improve the
4
+ # access speed to intervals (or objects containing intervals) intersecting
5
+ # given time_frames or covering time elements
6
+ class Collection
7
+ attr_reader :tree_nodes, :root
8
+ def initialize(item_list = [], sorted = false, &block)
9
+ @block = block ? block : ->(item) { item }
10
+ @tree_nodes = item_list.map do |item|
11
+ TreeNode.new(item: item, &@block)
12
+ end
13
+
14
+ sort_list(@tree_nodes) unless sorted
15
+ build_tree(0, @tree_nodes.size - 1)
16
+ @root = @tree_nodes[(@tree_nodes.size - 1) / 2]
17
+ end
18
+
19
+ def all_covering(time)
20
+ result = []
21
+ add_covering(time, @root, result)
22
+ result.sort_by { |item | [@block.call(item).min, @block.call(item).max] }
23
+ end
24
+
25
+ def all_intersecting(time_frame)
26
+ result = []
27
+ add_intersecting(time_frame, @root, result)
28
+ result.sort_by { |item | [@block.call(item).min, @block.call(item).max] }
29
+ end
30
+
31
+ private
32
+
33
+ def sort_list(item_list)
34
+ item_list.sort_by! do |item|
35
+ [item.time_frame.min, item.time_frame.max]
36
+ end
37
+ end
38
+
39
+ def build_tree(lower, upper, ancestor = nil, side = nil)
40
+ mid = (lower + upper) / 2
41
+ node = @tree_nodes[mid]
42
+
43
+ node.update_ancestor_relation(ancestor, side) if ancestor && side
44
+
45
+ build_tree(lower, mid - 1, node, :left) unless lower == mid
46
+ build_tree(mid + 1, upper, node, :right) unless upper == mid
47
+
48
+ node.update_child_range(node.min_child, node.max_child) if lower == upper
49
+ end
50
+
51
+ def add_covering(time, node, result)
52
+ result << node.item if node.time_frame.cover?(time)
53
+ if node.continue_left_side_search_for_time?(time)
54
+ add_covering(time, node.left_child, result)
55
+ end
56
+ return unless node.continue_right_side_search_for_time?(time)
57
+ add_covering(time, node.right_child, result)
58
+ end
59
+
60
+ def add_intersecting(time_frame, node, result)
61
+ result << node.item unless (node.time_frame & time_frame).empty?
62
+ if node.continue_left_side_search_for_time_frame?(time_frame)
63
+ add_intersecting(time_frame, node.left_child, result)
64
+ end
65
+ return unless node.continue_right_side_search_for_time_frame?(time_frame)
66
+ add_intersecting(time_frame, node.right_child, result)
67
+ end
68
+ end
@@ -0,0 +1,55 @@
1
+ # Encoding: utf-8
2
+ class Collection
3
+ # This is a helper class for the collection. It contains the node definition
4
+ # for the used tree structues.
5
+ class TreeNode
6
+ attr_accessor :max_child, :min_child, :left_child, :right_child
7
+ attr_reader :item, :time_frame, :ancestor
8
+ def initialize(args, &block)
9
+ @item = args.fetch(:item)
10
+ @time_frame = block.call(item)
11
+ # if ancestor is nil, then tree_item is root node
12
+ @ancestor = args.fetch(:ancestor, nil)
13
+ @left_child = args.fetch(:left_child, nil)
14
+ @right_child = args.fetch(:right_child, nil)
15
+
16
+ # if block is given use it to get item's time frame
17
+ @max_child = args.fetch(:max_child, @time_frame.max)
18
+ @min_child = args.fetch(:max_child, @time_frame.min)
19
+ end
20
+
21
+ def update_ancestor_relation(new_ancestor, side)
22
+ @ancestor = new_ancestor
23
+ new_ancestor.left_child = self if side == :left
24
+ new_ancestor.right_child = self if side == :right
25
+ end
26
+
27
+ def update_child_range(new_min_child, new_max_child)
28
+ @min_child = [@min_child, new_min_child].min
29
+ @max_child = [@max_child, new_max_child].max
30
+ ancestor.update_child_range(min_child, max_child) if ancestor
31
+ end
32
+
33
+ def continue_left_side_search_for_time?(time)
34
+ left_child &&
35
+ time >= left_child.min_child && time <= left_child.max_child
36
+ end
37
+
38
+ def continue_left_side_search_for_time_frame?(interval)
39
+ left_child &&
40
+ left_child.min_child <= interval.max &&
41
+ interval.min <= left_child.max_child
42
+ end
43
+
44
+ def continue_right_side_search_for_time?(time)
45
+ right_child &&
46
+ right_child.min_child <= time && time <= right_child.max_child
47
+ end
48
+
49
+ def continue_right_side_search_for_time_frame?(interval)
50
+ right_child &&
51
+ right_child.min_child <= interval.max &&
52
+ interval.min <= right_child.max_child
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  # Encoding: utf-8
2
2
  # gem version
3
3
  class TimeFrame
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/time_frame.rb CHANGED
@@ -10,3 +10,6 @@ require 'time_frame/time_frame_overlaps'
10
10
  require 'time_frame/time_frame_uniter'
11
11
 
12
12
  require 'time_frame/time_frame'
13
+
14
+ require 'time_frame/tree_node'
15
+ require 'time_frame/collection'
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe Collection do
4
+
5
+ let(:time_frame) { TimeFrame.new(min: Time.utc(2014), duration: 20.days) }
6
+ let(:time) { Time.utc(2014) }
7
+
8
+ describe '#all_covering' do
9
+ context 'when a pure time_frame tree is given' do
10
+ it 'returns all covering time_frames' do
11
+ time_frames = 20.times.map { |i| time_frame.shift_by((5 * i).days) }
12
+ tree = Collection.new(time_frames)
13
+
14
+ result = tree.all_covering(time)
15
+ expected_result = time_frames.select { |t| t.cover?(time) }
16
+ expect(result).to eq expected_result
17
+
18
+ result = tree.all_covering(time - 1.day)
19
+ expect(result).to eq []
20
+
21
+ result = tree.all_covering(time + 5.days)
22
+ expected_result = time_frames.select { |t| t.cover?(time + 5.days) }
23
+ expect(result).to eq expected_result
24
+
25
+ result = tree.all_covering(time + 7.days)
26
+ expected_result = time_frames.select { |t| t.cover?(time + 7.days) }
27
+ expect(result).to eq expected_result
28
+
29
+ result = tree.all_covering(time + 17.days)
30
+ expected_result = time_frames.select { |t| t.cover?(time + 17.days) }
31
+ expect(result).to eq expected_result
32
+
33
+ result = tree.all_covering(time + 42.days)
34
+ expected_result = time_frames.select { |t| t.cover?(time + 42.days) }
35
+ expect(result).to eq expected_result
36
+
37
+ result = tree.all_covering(time + 300.days)
38
+ expect(result).to eq []
39
+ end
40
+ end
41
+
42
+ context 'when objects containing time_frames are given' do
43
+ it 'returns all covering time_frames' do
44
+ objects = 20.times.map do |i|
45
+ OpenStruct.new(time_frame: time_frame.shift_by((5 * i).days))
46
+ end
47
+ tree = Collection.new(objects) { |item| item.time_frame }
48
+
49
+ result = tree.all_covering(time - 1.day)
50
+ expect(result).to eq []
51
+
52
+ result = tree.all_covering(time + 5.days)
53
+ expected_result = objects.select do |t|
54
+ t.time_frame.cover?(time + 5.days)
55
+ end
56
+ expect(result).to eq expected_result
57
+
58
+ result = tree.all_covering(time + 7.days)
59
+ expected_result = objects.select do |t|
60
+ t.time_frame.cover?(time + 7.days)
61
+ end
62
+ expect(result).to eq expected_result
63
+
64
+ result = tree.all_covering(time + 17.days)
65
+ expected_result = objects.select do |t|
66
+ t.time_frame.cover?(time + 17.days)
67
+ end
68
+ expect(result).to eq expected_result
69
+
70
+ result = tree.all_covering(time + 42.days)
71
+ expected_result = objects.select do |t|
72
+ t.time_frame.cover?(time + 42.days)
73
+ end
74
+ expect(result).to eq expected_result
75
+
76
+ result = tree.all_covering(time + 300.days)
77
+ expect(result).to eq []
78
+ end
79
+ end
80
+ end
81
+
82
+ describe '#all_intersecting' do
83
+ context 'when a pure time_frame tree is given' do
84
+ it 'returns all intersecting time_frames' do
85
+ time_frames = 20.times.map { |i| time_frame.shift_by((5 * i).days) }
86
+ tree = Collection.new(time_frames)
87
+ interval = TimeFrame.new(min: time, duration: 1.hour)
88
+
89
+ result = tree.all_intersecting(interval.shift_by((-1).day))
90
+ expect(result).to eq []
91
+
92
+ result = tree.all_intersecting(interval)
93
+ expected_result = time_frames.select do |t|
94
+ !(t & (interval)).empty?
95
+ end
96
+ expect(result).to eq expected_result
97
+
98
+ this_interval = interval.shift_by(5.days)
99
+ result = tree.all_intersecting(this_interval)
100
+ expected_result = time_frames.select do |t|
101
+ !(t & (this_interval)).empty?
102
+ end
103
+ expect(result).to eq expected_result
104
+
105
+ this_interval = interval.shift_by(7.days)
106
+ result = tree.all_intersecting(this_interval)
107
+ expected_result = time_frames.select do |t|
108
+ !(t & (this_interval)).empty?
109
+ end
110
+ expect(result).to eq expected_result
111
+
112
+ this_interval = interval.shift_by(17.days)
113
+ result = tree.all_intersecting(this_interval)
114
+ expected_result = time_frames.select do |t|
115
+ !(t & (this_interval)).empty?
116
+ end
117
+ expect(result).to eq expected_result
118
+
119
+ this_interval = interval.shift_by(42.days)
120
+ result = tree.all_intersecting(this_interval)
121
+ expected_result = time_frames.select do |t|
122
+ !(t & (this_interval)).empty?
123
+ end
124
+ expect(result).to eq expected_result
125
+
126
+ result = tree.all_intersecting(interval.shift_by(300.days))
127
+ expect(result).to eq []
128
+ end
129
+ end
130
+
131
+ context 'when objects containing time_frames are given' do
132
+ it 'returns all intersecting time_frames' do
133
+ objects = 20.times.map do |i|
134
+ OpenStruct.new(time_frame: time_frame.shift_by((5 * i).days))
135
+ end
136
+ tree = Collection.new(objects) { |item| item.time_frame }
137
+ interval = TimeFrame.new(min: time, duration: 1.hour)
138
+
139
+ result = tree.all_intersecting(interval.shift_by((-1).day))
140
+ expect(result).to eq []
141
+
142
+ result = tree.all_intersecting(interval)
143
+ expected_result = objects.select do |object|
144
+ !(object.time_frame & (interval)).empty?
145
+ end
146
+ expect(result).to eq expected_result
147
+
148
+ this_interval = interval.shift_by(5.days)
149
+ result = tree.all_intersecting(this_interval)
150
+ expected_result = objects.select do |object|
151
+ !(object.time_frame & (this_interval)).empty?
152
+ end
153
+ expect(result).to eq expected_result
154
+
155
+ this_interval = interval.shift_by(7.days)
156
+ result = tree.all_intersecting(this_interval)
157
+ expected_result = objects.select do |object|
158
+ !(object.time_frame & (this_interval)).empty?
159
+ end
160
+ expect(result).to eq expected_result
161
+
162
+ this_interval = interval.shift_by(17.days)
163
+ result = tree.all_intersecting(this_interval)
164
+ expected_result = objects.select do |object|
165
+ !(object.time_frame & (this_interval)).empty?
166
+ end
167
+ expect(result).to eq expected_result
168
+
169
+ this_interval = interval.shift_by(42.days)
170
+ result = tree.all_intersecting(this_interval)
171
+ expected_result = objects.select do |object|
172
+ !(object.time_frame & (this_interval)).empty?
173
+ end
174
+ expect(result).to eq expected_result
175
+
176
+ result = tree.all_intersecting(interval.shift_by(300.days))
177
+ expect(result).to eq []
178
+
179
+ end
180
+ end
181
+ end
182
+ end
@@ -77,6 +77,17 @@ describe TimeFrame do
77
77
  subject { super().max }
78
78
  it { should eq time }
79
79
  end
80
+
81
+ context 'when min is a date' do
82
+ context 'and duration is 0' do
83
+ it 'should be valid' do
84
+ expect do
85
+ TimeFrame.new(min: Date.new(2012), duration: 0.seconds)
86
+ end.not_to raise_error
87
+ end
88
+ end
89
+ end
90
+
80
91
  end
81
92
  context 'and time sframe covers a DST shift' do
82
93
  let(:time) do
data/time_frame.gemspec CHANGED
@@ -28,7 +28,6 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_development_dependency 'rake', '~> 10.3.2'
30
30
  spec.add_development_dependency 'rspec', '~> 3.0.0'
31
- spec.add_development_dependency 'bundler', '~> 1.6.1'
32
31
  spec.add_development_dependency 'simplecov', '~> 0.8.2'
33
32
  spec.add_development_dependency 'rubocop', '~> 0.23.0'
34
33
  spec.add_dependency 'activesupport', '~> 4.1.1'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_frame
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Derichs
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-07-29 00:00:00.000000000 Z
13
+ date: 2014-10-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -40,20 +40,6 @@ dependencies:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
42
  version: 3.0.0
43
- - !ruby/object:Gem::Dependency
44
- name: bundler
45
- requirement: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: 1.6.1
50
- type: :development
51
- prerelease: false
52
- version_requirements: !ruby/object:Gem::Requirement
53
- requirements:
54
- - - "~>"
55
- - !ruby/object:Gem::Version
56
- version: 1.6.1
57
43
  - !ruby/object:Gem::Dependency
58
44
  name: simplecov
59
45
  requirement: !ruby/object:Gem::Requirement
@@ -113,13 +99,16 @@ files:
113
99
  - README.md
114
100
  - Rakefile
115
101
  - lib/time_frame.rb
102
+ - lib/time_frame/collection.rb
116
103
  - lib/time_frame/empty.rb
117
104
  - lib/time_frame/time_frame.rb
118
105
  - lib/time_frame/time_frame_covered.rb
119
106
  - lib/time_frame/time_frame_overlaps.rb
120
107
  - lib/time_frame/time_frame_splitter.rb
121
108
  - lib/time_frame/time_frame_uniter.rb
109
+ - lib/time_frame/tree_node.rb
122
110
  - lib/time_frame/version.rb
111
+ - spec/collection_spec.rb
123
112
  - spec/spec_helper.rb
124
113
  - spec/time_frame_spec.rb
125
114
  - time_frame.gemspec
@@ -148,5 +137,6 @@ signing_key:
148
137
  specification_version: 4
149
138
  summary: Ruby gem that offers support for time frames
150
139
  test_files:
140
+ - spec/collection_spec.rb
151
141
  - spec/spec_helper.rb
152
142
  - spec/time_frame_spec.rb