time_span 0.0.1.alpha03 → 0.0.1.alpha04

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
1
  #require "time_span/version"
2
- require 'date'
3
2
 
4
3
  module TimeSpan
5
- class TimeSpan
4
+
5
+ class TimeSpan
6
6
 
7
7
  attr_accessor :starts, :ends # RelativeTime objects
8
8
 
@@ -11,6 +11,7 @@ module TimeSpan
11
11
  ##################################
12
12
 
13
13
  def initialize(starting_at, ending_at)
14
+ raise "Cannot make a span unless both points are on the same timeline" unless starting_at.colinear_with?(ending_at)
14
15
  self.starts = starting_at
15
16
  self.ends = ending_at
16
17
  starting_at.kind_of?(RelativeTime) && ending_at.kind_of?(RelativeTime)
@@ -85,6 +86,10 @@ module TimeSpan
85
86
  ends_with?(b) && starts_with?(b)
86
87
  end
87
88
 
89
+ def != (b)
90
+ !end_with(b) || !starts_with(b)
91
+ end
92
+
88
93
  def < (b)
89
94
  ends_before_other_starts?(b)
90
95
  end
@@ -110,8 +115,147 @@ module TimeSpan
110
115
  end
111
116
 
112
117
  end
118
+ #
119
+ class TimeLine < Array
120
+
121
+ attr_accessor :line, :indices_of, :name
122
+
123
+ def to_s
124
+ name.to_s
125
+ end
126
+
127
+ def initialize(name="")
128
+ @name = name
129
+ @line = []
130
+ @indices_of = {}
131
+ end
132
+
133
+ def position_of(obj)
134
+ @indices_of[obj]
135
+ end
136
+
137
+ def append(obj)
138
+ insert_at(@line.size, obj)
139
+ end
140
+
141
+ def increase_after(pos, by=1)
142
+ @indices_of.each_key do |key|
143
+ @indices_of[key] += by if (@indices_of[key] >= pos)
144
+ end
145
+ end
146
+
147
+ def append_to_next(relative_obj, obj, relative=1)
148
+ insert_at(position_of(relative_obj)+relative, obj)
149
+ end
150
+
151
+ def insert_before_next(relative_obj, obj, relative_offset=1)
152
+ relative_position = position_of(relative_obj)
153
+ increase_after(relative_position + relative_offset, relative_offset)
154
+ @line[relative_position + relative_offset,0] = nil
155
+ insert_at(relative_position+relative_offset, obj)
156
+ end
157
+
158
+ ## needs work for RelativeTime objects
159
+ def insert_at(pos, obj)
160
+
161
+ raise "can only add a time to its own timeline" unless obj.timeline.equal? self
162
+ if @line[pos].nil?
163
+ @line[pos] = [obj]
164
+ @indices_of[obj] = pos
165
+ else
166
+ op = @line[pos].kind_of?(Array) ? '<<' : '='
167
+ @line[pos].send(op.to_sym, obj) unless @line[pos].include?(obj) # no dupliates
168
+ @indices_of[obj] = pos # dup overwrites
169
+ end
170
+ end
171
+
172
+ def remove(obj)
173
+ # cannot remove [] or the @indices_of will be wrong
174
+ # call to #compress to remove the extra []s
175
+ pos = position_of(obj)
176
+
177
+ if pos # do nothing if it isn't there'
178
+ @line[pos].delete(obj)
179
+ @indices_of.delete(obj)
180
+ end
181
+
182
+ end
183
+
184
+ ## removes all [] elements, and decrements accordingly the @indices_of
185
+ ## ideally this should be transactional
186
+
187
+ def compress!
188
+
189
+ mod_level = 0
190
+ offsets = []
191
+ 0.upto(line.size-1) do |i|
192
+ mod_level -= 1 if @line[i].empty?
193
+ offsets << mod_level
194
+ end
195
+
196
+ ## poor man's transaction. Don't do directly on indices_of so less chance of interruption
197
+ indices = indices_of
198
+
199
+ ## refactor to use increase_after
200
+ indices.each_key do |key|
201
+ indices[key] = indices[key] + offsets[indices[key]]
202
+ end
203
+
204
+ indices_of = indices
205
+ @line.delete([])
206
+ end
207
+
208
+ end
209
+
210
+ ###########################################################################################
211
+ # #
212
+ # A relative time must be able to be have equal times #
213
+ # implemented as an Array of Arrays; all elements of the 'subarray' are equal in time, #
214
+ # otherwise, the time relationship are based on position in the main array #
215
+ # #
216
+ # diff cannot be done, it makes no sense, due to the fuzziness #
217
+ # #
218
+ # RelativeTime must be within a TimeLine #
219
+ ###########################################################################################
220
+
221
+ class RelativeTime
222
+
223
+ attr_accessor :timeline, :reference_to # reference_to should respond_to? :to_s
224
+
225
+ # create a realtive time *within a timeline* after position
226
+ def initialize tline, ref
227
+ @timeline= tline
228
+ @reference_to= ref
229
+ end
230
+
231
+ def to_s
232
+ reference_to.to_s
233
+ end
234
+
235
+ ## any method on fixnum with 1 RelativeTime param can be in the list below
236
+ %w{< <= == != >= >}.each{ |meth|
237
+ self.send(:define_method, meth) {|b|
238
+ raise "can only compare to other times on the same timeline." unless valid_and_comparable_with?(b) # can NOT compare across TimeLines
239
+ self.timeline.position_of(self).send(meth, b.timeline.position_of(b))
240
+ }
241
+ }
242
+
243
+ def positioned?
244
+ self.timeline.indices_of.keys.include?(self)
245
+ end
246
+
247
+ def colinear_with?(b)
248
+ self.timeline.equal?(b.timeline)
249
+ end
250
+
251
+
252
+ protected
253
+
254
+ def valid_and_comparable_with?(b)
255
+ !self.timeline.nil? && !b.timeline.nil? && colinear_with?(b)
256
+ end
257
+
113
258
 
114
- # first implementation is just as DateTime; later add fuzzy time
115
- class RelativeTime < ::DateTime
116
259
  end
260
+
117
261
  end
@@ -1,3 +1,3 @@
1
1
  module TimeSpan
2
- VERSION = "0.0.1.alpha03"
2
+ VERSION = "0.0.1.alpha04"
3
3
  end
@@ -2,82 +2,222 @@ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
2
 
3
3
  describe "TimeSpan" do
4
4
 
5
- context "TimeSpan" do
5
+ let(:timeline) do
6
+ TimeSpan::TimeLine.new "test timeline"
7
+ end
8
+
9
+ let (:time_a) do
10
+ TimeSpan::RelativeTime.new timeline, "Some timepoint a"
11
+ end
12
+
13
+ let (:time_b) do
14
+ TimeSpan::RelativeTime.new timeline, "Some timepoint b"
15
+ end
16
+
17
+ let (:time_c) do
18
+ TimeSpan::RelativeTime.new timeline, "Some timepoint c"
19
+ end
20
+
21
+ let (:time_d) do
22
+ TimeSpan::RelativeTime.new timeline, "Some timepoint d"
23
+ end
24
+
25
+ before(:each) do
26
+ timeline.append time_a
27
+ timeline.append time_b
28
+ timeline.append time_c
29
+ end
30
+
31
+ let (:time_span) do
32
+ TimeSpan::TimeSpan.new(time_a, time_c)
33
+ end
34
+
35
+ context "TimeSpan::TimeLine" do
6
36
 
7
37
  context "instance methods" do
8
38
 
9
- let (:alpha_start) do
10
- TimeSpan::RelativeTime.parse("Jan 1, 2007")
39
+ context "insertion" do
40
+
41
+ it "inserts a TimeSpan::RelativeTime into the timeline." do
42
+ timeline.line.should_not be_empty
43
+ end
44
+
45
+ it 'populates the indices_of as well' do
46
+ timeline.indices_of.should_not be_empty
47
+ end
48
+
49
+ it "won't insert into the wrong timeline" do
50
+ timeline_b = TimeSpan::TimeLine.new "Another Timeline"
51
+ lambda {
52
+ timeline_b.append(time_a) }.should raise_error RuntimeError
53
+ end
54
+
55
+ it "populates the index hash" do
56
+ timeline.indices_of.should_not be_empty
57
+ end
58
+
59
+ it "appends to the next element if it exists" do
60
+ timeline.append_to_next time_a, time_d
61
+ timeline.line.should == [[time_a], [time_b, time_d], [time_c]]
62
+ end
63
+
64
+ it "inserts after and creates if element does not exist" do
65
+ timeline.append_to_next time_c, time_d
66
+ timeline.line.should == [[time_a], [time_b], [time_c], [time_d]]
67
+ end
68
+
69
+ it "inserts after a given time with insert_next" do
70
+ timeline.insert_before_next time_a, time_d
71
+ timeline.line.should == [[time_a], [time_d], [time_b], [time_c]]
72
+ end
73
+
74
+ it "should adjust the indices when inserting next" do
75
+ timeline.insert_before_next time_a, time_d
76
+ timeline.indices_of[time_b].should == 2
77
+ end
78
+
11
79
  end
12
80
 
13
- let (:alpha_end) do
14
- alpha_start + 2000
81
+ context "appending" do
82
+
83
+ it "appends to the timeline" do
84
+ timeline.append time_d
85
+ timeline.indices_of[time_d].should == 3
86
+ end
87
+
88
+ it "has only one RelativeTime after appending to an empty timeline" do
89
+ timeline.append time_d
90
+ timeline.line.size.should be(4)
91
+ end
92
+
15
93
  end
16
94
 
95
+ context "finding" do
96
+
97
+ it "finds an inserted time_span" do
98
+ timeline.position_of(time_b).should == 1
99
+ end
17
100
 
18
- let (:alpha) do
19
- TimeSpan::TimeSpan.new(alpha_start, alpha_end)
20
101
  end
21
102
 
22
- context "single time point comparators" do
103
+ context "removal" do
23
104
 
24
- context "same ends time, alpha starts before beta" do
105
+ it "removes the content with #remove(obj)" do
106
+ timeline.remove time_b
107
+ timeline.position_of(time_b).should be_nil
108
+ end
25
109
 
26
- let (:beta_start) do
27
- TimeSpan::RelativeTime.parse("Jan 1, 2008")
28
- end
110
+ it "removes from the index as well" do
111
+ timeline.remove time_b
112
+ timeline.indices_of[time_b].should be_nil
113
+ end
29
114
 
30
- let (:beta) do
31
- TimeSpan::TimeSpan.new(beta_start, alpha_end)
32
- end
115
+ it "does nothing when asked to remove a non-existent object" do
116
+ lambda { timeline.remove(time_d)}.should_not change(timeline, :indices_of)
117
+ end
118
+
119
+ end
120
+
121
+ context "compression" do
122
+
123
+ it "returns identity when compressing full timeline" do
124
+ timeline.compress!
125
+ timeline.line.size.should be 3
126
+ end
127
+
128
+ it "does not remove from index position when not compressing" do
129
+ timeline.remove time_b
130
+ timeline.indices_of[time_c].should == 2
131
+ end
33
132
 
34
- context "alpha" do
133
+ it "removes from the index when compressing" do
134
+ timeline.remove time_b
135
+ timeline.compress!
136
+ timeline.indices_of[time_c].should == 1
137
+ end
35
138
 
36
- it "starts before beta" do
37
- alpha.should be_starts_before(beta)
38
- end
139
+ it "removes content positions from the timeline when compressing" do
140
+ timeline.remove time_b
141
+ timeline.compress!
142
+ timeline.line.should == [[time_a], [time_c]]
143
+ end
39
144
 
145
+ end
146
+
147
+ context "finding position" do
148
+
149
+ it "returns the position of a RelativeTime" do
150
+ timeline.position_of(time_b).should == 1
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+
157
+ end
40
158
 
41
- it "ends with beta" do
42
- alpha.should be_ends_with(beta)
43
- end
159
+ context "TimeSpan::TimeSpan" do
44
160
 
161
+ context "instance methods" do
162
+
163
+ context "creation" do
164
+
165
+ it "should not allow creation when RelativeTime elements which are not on the same TimeLine" do
166
+ other_timeline = TimeSpan::TimeLine.new "another timeline"
167
+ time_other = TimeSpan::RelativeTime.new other_timeline, "Time on other timeline"
168
+ lambda {
169
+ TimeSpan::TimeSpan.new(time_a, time_other)
170
+ }.should raise_error
171
+ end
172
+
173
+ end
174
+
175
+ context "single time point comparators" do
176
+
177
+ context "same end time, time_span starts before other time_span" do
178
+
179
+ let (:time_span_2) do
180
+ TimeSpan::TimeSpan.new(time_b, time_c)
45
181
  end
46
182
 
47
- context "alpha does not" do
183
+ it "start before other time_span" do
184
+ time_span.should be_starts_before(time_span_2)
185
+ end
48
186
 
49
- it "start after beta" do
50
- alpha.should_not be_starts_after(beta)
51
- end
187
+ it "end with other time_span" do
188
+ time_span.should be_ends_with(time_span_2)
189
+ end
52
190
 
53
- it "start with beta" do
54
- alpha.should_not be_starts_with(beta)
55
- end
191
+ it "not start after other time_span" do
192
+ time_span.should_not be_starts_after(time_span_2)
193
+ end
56
194
 
57
- it "end before beta" do
58
- alpha.should_not be_ends_before(beta)
59
- end
195
+ it "not start with other time_span" do
196
+ time_span.should_not be_starts_with(time_span_2)
197
+ end
60
198
 
61
- it "end after beta" do
62
- alpha.should_not be_ends_after(beta)
63
- end
199
+ it "not end before other time_span" do
200
+ time_span.should_not be_ends_before(time_span_2)
201
+ end
64
202
 
65
- it "end before beta starts" do
66
- alpha.should_not be_ends_before_other_starts(beta)
67
- end
203
+ it "end after other time_span" do
204
+ time_span.should_not be_ends_after(time_span_2)
205
+ end
68
206
 
69
- it "end as beta starts" do
70
- alpha.should_not be_ends_as_other_starts(beta)
71
- end
207
+ it "end before other time_span starts" do
208
+ time_span.should_not be_ends_before_other_starts(time_span_2)
209
+ end
72
210
 
73
- it "start after beta ends" do
74
- alpha.should_not be_starts_after_other_ends(beta)
75
- end
211
+ it "end as other time_span starts" do
212
+ time_span.should_not be_ends_as_other_starts(time_span_2)
213
+ end
76
214
 
77
- it "start as beta ends" do
78
- alpha.should_not be_starts_as_other_ends(beta)
79
- end
215
+ it "start after other time_span ends" do
216
+ time_span.should_not be_starts_after_other_ends(time_span_2)
217
+ end
80
218
 
219
+ it "start as other time_span ends" do
220
+ time_span.should_not be_starts_as_other_ends(time_span_2)
81
221
  end
82
222
 
83
223
  end
@@ -86,63 +226,65 @@ describe "TimeSpan" do
86
226
 
87
227
  context "time span comparators" do
88
228
 
89
- let (:beta_earliest) do
90
- alpha_start - 1000
229
+ let (:time_d) do
230
+ TimeSpan::RelativeTime.new timeline, "Some timepoint d"
91
231
  end
92
232
 
93
- let (:beta_before_alpha_starts) do
94
- alpha_start - 500
233
+ let (:time_e) do
234
+ TimeSpan::RelativeTime.new timeline, "Some timepoint e"
95
235
  end
96
236
 
97
- let (:beta_after_alpha_starts) do
98
- alpha_start + 500
237
+ before(:each) do
238
+ timeline.append time_a
239
+ timeline.append time_b
240
+ timeline.append time_c
241
+ timeline.append time_d
242
+ timeline.append time_e
99
243
  end
100
244
 
101
- let (:beta_before_alpha_ends) do
102
- alpha_start + 1000
245
+ let (:time_span_mid) do
246
+ TimeSpan::TimeSpan.new(time_b, time_c)
103
247
  end
104
248
 
105
- let (:beta_right_after_alpha_ends) do
106
- alpha_end + 500
249
+ let(:time_span_all) do
250
+ TimeSpan::TimeSpan.new(time_a, time_e)
107
251
  end
108
252
 
109
- let (:beta_last) do
110
- alpha_end + 1000
253
+ let (:time_span_last) do
254
+ TimeSpan::TimeSpan.new(time_d, time_e)
111
255
  end
112
256
 
113
257
  it "identical start and end times should return == as true" do
114
- beta = TimeSpan::TimeSpan.new(alpha_start, alpha_end)
115
- alpha.should == beta
258
+ time_span_3 = TimeSpan::TimeSpan.new(time_a, time_c)
259
+ time_span.should == time_span_3
116
260
  end
117
261
 
118
- it "alpha ends before beta starts, should return alpha < beta" do
119
- beta = TimeSpan::TimeSpan.new(beta_right_after_alpha_ends, beta_last)
120
- alpha.should < beta
262
+ it "any differences should return not equal" do
263
+ time_span_all.should_not == time_span # end times differ
121
264
  end
122
265
 
123
- it "alpha starts after beta ends, should return alpha > beta" do
124
- beta = TimeSpan::TimeSpan.new(beta_earliest, beta_before_alpha_starts)
125
- alpha.should > beta
266
+ it "time_span ends before time_span_last starts, should return time_span < time_span_last" do
267
+ time_span.should < time_span_last
126
268
  end
127
269
 
128
- it "alpha starting after and ending before beta should be contained inside beta" do
129
- beta = TimeSpan::TimeSpan.new(beta_earliest, beta_last)
130
- alpha.should be_contained_fully_inside(beta)
270
+ it "time_span starts after time_span_last ends, should return time_span_last > time_span" do
271
+ time_span_last.should > time_span
131
272
  end
132
273
 
133
- it "alpha starting with beta but ending before, is contained inside" do
134
- beta = TimeSpan::TimeSpan.new(alpha_start, beta_last)
135
- alpha.should be_contained_inside(beta)
274
+ it "time_span starting after and ending before time_span_mid should be contained inside time_span_mid" do
275
+ time_span_mid.should be_contained_fully_inside(time_span_all)
136
276
  end
137
277
 
138
- it "alpha starting before and ending after full contains beta" do
139
- beta = TimeSpan::TimeSpan.new(beta_after_alpha_starts, beta_before_alpha_ends)
140
- alpha.should be_contains_fully(beta)
278
+ it "time_span starting with time_span_all but ending before, is contained inside" do
279
+ time_span.should be_contained_inside(time_span_all)
141
280
  end
142
281
 
143
- it "alpha starting before but ending with beta should contain beta" do
144
- beta = TimeSpan::TimeSpan.new(beta_after_alpha_starts, alpha_end)
145
- alpha.should be_contains(beta)
282
+ it "time_span starting before and ending after time_span_full contains time_span_mid" do
283
+ time_span_all.should be_contains_fully(time_span_mid)
284
+ end
285
+
286
+ it "time_span starting before but ending with time_span_mid should contain time_span_mmid" do
287
+ time_span.should be_contains(time_span_mid)
146
288
  end
147
289
 
148
290
  end
@@ -151,6 +293,35 @@ describe "TimeSpan" do
151
293
 
152
294
  end
153
295
 
296
+ context "TimeSpan::RelativeTime" do
297
+
298
+ context "instance methods" do
299
+
300
+ ## comparators make sense only in TimeLine context, not tested here.
154
301
 
302
+ let (:timeline) do
303
+ TimeSpan::TimeLine.new "Some timeline"
304
+ end
305
+
306
+ let (:time) do
307
+ TimeSpan::RelativeTime.new timeline, "time point"
308
+ end
309
+
310
+ it "returns a string if using '.to_s'" do
311
+ time.to_s.should == 'time point'
312
+ end
313
+
314
+ it "is not positioned if not appended" do
315
+ time.should_not be_positioned
316
+ end
317
+
318
+ it 'is positioned once appended.' do
319
+ timeline.append time
320
+ time.should be_positioned
321
+ end
322
+
323
+ end
324
+
325
+ end
155
326
 
156
327
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_span
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.alpha03
4
+ version: 0.0.1.alpha04
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-31 00:00:00.000000000 Z
12
+ date: 2012-02-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &79762560 !ruby/object:Gem::Requirement
16
+ requirement: &79260830 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *79762560
24
+ version_requirements: *79260830
25
25
  description: Time spans, including many comparators
26
26
  email:
27
27
  - craig.a.cook@gmail.com