time_frame 0.0.6 → 0.1.0

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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- Nzk5ZmI2MDEwY2E0MTkzOTU1NDE2MDYwNzE5ZDcwZjZhY2U2MTg3Mg==
5
- data.tar.gz: !binary |-
6
- ZjRkOTNjYmVmMjMxY2MwZjYyMTgwNmM1ODBmZDFkZTA0YmI2ZjE5OQ==
2
+ SHA1:
3
+ metadata.gz: 3a7329ea6aa8db160440c921d298bb8037194707
4
+ data.tar.gz: 445018532eec6574f3c254f388b11162d34a23af
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NzBmZjQ5MWYwNDRjYjNjOTZmOTYwMGU3OTYzNzBiNjZhOTMwOThlMDEyZDRh
10
- NTQ4MTk3NTgyZmNkMmQyYzUyMmQxNWMxNDIwODdmNmI5MDc0NjIzZDE3Nzc5
11
- OGFhYzA5OTZmZGU0OWU0YzBmZjM0M2U3NzMwMGZmNzRhOTJiYmM=
12
- data.tar.gz: !binary |-
13
- OGU5YTZhZDE3ZDc0NTY5MmI5OGUyYjU5OGEzNTY2NjljMDkwMTQ3OWQ3MTEw
14
- YTgwMWNiZmU1YTFjNjYzZTczZmU5NTc0YTRjOTMxNjg0ODFjOTk0YzE2OTcw
15
- N2Q0MDIzYjYyNWVmMzllYjcxZTEzZjBjY2U1YmQ3MzFhMTllOTE=
6
+ metadata.gz: eef4ffa1fbc1a5bf4154582445d7dea4fcae7e21f3b6c019651c4d8e4bb2562180017c629f50669c2d671c549b66ab05f4792f88285296158c5a909791636a00
7
+ data.tar.gz: 08d510eb2c5b59d6eec01334183d582b44c93d623c75dd3f98db55817d915f339d829fc9ced18fe667239038abe3da6440a62b2d8ccbf9985295245cc0293256
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rspec/core/rake_task'
3
3
  require 'rubocop/rake_task'
4
4
  require 'bundler/gem_tasks'
5
5
 
6
- Rubocop::RakeTask.new
6
+ RuboCop::RakeTask.new
7
7
 
8
8
  RSpec::Core::RakeTask.new :spec
9
9
 
@@ -1,7 +1,9 @@
1
1
  # Encoding: utf-8
2
2
  require 'active_support'
3
3
  require 'active_support/core_ext'
4
+ require 'singleton'
4
5
 
6
+ require 'time_frame/empty'
5
7
  require 'time_frame/time_frame_splitter'
6
8
  require 'time_frame/time_frame_covered'
7
9
  require 'time_frame/time_frame_overlaps'
@@ -0,0 +1,50 @@
1
+ class TimeFrame
2
+ # Singleton class for the empty time frame object
3
+ class Empty < TimeFrame
4
+ include Singleton
5
+
6
+ def duration
7
+ 0
8
+ end
9
+
10
+ def cover?(element)
11
+ element == EMPTY
12
+ end
13
+
14
+ def empty?
15
+ true
16
+ end
17
+
18
+ def deviation_of(_)
19
+ fail TypeError, 'deviation_of is undefined for empty time frame'
20
+ end
21
+
22
+ def &(_other)
23
+ self
24
+ end
25
+
26
+ def overlaps?(_)
27
+ false
28
+ end
29
+
30
+ def split_by_interval(_)
31
+ []
32
+ end
33
+
34
+ def shift_by(_)
35
+ fail TypeError, 'can\'t shift empty time frame'
36
+ end
37
+
38
+ def shift_to(_)
39
+ fail TypeError, 'can\'t shift empty time frame'
40
+ end
41
+
42
+ def without(*_)
43
+ []
44
+ end
45
+
46
+ def inspect
47
+ 'EMPTY'
48
+ end
49
+ end
50
+ end
@@ -4,6 +4,8 @@
4
4
  class TimeFrame
5
5
  attr_reader :min, :max
6
6
 
7
+ EMPTY = Empty.instance
8
+
7
9
  def initialize(args)
8
10
  min = args.fetch(:min)
9
11
  max = args.fetch(:max, nil) || min + args.fetch(:duration)
@@ -23,18 +25,19 @@ class TimeFrame
23
25
 
24
26
  def cover?(element)
25
27
  if element.respond_to?(:min) && element.respond_to?(:max)
26
- return min <= element.min && element.max <= max
28
+ element.empty? || min <= element.min && element.max <= max
29
+ else
30
+ min <= element && element <= max
27
31
  end
28
- min <= element && element <= max
29
32
  end
30
33
 
31
34
  def deviation_of(item)
32
- case
33
- when item.respond_to?(:min) && item.respond_to?(:max)
34
- [deviation_of(item.min), deviation_of(item.max)].min_by { |a| a.abs }
35
- when cover?(item)
35
+ if item.respond_to?(:min) && item.respond_to?(:max)
36
+ fail ArgumentError, 'time frame is empty' if item.empty?
37
+ [deviation_of(item.min), deviation_of(item.max)].min_by(&:abs)
38
+ elsif cover?(item)
36
39
  0
37
- when item < min
40
+ elsif item < min
38
41
  item - min
39
42
  else
40
43
  item - max
@@ -42,7 +45,7 @@ class TimeFrame
42
45
  end
43
46
 
44
47
  def empty?
45
- min == max
48
+ false
46
49
  end
47
50
 
48
51
  def self.union(time_frames, options = {})
@@ -51,20 +54,21 @@ class TimeFrame
51
54
 
52
55
  def self.intersection(time_frames)
53
56
  time_frames.reduce(time_frames.first) do |intersection, time_frame|
54
- return unless intersection
55
57
  intersection & time_frame
56
58
  end
57
59
  end
58
60
 
59
61
  # Returns true if the interior intersect.
60
62
  def overlaps?(other)
63
+ return false if other.duration == 0
61
64
  other.max > min && other.min < max
62
65
  end
63
66
 
64
67
  def &(other)
68
+ return EMPTY if other.empty?
65
69
  new_min = [min, other.min].max
66
70
  new_max = [max, other.max].min
67
- TimeFrame.new(min: new_min, max: new_max) if new_min <= new_max
71
+ new_min <= new_max ? TimeFrame.new(min: new_min, max: new_max) : EMPTY
68
72
  end
69
73
 
70
74
  def shift_by(duration)
@@ -107,9 +111,6 @@ class TimeFrame
107
111
 
108
112
  def without_frame(other)
109
113
  intersection = self & other
110
- # this case is never used up to now (15.03.2013),
111
- # since without selects the values correctly
112
- return [self] unless intersection
113
114
 
114
115
  result = []
115
116
  if intersection.min > min
@@ -3,11 +3,11 @@ class TimeFrame
3
3
  # Getting the covering time frame from a bunch of time_frame's.
4
4
  class CoveredFrame
5
5
  def initialize(time_frames)
6
- @time_frames = time_frames
6
+ @time_frames = time_frames.reject(&:empty?)
7
7
  end
8
8
 
9
9
  def frame
10
- return nil unless @time_frames.any?
10
+ return EMPTY unless @time_frames.any?
11
11
  min = @time_frames.min_by(&:min).min
12
12
  max = @time_frames.max_by(&:max).max
13
13
  TimeFrame.new(min: min, max: max)
@@ -8,16 +8,16 @@ class TimeFrame
8
8
  # * requires each of the arrays to be sorted
9
9
  class Overlaps
10
10
  def initialize(array1, array2)
11
- @array1 = array1.dup
12
- @array2 = array2.dup
11
+ @array1 = array1.reject(&:empty?)
12
+ @array2 = array2.reject(&:empty?)
13
13
  end
14
14
 
15
15
  def each(&block)
16
16
  return [] if @array1.empty? || @array2.empty?
17
- yield_current_items(&block) if current_items_overlapping?
18
- while arrays_have_many_items?
17
+ yield_current_pair(&block) if current_pair_overlaps?
18
+ while each_array_has_many_items?
19
19
  shift
20
- yield_current_items(&block) if current_items_overlapping?
20
+ yield_current_pair(&block) if current_pair_overlaps?
21
21
  end
22
22
  end
23
23
 
@@ -32,17 +32,16 @@ class TimeFrame
32
32
  end
33
33
  end
34
34
 
35
- def arrays_have_many_items?
35
+ def each_array_has_many_items?
36
36
  @array1.many? || @array2.many?
37
37
  end
38
38
 
39
- def yield_current_items
39
+ def yield_current_pair
40
40
  yield @array1.first, @array2.first
41
41
  end
42
42
 
43
- def current_items_overlapping?
44
- time_frame = @array1.first & @array2.first
45
- time_frame && time_frame.duration > 0
43
+ def current_pair_overlaps?
44
+ @array1.first.overlaps? @array2.first
46
45
  end
47
46
  end
48
47
  end
@@ -4,7 +4,7 @@ class TimeFrame
4
4
  # the min Time value.
5
5
  class Uniter
6
6
  def initialize(time_frames, options = {})
7
- @time_frames = time_frames
7
+ @time_frames = time_frames.reject(&:empty?)
8
8
  @sorted = options[:sorted]
9
9
  end
10
10
 
@@ -1,5 +1,5 @@
1
1
  # Encoding: utf-8
2
2
  # gem version
3
3
  class TimeFrame
4
- VERSION = '0.0.6'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -1,11 +1,11 @@
1
1
  # Encoding: utf-8
2
- require 'time_frame'
3
-
4
2
  require 'simplecov'
5
3
  SimpleCov.start do
6
4
  add_filter '/spec/'
7
5
  end
8
6
 
7
+ require 'time_frame'
8
+
9
9
  Time.zone ||= 'UTC'
10
10
 
11
11
  require 'rspec'
@@ -0,0 +1,1098 @@
1
+ # Encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe TimeFrame do
5
+ let(:time) { Time.zone.local(2012) }
6
+ let(:duration) { 2.hours }
7
+
8
+ before do
9
+ # Avoid i18n deprecation warning
10
+ I18n.enforce_available_locales = true
11
+ end
12
+
13
+ describe '#min and #max' do
14
+ context 'when given two times' do
15
+ context 'and min is smaller than max' do
16
+ subject { TimeFrame.new(min: time, max: time + duration) }
17
+
18
+ describe '#min' do
19
+ subject { super().min }
20
+ it { should eq time }
21
+ end
22
+
23
+ describe '#max' do
24
+ subject { super().max }
25
+ it { should eq time + duration }
26
+ end
27
+ end
28
+ context 'and max is smaller than min' do
29
+ specify do
30
+ expect do
31
+ TimeFrame.new(min: time, max: time - 1.day)
32
+ end.to raise_error(ArgumentError)
33
+ end
34
+ end
35
+ context 'which are equal' do
36
+ subject { TimeFrame.new(min: time, max: time) }
37
+
38
+ describe '#min' do
39
+ subject { super().min }
40
+ it { should eq time }
41
+ end
42
+
43
+ describe '#max' do
44
+ subject { super().max }
45
+ it { should eq time }
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'when time and duration is given' do
51
+ context ' and duration is positive' do
52
+ subject { TimeFrame.new(min: time, duration: 1.hour) }
53
+
54
+ describe '#min' do
55
+ subject { super().min }
56
+ it { should eq time }
57
+ end
58
+
59
+ describe '#max' do
60
+ subject { super().max }
61
+ it { should eq time + 1.hour }
62
+ end
63
+ end
64
+ context 'and duration is negative' do
65
+ let(:invalid_t_r) { TimeFrame.new(min: time, duration: - 1.hour) }
66
+ specify { expect { invalid_t_r }.to raise_error }
67
+ end
68
+ context 'and duration is 0' do
69
+ subject { TimeFrame.new(min: time, duration: 0.hour) }
70
+
71
+ describe '#min' do
72
+ subject { super().min }
73
+ it { should eq time }
74
+ end
75
+
76
+ describe '#max' do
77
+ subject { super().max }
78
+ it { should eq time }
79
+ end
80
+ end
81
+ context 'and time sframe covers a DST shift' do
82
+ let(:time) do
83
+ Time.use_zone('Europe/Berlin') { Time.zone.local(2013, 10, 27) }
84
+ end
85
+ subject { TimeFrame.new(min: time, duration: 1.day) }
86
+
87
+ describe '#min' do
88
+ subject { super().min }
89
+ it { should eq time }
90
+ end
91
+
92
+ describe '#max' do
93
+ subject { super().max }
94
+ it { should eq time + 25.hours }
95
+ end
96
+ end
97
+ end
98
+
99
+ specify '#min and #max are nil if time frame is empty' do
100
+ expect(TimeFrame::EMPTY.min).to be_nil
101
+ expect(TimeFrame::EMPTY.max).to be_nil
102
+ end
103
+ end
104
+
105
+ describe '#duration' do
106
+ context 'when borders are different' do
107
+ subject { TimeFrame.new(min: time, duration: 2.hours).duration }
108
+ it { should eq 2.hours }
109
+ end
110
+ context 'when borders are equal' do
111
+ subject { TimeFrame.new(min: time, max: time).duration }
112
+ it { should eq 0 }
113
+ end
114
+ context 'when time frame containts a DST shift' do
115
+ it 'should gain 1 hour on summer -> winter shifts' do
116
+ Time.use_zone('Europe/Berlin') do
117
+ time_frame = TimeFrame.new(min: Time.zone.local(2013, 10, 27),
118
+ max: Time.zone.local(2013, 10, 28))
119
+ expect(time_frame.duration).to eq 25.hours
120
+ end
121
+ end
122
+ it 'should lose 1 hour on winter -> summer shifts' do
123
+ Time.use_zone('Europe/Berlin') do
124
+ time_frame = TimeFrame.new(min: Time.zone.local(2013, 3, 31),
125
+ max: Time.zone.local(2013, 4, 1))
126
+ expect(time_frame.duration).to eq 23.hours
127
+ end
128
+ end
129
+ end
130
+ it 'returns 0 when time frame is empty' do
131
+ expect(TimeFrame::EMPTY.duration).to eq 0
132
+ end
133
+ end
134
+
135
+ describe '#==' do
136
+ let(:time_frame) { TimeFrame.new(min: time, duration: 2.hours) }
137
+ context 'when borders are equal' do
138
+ let(:other) { TimeFrame.new(min: time, duration: 2.hours) }
139
+ subject { time_frame == other }
140
+ it { should be true }
141
+ end
142
+ context 'when min value is different' do
143
+ let(:other) do
144
+ TimeFrame.new(min: time - 1.hour, max: time + 2.hours)
145
+ end
146
+ subject { time_frame == other }
147
+ it { should be false }
148
+ end
149
+ context 'when max value is different' do
150
+ let(:other) { TimeFrame.new(min: time, duration: 3.hours) }
151
+ subject { time_frame == other }
152
+ it { should be false }
153
+ end
154
+ it 'returns true when self and the argument are both empty' do
155
+ expect(TimeFrame::EMPTY).to eq TimeFrame::EMPTY
156
+ end
157
+ it 'returns false if argument is empty, but self is not' do
158
+ expect(
159
+ TimeFrame.new(min: time, duration: 3.hours)
160
+ ).not_to eq TimeFrame::EMPTY
161
+ end
162
+ it 'returns false if self is empty, but argument is not' do
163
+ expect(
164
+ TimeFrame::EMPTY
165
+ ).not_to eq TimeFrame.new(min: time, duration: 3.hours)
166
+ end
167
+ end
168
+
169
+ describe '#cover?' do
170
+ let(:time_frame) { TimeFrame.new(min: time, duration: 4.hours) }
171
+ context 'when argument is a Time instance' do
172
+ context 'and its covered' do
173
+ context 'and equal to min' do
174
+ subject { time_frame.cover?(time_frame.min) }
175
+ it { should be true }
176
+ end
177
+ context 'and equal to max' do
178
+ subject { time_frame.cover?(time_frame.max) }
179
+ it { should be true }
180
+ end
181
+ context 'and is an inner value' do
182
+ subject { time_frame.cover?(time_frame.min + 1.hour) }
183
+ it { should be true }
184
+ end
185
+ end
186
+ context 'and its not covered' do
187
+ context 'and smaller than min' do
188
+ subject { time_frame.cover?(time_frame.min - 1.hour) }
189
+ it { should be false }
190
+ end
191
+ context 'and greater than max' do
192
+ subject { time_frame.cover?(time_frame.max + 5.hours) }
193
+ it { should be false }
194
+ end
195
+ end
196
+ end
197
+ context 'when argument is a TimeFrame' do
198
+ context 'and its covered' do
199
+ context 'and they have the same min value' do
200
+ let(:other) { TimeFrame.new(min: time_frame.min, duration: 2.hours) }
201
+ subject { time_frame.cover?(other) }
202
+ it { should be true }
203
+ end
204
+ context 'and they have the same max value' do
205
+ let(:other) do
206
+ TimeFrame.new(min: time_frame.min + 1.hour, max: time_frame.max)
207
+ end
208
+ subject { time_frame.cover?(other) }
209
+ it { should be true }
210
+ end
211
+ context 'and it is within the interior of self' do
212
+ let(:other) do
213
+ TimeFrame.new(
214
+ min: time_frame.min + 1.hour, max: time_frame.max - 1.hour
215
+ )
216
+ end
217
+ subject { time_frame.cover?(other) }
218
+ it { should be true }
219
+ end
220
+ context 'and are equal' do
221
+ let(:other) { time_frame.clone }
222
+ subject { time_frame.cover?(other) }
223
+ it { should be true }
224
+ end
225
+ end
226
+ context 'and it is not covered' do
227
+ context 'and other is left of self' do
228
+ let(:other) { time_frame.shift_by(-5.hours) }
229
+ subject { time_frame.cover?(other) }
230
+ it { should be false }
231
+ end
232
+ context 'and other overlaps left hand side' do
233
+ let(:other) { time_frame.shift_by(-1.hour) }
234
+ subject { time_frame.cover?(other) }
235
+ it { should be false }
236
+ end
237
+ context 'and other overlaps left hand side at the border only' do
238
+ let(:other) { time_frame.shift_by(-time_frame.duration) }
239
+ subject { time_frame.cover?(other) }
240
+ it { should be false }
241
+ end
242
+ context 'and other is right of self' do
243
+ let(:other) { time_frame.shift_by(5.hours) }
244
+ subject { time_frame.cover?(other) }
245
+ it { should be false }
246
+ end
247
+ context 'and other overlaps right hand side' do
248
+ let(:other) { time_frame.shift_by(1.hours) }
249
+ subject { time_frame.cover?(other) }
250
+ it { should be false }
251
+ end
252
+ context 'and other overlaps right hand side at the border only' do
253
+ let(:other) { time_frame.shift_by(time_frame.duration) }
254
+ subject { time_frame.cover?(other) }
255
+ it { should be false }
256
+ end
257
+ end
258
+ end
259
+ it 'returns true when argument is empty' do
260
+ expect(
261
+ TimeFrame.new(min: time, duration: 3.hours)
262
+ ).to cover(TimeFrame::EMPTY)
263
+ end
264
+ it 'returns true when self and argument are both empty' do
265
+ expect(TimeFrame::EMPTY).to cover(TimeFrame::EMPTY)
266
+ end
267
+ it 'returns false when only self is empty' do
268
+ expect(
269
+ TimeFrame::EMPTY
270
+ ).not_to cover(TimeFrame.new(min: time, duration: 3.hours))
271
+ end
272
+ it 'returns true when only the argument is empty' do
273
+ expect(
274
+ TimeFrame.new(min: time, duration: 3.hours)
275
+ ).to cover(TimeFrame::EMPTY)
276
+ end
277
+ end
278
+
279
+ describe '#deviation_of' do
280
+ let(:time_frame) do
281
+ TimeFrame.new(min: Time.zone.local(2012), duration: 2.days)
282
+ end
283
+ context 'when providing a time object' do
284
+ describe 'when self covers time' do
285
+ context 'and time equals min' do
286
+ let(:time) { time_frame.min }
287
+ subject { time_frame.deviation_of(time) }
288
+ it { should eq 0.minutes }
289
+ end
290
+ context 'and time equals max' do
291
+ let(:time) { time_frame.max }
292
+ subject { time_frame.deviation_of(time) }
293
+ it { should eq 0.minutes }
294
+ end
295
+ context 'and time is an interior point of self' do
296
+ let(:time) { time_frame.min + (time_frame.duration / 2.0) }
297
+ subject { time_frame.deviation_of(time) }
298
+ it { should eq 0.minutes }
299
+ end
300
+ end
301
+ context 'when self do not cover time' do
302
+ context 'and time is smaller than the left bound' do
303
+ let(:time) { time_frame.min - 42.hours - 42.minutes }
304
+ subject { time_frame.deviation_of(time) }
305
+ it { should eq(-42.hours - 42.minutes) }
306
+ end
307
+ context 'and time is greater than the right bound' do
308
+ let(:time) { time_frame.max + 42.hours + 42.minutes }
309
+ subject { time_frame.deviation_of(time) }
310
+ it { should eq 42.hours + 42.minutes }
311
+ end
312
+ end
313
+ end
314
+ context 'when providing a time_frame' do
315
+ describe 'when self overlaps other' do
316
+ context 'and its partly' do
317
+ let(:other) { time_frame.shift_by(time_frame.duration / 2) }
318
+ subject { time_frame.deviation_of(other) }
319
+ it { should eq 0.minutes }
320
+ end
321
+ context 'and time equals max' do
322
+ let(:other) { time_frame }
323
+ subject { time_frame.deviation_of(other) }
324
+ it { should eq 0.minutes }
325
+ end
326
+ context 'and other lies in the interior of self' do
327
+ let(:other) do
328
+ TimeFrame.new(min: time_frame.min + 1.hour, duration: 1.hour)
329
+ end
330
+ subject { time_frame.deviation_of(other) }
331
+ it { should eq 0.minutes }
332
+ end
333
+ end
334
+ context 'when self do not cover time' do
335
+ context 'and time is smaller than the left bound' do
336
+ let(:other) { time_frame.shift_by(-2.days - 42.seconds) }
337
+ subject { time_frame.deviation_of(other) }
338
+ it { should eq(-42.seconds) }
339
+ end
340
+ context 'and time is greater than the right bound' do
341
+ let(:other) { time_frame.shift_by(2.days + 42.seconds) }
342
+ subject { time_frame.deviation_of(other) }
343
+ it { should eq 42.seconds }
344
+ end
345
+ end
346
+ it 'fails when only argument is empty' do
347
+ expect(-> { time_frame.deviation_of(TimeFrame::EMPTY) })
348
+ .to raise_error ArgumentError
349
+ end
350
+ it 'fails when only self is empty' do
351
+ expect(-> { TimeFrame::EMPTY.deviation_of(time_frame) })
352
+ .to raise_error TypeError
353
+ end
354
+ end
355
+ end
356
+
357
+ describe '#empty?' do
358
+ it 'returns false when self is not contains at least one point' do
359
+ expect(TimeFrame.new(min: time, max: time)).not_to be_empty
360
+ end
361
+
362
+ it 'returns true when self is empty' do
363
+ expect(TimeFrame::EMPTY).to be_empty
364
+ end
365
+ end
366
+
367
+ describe '.union' do
368
+
369
+ context 'when given an empty array' do
370
+ subject { TimeFrame.union([]) }
371
+ it { should eq [] }
372
+ end
373
+
374
+ context 'when given a single time frame' do
375
+ let(:time_frame) { TimeFrame.new(min: time, duration: 1.hour) }
376
+ subject { TimeFrame.union([time_frame]) }
377
+ it { should eq [time_frame] }
378
+ end
379
+
380
+ context 'when getting single element it returns a dup' do
381
+ let(:time_frames) { [TimeFrame.new(min: time, duration: 1.hour)] }
382
+ subject { TimeFrame.union(time_frames) }
383
+ it { should_not equal time_frames }
384
+ end
385
+
386
+ context 'when given time frames' do
387
+ context 'in order' do
388
+ context 'and no sorted flag is provided' do
389
+ context 'that are overlapping' do
390
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
391
+ let(:time_frame2) { time_frame1.shift_by(1.hour) }
392
+ let(:expected) do
393
+ [TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
394
+ end
395
+ subject { TimeFrame.union([time_frame1, time_frame2]) }
396
+ it { should eq expected }
397
+ end
398
+ context 'that are disjoint' do
399
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
400
+ let(:time_frame2) { time_frame1.shift_by(3.hours) }
401
+ subject { TimeFrame.union([time_frame1, time_frame2]) }
402
+ it { should eq [time_frame1, time_frame2] }
403
+ end
404
+ context 'that intersect at their boundaries' do
405
+ let(:time_frame1) { TimeFrame.new(min: time, duration: + 2.hour) }
406
+ let(:time_frame2) { time_frame1.shift_by(time_frame1.duration) }
407
+ let(:expected) do
408
+ [TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
409
+ end
410
+ subject { TimeFrame.union([time_frame1, time_frame2]) }
411
+ it { should eq expected }
412
+ end
413
+ end
414
+ context 'and the sorted flag is provided' do
415
+ context 'that are overlapping' do
416
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
417
+ let(:time_frame2) { time_frame1.shift_by(1.hour) }
418
+ let(:expected) do
419
+ [TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
420
+ end
421
+ subject do
422
+ TimeFrame.union([time_frame1, time_frame2], sorted: true)
423
+ end
424
+ it { should eq expected }
425
+ end
426
+ context 'that are disjoint' do
427
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
428
+ let(:time_frame2) { time_frame1.shift_by(3.hours) }
429
+ subject do
430
+ TimeFrame.union([time_frame1, time_frame2], sorted: true)
431
+ end
432
+ it { should eq [time_frame1, time_frame2] }
433
+ end
434
+ context 'that intersect at their boundaries' do
435
+ let(:time_frame1) { TimeFrame.new(min: time, duration: + 2.hour) }
436
+ let(:time_frame2) { time_frame1.shift_by(time_frame1.duration) }
437
+ let(:expected) do
438
+ [TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
439
+ end
440
+ subject do
441
+ TimeFrame.union([time_frame1, time_frame2], sorted: true)
442
+ end
443
+ it { should eq expected }
444
+ end
445
+ end
446
+ end
447
+ context 'not in order' do
448
+ context 'that are overlapping' do
449
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
450
+ let(:time_frame2) { time_frame1.shift_by(1.hour) }
451
+ subject { TimeFrame.union([time_frame2, time_frame1]) }
452
+ it do
453
+ should eq [
454
+ TimeFrame.new(min: time_frame1.min, max: time_frame2.max)
455
+ ]
456
+ end
457
+ end
458
+ context 'that are disjoint' do
459
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
460
+ let(:time_frame2) { time_frame1.shift_by(3.hours) }
461
+ subject { TimeFrame.union([time_frame2, time_frame1]) }
462
+ it { should eq [time_frame1, time_frame2] }
463
+ end
464
+ context 'that intersect at their boundaries' do
465
+ let(:time_frame1) { TimeFrame.new(min: time, duration: + 2.hour) }
466
+ let(:time_frame2) { time_frame1.shift_by(time_frame1.duration) }
467
+ subject { TimeFrame.union([time_frame2, time_frame1]) }
468
+ it do
469
+ should eq [
470
+ TimeFrame.new(min: time_frame1.min, max: time_frame2.max)
471
+ ]
472
+ end
473
+ end
474
+ end
475
+ end
476
+
477
+ it 'ignores any empty time frames' do
478
+ expect(TimeFrame.union([TimeFrame::EMPTY, TimeFrame::EMPTY])).to eq []
479
+ end
480
+ end
481
+
482
+ describe '.intersection' do
483
+ it 'returns the intersection of all time frames' do
484
+ time_frame1 = TimeFrame.new(min: Time.zone.local(2012), duration: 3.days)
485
+ time_frame2 = time_frame1.shift_by(-1.day)
486
+ time_frame3 = time_frame1.shift_by(-2.days)
487
+ expect(TimeFrame.intersection([time_frame1, time_frame2, time_frame3]))
488
+ .to eq TimeFrame.new(min: Time.zone.local(2012), duration: 1.day)
489
+ end
490
+ it 'is empty if the intersection is empty' do
491
+ time_frame1 = TimeFrame.new(min: Time.zone.local(2012), duration: 1.days)
492
+ time_frame2 = time_frame1.shift_by(-2.day)
493
+ time_frame3 = time_frame1.shift_by(-4.days)
494
+ expect(
495
+ TimeFrame.intersection([time_frame1, time_frame2, time_frame3])
496
+ ).to be_empty
497
+ end
498
+ end
499
+
500
+ describe '#overlaps?' do
501
+ let(:time_frame) { TimeFrame.new(min: time, duration: 3.hours) }
502
+ context 'when self is equal to other' do
503
+ let(:other) { time_frame.clone }
504
+ subject { time_frame.overlaps?(other) }
505
+ it { should be true }
506
+ end
507
+ context 'when self covers other' do
508
+ let(:other) do
509
+ TimeFrame.new(
510
+ min: time_frame.min + 1.hour, max: time_frame.max - 1.hour
511
+ )
512
+ end
513
+ subject { time_frame.overlaps?(other) }
514
+ it { should be true }
515
+ end
516
+ context 'when other covers self' do
517
+ let(:other) do
518
+ TimeFrame.new(
519
+ min: time_frame.min - 1.hour, max: time_frame.max + 1.hour
520
+ )
521
+ end
522
+ subject { time_frame.overlaps?(other) }
523
+ it { should be true }
524
+ end
525
+ context 'when self begins earlier than other' do
526
+ context 'and they are disjoint' do
527
+ let(:other) { time_frame.shift_by(-time_frame.duration - 1.hour) }
528
+ subject { time_frame.overlaps?(other) }
529
+ it { should be false }
530
+ end
531
+ context 'and they are overlapping' do
532
+ let(:other) { time_frame.shift_by(-1.hours) }
533
+ subject { time_frame.overlaps?(other) }
534
+ it { should be true }
535
+ end
536
+ context 'and they intersect at their boundaries' do
537
+ let(:other) { time_frame.shift_by(-time_frame.duration) }
538
+ subject { time_frame.overlaps?(other) }
539
+ it { should be false }
540
+ end
541
+ end
542
+ context 'when other begins earlier than self' do
543
+ context 'and they are disjoint' do
544
+ let(:other) { time_frame.shift_by(time_frame.duration + 1.hour) }
545
+ subject { time_frame.overlaps?(other) }
546
+ it { should be false }
547
+ end
548
+ context 'and they are overlapping' do
549
+ let(:other) { time_frame.shift_by(1.hours) }
550
+ subject { time_frame.overlaps?(other) }
551
+ it { should be true }
552
+ end
553
+ context 'and they intersect at their boundaries' do
554
+ let(:other) { time_frame.shift_by(time_frame.duration) }
555
+ subject { time_frame.overlaps?(other) }
556
+ it { should be false }
557
+ end
558
+ end
559
+ it 'returns false when self contains only one point' do
560
+ singleton_time_frame = TimeFrame.new(min: time, max: time)
561
+ time_frame = TimeFrame.new(min: time, duration: 1.hour)
562
+ expect(singleton_time_frame.overlaps?(time_frame)).to be false
563
+ expect(singleton_time_frame.overlaps?(singleton_time_frame)).to be false
564
+ expect(singleton_time_frame.overlaps?(TimeFrame::EMPTY)).to be false
565
+ end
566
+ it 'returns false when self is empty' do
567
+ singleton_time_frame = TimeFrame.new(min: time, max: time)
568
+ time_frame = TimeFrame.new(min: time, duration: 1.hour)
569
+ expect(TimeFrame::EMPTY.overlaps?(time_frame)).to be false
570
+ expect(TimeFrame::EMPTY.overlaps?(singleton_time_frame)).to be false
571
+ expect(TimeFrame::EMPTY.overlaps?(TimeFrame::EMPTY)).to be false
572
+ end
573
+ it 'returns false when other contains only one point' do
574
+ singleton_time_frame = TimeFrame.new(min: time, max: time)
575
+ time_frame = TimeFrame.new(min: time, duration: 1.hour)
576
+ expect(time_frame.overlaps?(singleton_time_frame)).to be false
577
+ expect(singleton_time_frame.overlaps?(singleton_time_frame)).to be false
578
+ expect(TimeFrame::EMPTY.overlaps?(singleton_time_frame)).to be false
579
+ end
580
+ it 'returns false when other is empty' do
581
+ singleton_time_frame = TimeFrame.new(min: time, max: time)
582
+ time_frame = TimeFrame.new(min: time, duration: 1.hour)
583
+ expect(time_frame.overlaps?(TimeFrame::EMPTY)).to be false
584
+ expect(singleton_time_frame.overlaps?(TimeFrame::EMPTY)).to be false
585
+ expect(TimeFrame::EMPTY.overlaps?(TimeFrame::EMPTY)).to be false
586
+ end
587
+ end
588
+
589
+ describe '#&' do
590
+ let(:time_frame) { TimeFrame.new(min: time, duration: 3.hours) }
591
+ context 'when self is equal to other' do
592
+ let(:other) { time_frame.clone }
593
+ subject { time_frame & other }
594
+ it { should eq time_frame }
595
+ end
596
+ context 'when self covers other' do
597
+ let(:other) do
598
+ TimeFrame.new(
599
+ min: time_frame.min + 1.hour, max: time_frame.max - 1.hour
600
+ )
601
+ end
602
+ subject { time_frame & other }
603
+ it { should eq other }
604
+ end
605
+ context 'when other covers self' do
606
+ let(:other) do
607
+ TimeFrame.new(
608
+ min: time_frame.min - 1.hour, max: time_frame.max + 1.hour
609
+ )
610
+ end
611
+ subject { time_frame & other }
612
+ it { should eq time_frame }
613
+ end
614
+ context 'when self begins earlier than other' do
615
+ context 'and they are disjoint' do
616
+ let(:other) { time_frame.shift_by(time_frame.duration + 1.hour) }
617
+ subject { time_frame & other }
618
+ it { should be_empty }
619
+ end
620
+ context 'and they are overlapping' do
621
+ let(:other) { time_frame.shift_by(1.hour) }
622
+ subject { time_frame & other }
623
+ it { should eq TimeFrame.new(min: other.min, max: time_frame.max) }
624
+ end
625
+ context 'and they intersect at their boundaries' do
626
+ let(:other) { time_frame.shift_by(time_frame.duration) }
627
+ subject { time_frame & other }
628
+ it { should eq TimeFrame.new(min: time_frame.max, max: time_frame.max) }
629
+ end
630
+ end
631
+ context 'when other begins earlier than self' do
632
+ context 'and they are disjoint' do
633
+ let(:other) { time_frame.shift_by(-time_frame.duration - 1.hour) }
634
+ subject { time_frame & other }
635
+ it { should be_empty }
636
+ end
637
+ context 'and they are overlapping' do
638
+ let(:other) { time_frame.shift_by(-1.hour) }
639
+ subject { time_frame & other }
640
+ it { should eq TimeFrame.new(min: time_frame.min, max: other.max) }
641
+ end
642
+ context 'and they intersect at their boundaries' do
643
+ let(:other) { time_frame.shift_by(-time_frame.duration) }
644
+ subject { time_frame & other }
645
+ it { should eq TimeFrame.new(min: time_frame.min, max: time_frame.min) }
646
+ end
647
+ end
648
+ it 'is empty time frame when self it empty' do
649
+ expect(TimeFrame::EMPTY & time_frame).to eq TimeFrame::EMPTY
650
+ expect(TimeFrame::EMPTY & TimeFrame::EMPTY).to eq TimeFrame::EMPTY
651
+ end
652
+ it 'is empty time frame when self it not empty and the argument is empty' do
653
+ expect(time_frame & TimeFrame::EMPTY).to eq TimeFrame::EMPTY
654
+ end
655
+ end
656
+
657
+ describe '#split_by_interval' do
658
+ context 'when time frame duration is divisible by interval' do
659
+ let(:time) { Time.new(2012, 1, 1) }
660
+ let(:interval) { 1.day }
661
+ let(:time_frame) do
662
+ TimeFrame.new(min: time, duration: 7.days)
663
+ end
664
+ subject do
665
+ time_frame.split_by_interval(interval)
666
+ end
667
+
668
+ describe '#size' do
669
+ subject { super().size }
670
+ it { should eq 7 }
671
+ end
672
+ (0..6).each do |day|
673
+ it "should have the right borders on day #{day}" do
674
+ expected = TimeFrame.new(min: time, duration: interval)
675
+ expect(subject[day]).to eq expected.shift_by(day.days)
676
+ end
677
+ end
678
+ end
679
+
680
+ context 'when time frame duration is not divisible by interval' do
681
+ let(:time) { Time.new(2012, 1, 1) }
682
+ let(:interval) { 1.day }
683
+ let(:time_frame) do
684
+ TimeFrame.new(min: time, duration: 7.days + 12.hours)
685
+ end
686
+ subject do
687
+ time_frame.split_by_interval(interval)
688
+ end
689
+
690
+ describe '#size' do
691
+ subject { super().size }
692
+ it { should eq 8 }
693
+ end
694
+ (0..6).each do |day|
695
+ it "should have the right borders on day #{day}" do
696
+ expected = TimeFrame.new(min: time, duration: interval)
697
+ expect(subject[day]).to eq expected.shift_by(day.days)
698
+ end
699
+ end
700
+ it 'should have a smaller time_frame at the end' do
701
+ expected = TimeFrame.new(min: time + 7.days, duration: 12.hours)
702
+ expect(subject[7]).to eq expected
703
+ end
704
+ end
705
+
706
+ it 'returns an empty array when self is empty' do
707
+ expect(TimeFrame::EMPTY.split_by_interval(1.day)).to eq []
708
+ end
709
+ end
710
+
711
+ describe '#shift_by' do
712
+ let(:min) { time }
713
+ let(:max) { time + 2.days }
714
+ let(:time_frame) { TimeFrame.new(min: min, max: max) }
715
+ context 'when shifting into the future' do
716
+ subject { time_frame.shift_by(1.day) }
717
+
718
+ describe '#min' do
719
+ subject { super().min }
720
+ it { should eq min + 1.day }
721
+ end
722
+
723
+ describe '#max' do
724
+ subject { super().max }
725
+ it { should eq max + 1.day }
726
+ end
727
+ it { should_not equal time_frame }
728
+ end
729
+ context 'when shifting into the past' do
730
+ subject { time_frame.shift_by(-1.day) }
731
+
732
+ describe '#min' do
733
+ subject { super().min }
734
+ it { should eq min - 1.day }
735
+ end
736
+
737
+ describe '#max' do
738
+ subject { super().max }
739
+ it { should eq max - 1.day }
740
+ end
741
+ it { should_not equal time_frame }
742
+ end
743
+ context 'when shifting by 0' do
744
+ subject { time_frame.shift_by(0) }
745
+
746
+ describe '#min' do
747
+ subject { super().min }
748
+ it { should eq min }
749
+ end
750
+
751
+ describe '#max' do
752
+ subject { super().max }
753
+ it { should eq max }
754
+ end
755
+ it { should_not equal time_frame }
756
+ end
757
+ context 'when shifting back and forth' do
758
+ subject { time_frame.shift_by(-1.day).shift_by(1.day) }
759
+
760
+ describe '#min' do
761
+ subject { super().min }
762
+ it { should eq min }
763
+ end
764
+
765
+ describe '#max' do
766
+ subject { super().max }
767
+ it { should eq max }
768
+ end
769
+ it { should_not equal time_frame }
770
+ end
771
+ it 'raises a TypeError when time frame is empty' do
772
+ expect { TimeFrame::EMPTY.shift_by(1.day) }.to raise_error TypeError
773
+ end
774
+ end
775
+
776
+ describe '#shift_to' do
777
+
778
+ let(:duration) { 1.day }
779
+ let(:min) { Time.zone.local(2012, 1, 2) }
780
+ let(:max) { min + duration }
781
+ let(:time_frame) { TimeFrame.new(min: min, max: max) }
782
+
783
+ context 'when shifting to a future time' do
784
+ let(:destination) { min + duration }
785
+ subject { time_frame.shift_to(destination) }
786
+ it { should_not equal time_frame }
787
+
788
+ describe '#min' do
789
+ subject { super().min }
790
+ it { should eq destination }
791
+ end
792
+
793
+ describe '#max' do
794
+ subject { super().max }
795
+ it { should eq destination + duration }
796
+ end
797
+ end
798
+
799
+ context 'when shifting to a past time' do
800
+ let(:destination) { min - duration }
801
+ subject { time_frame.shift_to(destination) }
802
+ it { should_not equal time_frame }
803
+
804
+ describe '#min' do
805
+ subject { super().min }
806
+ it { should eq destination }
807
+ end
808
+
809
+ describe '#max' do
810
+ subject { super().max }
811
+ it { should eq destination + duration }
812
+ end
813
+ end
814
+
815
+ context 'when shifting to same time' do
816
+ let(:destination) { min }
817
+ subject { time_frame.shift_to(destination) }
818
+ it { should_not equal time_frame }
819
+
820
+ describe '#min' do
821
+ subject { super().min }
822
+ it { should eq destination }
823
+ end
824
+
825
+ describe '#max' do
826
+ subject { super().max }
827
+ it { should eq destination + duration }
828
+ end
829
+ end
830
+
831
+ it 'raises a TypeError when time frame is empty' do
832
+ expect do
833
+ TimeFrame::EMPTY.shift_to(Time.zone.local(2012, 1, 2))
834
+ end.to raise_error TypeError
835
+ end
836
+ end
837
+
838
+ describe '#without' do
839
+ let(:time_frame) { TimeFrame.new(min: time, duration: 10.hours) }
840
+ context 'when the arguments do not intersect' do
841
+ context 'and do not touch the boundaries' do
842
+ let(:arg) do
843
+ shift = time_frame.duration + 1.hour
844
+ [
845
+ TimeFrame.new(min: time - 2.hours, duration: 1.hour),
846
+ TimeFrame.new(min: time + shift, duration: 1.hour)
847
+ ]
848
+ end
849
+ subject { time_frame.without(*arg) }
850
+ it { should eq [time_frame] }
851
+ end
852
+ context 'and they touch boundaries' do
853
+ let(:arg) do
854
+ [
855
+ TimeFrame.new(min: time - 1.hour, duration: 1.hour),
856
+ TimeFrame.new(min: time + time_frame.duration, duration: 1.hour)
857
+ ]
858
+ end
859
+ subject { time_frame.without(*arg) }
860
+ it { should eq [time_frame] }
861
+ end
862
+ end
863
+ context 'when the arguments intersect' do
864
+ context 'and the argument time_frames overlaps themself' do
865
+ let(:arg) do
866
+ [
867
+ TimeFrame.new(min: time + 1.hour, duration: 2.hours),
868
+ TimeFrame.new(min: time + 2.hours, duration: 2.hours)
869
+ ]
870
+ end
871
+ let(:expected) do
872
+ [
873
+ TimeFrame.new(min: time_frame.min, duration: 1.hour),
874
+ TimeFrame.new(min: time + 4.hours, max: time_frame.max)
875
+ ]
876
+ end
877
+ subject { time_frame.without(*arg) }
878
+ it { should eq expected }
879
+ end
880
+ context 'and they cover self' do
881
+ let(:arg) do
882
+ duration = 0.5 * time_frame.duration
883
+ [
884
+ TimeFrame.new(min: time, duration: duration),
885
+ TimeFrame.new(min: time + duration, duration: duration)
886
+ ]
887
+ end
888
+ subject { time_frame.without(*arg) }
889
+ it { should eq [] }
890
+ end
891
+ context 'and they overlap at the boundaries' do
892
+ let(:arg) do
893
+ shift = time_frame.duration - 1.hour
894
+ [
895
+ TimeFrame.new(min: time - 1.hour, duration: 2.hour),
896
+ TimeFrame.new(min: time + shift, duration: 2.hour)
897
+ ]
898
+ end
899
+ let(:expected) do
900
+ [
901
+ TimeFrame.new(min: time_frame.min + 1.hour,
902
+ max: time_frame.max - 1.hour)
903
+ ]
904
+ end
905
+ subject { time_frame.without(*arg) }
906
+ it { should eq expected }
907
+ end
908
+ context 'and we have three time_frames in args overlaped by self' do
909
+ context 'which are sorted' do
910
+ let(:arg) do
911
+ [
912
+ TimeFrame.new(min: time + 1.hour, duration: 2.hour),
913
+ TimeFrame.new(min: time + 4.hours, duration: 2.hour),
914
+ TimeFrame.new(min: time + 7.hours, duration: 2.hour)
915
+ ]
916
+ end
917
+ let(:expected) do
918
+ [
919
+ TimeFrame.new(min: time, max: time + 1.hour),
920
+ TimeFrame.new(min: time + 3.hours, max: time + 4.hour),
921
+ TimeFrame.new(min: time + 6.hours, max: time + 7.hours),
922
+ TimeFrame.new(min: time + 9.hours, max: time + 10.hours)
923
+ ]
924
+ end
925
+ subject { time_frame.without(*arg) }
926
+ it { should eq expected }
927
+ end
928
+ context 'and they are unsorted' do
929
+ let(:arg) do
930
+ [
931
+ TimeFrame.new(min: time + 4.hours, duration: 2.hour),
932
+ TimeFrame.new(min: time + 1.hour, duration: 2.hour),
933
+ TimeFrame.new(min: time + 7.hours, duration: 2.hour)
934
+ ]
935
+ end
936
+ let(:expected) do
937
+ [
938
+ TimeFrame.new(min: time, max: time + 1.hour),
939
+ TimeFrame.new(min: time + 3.hours, max: time + 4.hour),
940
+ TimeFrame.new(min: time + 6.hours, max: time + 7.hours),
941
+ TimeFrame.new(min: time + 9.hours, max: time + 10.hours)
942
+ ]
943
+ end
944
+ subject { time_frame.without(*arg) }
945
+ it { should eq expected }
946
+ end
947
+ end
948
+ end
949
+
950
+ it 'returns self (as a singleton array) if there are no arguments' do
951
+ expect(time_frame.without).to eq [time_frame]
952
+ end
953
+
954
+ it 'returns an empty array if self is empty' do
955
+ expect(TimeFrame::EMPTY.without(time_frame)).to eq []
956
+ end
957
+
958
+ it 'ignores empty time ranges within the arguments' do
959
+ expect(time_frame.without(TimeFrame::EMPTY)).to eq [time_frame]
960
+ end
961
+ end
962
+
963
+ describe '.covering_time_frame_for' do
964
+
965
+ context 'for a single time frame' do
966
+ let(:time_frame) { TimeFrame.new(min: time, duration: 1.hour) }
967
+ subject { TimeFrame.covering_time_frame_for([time_frame]) }
968
+ it { should eq time_frame }
969
+ end
970
+
971
+ context 'for multiple time frames' do
972
+ let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
973
+ let(:time_frame2) { time_frame1.shift_by(-1.hour) }
974
+ let(:time_frame3) { time_frame1.shift_by(3.hours) }
975
+ subject do
976
+ TimeFrame.covering_time_frame_for(
977
+ [time_frame1, time_frame2, time_frame3]
978
+ )
979
+ end
980
+
981
+ describe '#min' do
982
+ subject { super().min }
983
+ it { should eq time_frame2.min }
984
+ end
985
+
986
+ describe '#max' do
987
+ subject { super().max }
988
+ it { should eq time_frame3.max }
989
+ end
990
+ end
991
+
992
+ it 'returns the empty time frame if the array is empty' do
993
+ expect(TimeFrame.covering_time_frame_for([])).to eq TimeFrame::EMPTY
994
+ end
995
+
996
+ it 'ignores empty time frames' do
997
+ time_frame = TimeFrame.new(min: time, duration: 2.hours)
998
+ expect(
999
+ TimeFrame.covering_time_frame_for([time_frame, TimeFrame::EMPTY])
1000
+ ).to eq time_frame
1001
+ end
1002
+ end
1003
+
1004
+ describe '.each_overlap' do
1005
+
1006
+ # Visualization of example input:
1007
+ #
1008
+ # array1: |---|-------| |-------|-----------|
1009
+ # array2: |-----------| |---| |---| |---|
1010
+ #
1011
+ # 0 1 2 3 4 5 6 7 8 9 10 11
1012
+
1013
+ let(:array1) do
1014
+ [
1015
+ TimeFrame.new(min: time, max: time + 1.hour),
1016
+ TimeFrame.new(min: time + 1.hour, max: time + 3.hours),
1017
+ TimeFrame.new(min: time + 4.hours, max: time + 6.hours),
1018
+ TimeFrame.new(min: time + 6.hours, max: time + 9.hours)
1019
+ ]
1020
+ end
1021
+
1022
+ let(:array2) do
1023
+ [
1024
+ TimeFrame.new(min: time + 2.hours, max: time + 5.hour),
1025
+ TimeFrame.new(min: time + 6.hour, max: time + 7.hours),
1026
+ TimeFrame.new(min: time + 8.hours, max: time + 9.hours),
1027
+ TimeFrame.new(min: time + 10.hours, max: time + 11.hours),
1028
+ TimeFrame::EMPTY
1029
+ ]
1030
+ end
1031
+
1032
+ it 'yields the block for each overlap' do
1033
+ overlaps = []
1034
+ TimeFrame.each_overlap(array1, array2) { |a, b| overlaps << [a, b] }
1035
+ expect(overlaps).to eq [
1036
+ [array1[1], array2[0]],
1037
+ [array1[2], array2[0]],
1038
+ [array1[3], array2[1]],
1039
+ [array1[3], array2[2]]
1040
+ ]
1041
+ end
1042
+
1043
+ it 'still works when switching arguments' do
1044
+ overlaps = []
1045
+ TimeFrame.each_overlap(array2, array1) { |a, b| overlaps << [a, b] }
1046
+ expect(overlaps).to eq [
1047
+ [array2[0], array1[1]],
1048
+ [array2[0], array1[2]],
1049
+ [array2[1], array1[3]],
1050
+ [array2[2], array1[3]]
1051
+ ]
1052
+ end
1053
+
1054
+ it 'works if first array is empty' do
1055
+ overlaps = []
1056
+ TimeFrame.each_overlap([], array2) { |a, b| overlaps << [a, b] }
1057
+ expect(overlaps).to be_empty
1058
+ end
1059
+
1060
+ it 'works if second array is empty' do
1061
+ overlaps = []
1062
+ TimeFrame.each_overlap(array1, []) { |a, b| overlaps << [a, b] }
1063
+ expect(overlaps).to be_empty
1064
+ end
1065
+ end
1066
+
1067
+ describe '#inspect' do
1068
+ it 'works for a TimeFrame with same min and max' do
1069
+ time = Time.now
1070
+ expected = "#{time}..#{time}"
1071
+ tr = TimeFrame.new(min: time, max: time)
1072
+ actual = tr.inspect
1073
+ expect(actual).to eq expected
1074
+ end
1075
+
1076
+ it 'works for a TimeFrame created with min and max' do
1077
+ min = Time.now
1078
+ max = min + 10.minutes
1079
+ expected = "#{min}..#{max}"
1080
+ tr = TimeFrame.new(min: min, max: max)
1081
+ actual = tr.inspect
1082
+ expect(actual).to eq expected
1083
+ end
1084
+
1085
+ it 'works for a TimeFrame created with min and duration' do
1086
+ min = Time.now
1087
+ max = min + 10.minutes
1088
+ expected = "#{min}..#{max}"
1089
+ tr = TimeFrame.new(min: min, duration: 10.minutes)
1090
+ actual = tr.inspect
1091
+ expect(actual).to eq expected
1092
+ end
1093
+
1094
+ it 'is overridden for empty time frames' do
1095
+ expect(TimeFrame::EMPTY.inspect).to eq 'EMPTY'
1096
+ end
1097
+ end
1098
+ end