time_span 0.0.1.alpha03 → 0.0.1.alpha04

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.
@@ -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