time_frame 0.0.6 → 0.1.0

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