timed 0.1.0 → 0.2.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 +4 -4
- data/README.md +1 -0
- data/lib/timed/item.rb +18 -19
- data/lib/timed/moment.rb +0 -13
- data/lib/timed/sequence.rb +169 -18
- data/lib/timed/version.rb +1 -1
- data/timed.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51ecc6d39d16ae5d47b2b658519e10bf6eb16098
|
4
|
+
data.tar.gz: e6f32281f459d65b349774944625d7232c95654b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ceb78b7e82c7f1ab1de38cd7747a681261906d3b8517324a26245f899346cbbd749cec81e6bd54fdadf34d21b1ea0095d27ddf8c39148fc0693c3a5d95aa4901
|
7
|
+
data.tar.gz: d97ed62d7ca6e1c787749b824cdfd8c64790b8662020ba7cfde66b240a4e9dd9567476f968cc25be83412538871ac62247447eae2c23e9a7c11fafc2728ca4b2
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Timed
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/timed)
|
3
4
|
[](https://travis-ci.org/seblindberg/ruby-timed)
|
4
5
|
[](https://coveralls.io/github/seblindberg/ruby-timed?branch=master)
|
5
6
|
[](http://inch-ci.org/github/seblindberg/ruby-timed)
|
data/lib/timed/item.rb
CHANGED
@@ -4,15 +4,22 @@ module Timed
|
|
4
4
|
# The Timed Item is a Moment that can be chained to others to form a sequence.
|
5
5
|
# Importantly, items in a sequence are guaranteed to be sequential and non
|
6
6
|
# overlapping.
|
7
|
-
|
7
|
+
|
8
8
|
class Item < Linked::Item
|
9
9
|
include Moment
|
10
|
+
|
10
11
|
# The value field is used to store the timespan and shuold not be accessed
|
11
12
|
# directly.
|
12
13
|
|
13
14
|
protected :value
|
14
15
|
private :value=
|
15
16
|
|
17
|
+
# Provide a more ideomatic accessor for the sequence that the item is part
|
18
|
+
# of.
|
19
|
+
|
20
|
+
alias sequence list
|
21
|
+
alias in_sequence? in_list?
|
22
|
+
|
16
23
|
# Creates a new Timed Item from a timespan. A timespan is any object that
|
17
24
|
# responds to #begin and #end with two numerical values. Note that the end
|
18
25
|
# must occur after the start for the span to be valid.
|
@@ -22,9 +29,10 @@ module Timed
|
|
22
29
|
#
|
23
30
|
# timespan - object responding to #begin and #end.
|
24
31
|
# end_at - if given, it togheter with the first argument will be used as the
|
25
|
-
# begin and end time for the item
|
32
|
+
# begin and end time for the item.
|
33
|
+
# sequence - optional sequence to add the item to.
|
26
34
|
|
27
|
-
def initialize(timespan, end_at = nil)
|
35
|
+
def initialize(timespan, end_at = nil, sequence: nil)
|
28
36
|
if end_at
|
29
37
|
begin_at = timespan
|
30
38
|
else
|
@@ -35,34 +43,21 @@ module Timed
|
|
35
43
|
raise TypeError unless begin_at.is_a?(Numeric) && end_at.is_a?(Numeric)
|
36
44
|
raise ArgumentError if end_at < begin_at
|
37
45
|
|
38
|
-
super begin_at..end_at
|
46
|
+
super begin_at..end_at, list: sequence
|
39
47
|
end
|
40
48
|
|
41
49
|
# Returns the time when the item starts.
|
42
50
|
|
43
51
|
def begin
|
44
|
-
value.begin
|
52
|
+
offset value.begin
|
45
53
|
end
|
46
54
|
|
47
55
|
# Returns the time when the item ends.
|
48
56
|
|
49
57
|
def end
|
50
|
-
value.end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns a new Item in the intersection
|
54
|
-
#
|
55
|
-
# other - object that implements both #begin and #end.
|
56
|
-
|
57
|
-
def intersect(other)
|
58
|
-
begin_at = self.begin >= other.begin ? self.begin : other.begin
|
59
|
-
end_at = self.end <= other.end ? self.end : other.end
|
60
|
-
|
61
|
-
begin_at <= end_at ? self.class.new(begin_at, end_at) : nil
|
58
|
+
offset value.end
|
62
59
|
end
|
63
60
|
|
64
|
-
alias & intersect
|
65
|
-
|
66
61
|
# Inserts an item after this one and before the next in the sequence. The
|
67
62
|
# new item may not overlap with the two it sits between. A RuntimeError will
|
68
63
|
# be raised if it does.
|
@@ -92,5 +87,9 @@ module Timed
|
|
92
87
|
|
93
88
|
super
|
94
89
|
end
|
90
|
+
|
91
|
+
protected def offset(time)
|
92
|
+
in_sequence? ? sequence.offset(time) : time
|
93
|
+
end
|
95
94
|
end
|
96
95
|
end
|
data/lib/timed/moment.rb
CHANGED
@@ -81,19 +81,6 @@ module Timed
|
|
81
81
|
|
82
82
|
alias & intersect
|
83
83
|
|
84
|
-
# Compare the moments with others.
|
85
|
-
#
|
86
|
-
# Return -1 if the item is before the other, 0 if they overlap and 1 if the
|
87
|
-
# item is after the other.
|
88
|
-
|
89
|
-
# def <=>(other)
|
90
|
-
# case
|
91
|
-
# when before?(other) then -1
|
92
|
-
# when after?(other) then 1
|
93
|
-
# else 0
|
94
|
-
# end
|
95
|
-
# end
|
96
|
-
|
97
84
|
# Override the default implementation and provide a more useful
|
98
85
|
# representation of the Timed Moment, including when it begins and ends.
|
99
86
|
#
|
data/lib/timed/sequence.rb
CHANGED
@@ -2,8 +2,8 @@ module Timed
|
|
2
2
|
# Sequence
|
3
3
|
#
|
4
4
|
# This class implements a sequence of Timed Items. Any object that implements
|
5
|
-
# the methods #begin and #end can be
|
6
|
-
# must be inserted in chronological order, or the sequence will raise an
|
5
|
+
# the methods #begin and #end can be added to the sequence. Note that the
|
6
|
+
# items must be inserted in chronological order, or the sequence will raise an
|
7
7
|
# exception.
|
8
8
|
#
|
9
9
|
# Example
|
@@ -13,11 +13,19 @@ module Timed
|
|
13
13
|
#
|
14
14
|
# A sequence can also be treated like a Moment and be compared, in time, with
|
15
15
|
# other compatible objects.
|
16
|
-
|
16
|
+
#
|
17
|
+
# Sequences also provide a mechanism to offset the items in it, in time by
|
18
|
+
# providing the #offset method. Items can use it to offset their begin and end
|
19
|
+
# times on the fly.
|
20
|
+
|
17
21
|
class Sequence
|
18
22
|
include Moment
|
19
23
|
include Linked::List
|
20
24
|
|
25
|
+
# Provide a more ideomatic name for the identity method #list.
|
26
|
+
|
27
|
+
alias sequence list
|
28
|
+
|
21
29
|
# Returns the time at which the first item in the sequence, and therefore
|
22
30
|
# the sequence as a whole, begins. If the sequence is empty, by convention,
|
23
31
|
# it both begins and ends at time 0, giving it a 0 length.
|
@@ -76,6 +84,96 @@ module Timed
|
|
76
84
|
end
|
77
85
|
end
|
78
86
|
|
87
|
+
# Offset the entire sequence by specifying the coefficients of a polynomial
|
88
|
+
# of up to degree 2. This is then used to recalculate the begin and end
|
89
|
+
# times of each item in the set. The operation does not change the items but
|
90
|
+
# is instead performed on the fly every time either #begin or #end is
|
91
|
+
# called.
|
92
|
+
#
|
93
|
+
# Example
|
94
|
+
# sequence.offset_by 10, 1.1, 0.01
|
95
|
+
# ^ ^ ^ Quadratic term
|
96
|
+
# | + Linear term
|
97
|
+
# + Constant term
|
98
|
+
# sequence.offset 42.0 # => 73.84
|
99
|
+
#
|
100
|
+
# c - list of coefficients, starting with the constant term and ending with,
|
101
|
+
# at most, the quadratic.
|
102
|
+
|
103
|
+
def offset_by(*c)
|
104
|
+
body =
|
105
|
+
case c.length
|
106
|
+
when 0 then proc { |t| t }
|
107
|
+
when 1
|
108
|
+
if c[0] == 0 || !c[0]
|
109
|
+
proc { |t| t }
|
110
|
+
else
|
111
|
+
proc { |t| c[0] + t }
|
112
|
+
end
|
113
|
+
when 2 then proc { |t| c[0] + c[1] * t }
|
114
|
+
when 3 then proc { |t| c[0] + c[1] * t + c[2] * t**2 }
|
115
|
+
else
|
116
|
+
raise ArgumentError,
|
117
|
+
'Only polynomilas of order 2 or lower are supported'
|
118
|
+
end
|
119
|
+
|
120
|
+
redefine_method :offset, body
|
121
|
+
end
|
122
|
+
|
123
|
+
alias offset= offset_by
|
124
|
+
|
125
|
+
# Offset any time using the current offset settings of the sequence. Note
|
126
|
+
# that this method is overridden everytime #offset_by is called.
|
127
|
+
#
|
128
|
+
# time - the time to be offset.
|
129
|
+
#
|
130
|
+
# Returns the offset time.
|
131
|
+
|
132
|
+
def offset(time)
|
133
|
+
time
|
134
|
+
end
|
135
|
+
|
136
|
+
# Iterate over all of the edges in the sequence. An edge is the point in
|
137
|
+
# time when an item either begins or ends. That time, a numeric value, will
|
138
|
+
# be yielded to the block. If a block is not given and enumerator is
|
139
|
+
# returned.
|
140
|
+
#
|
141
|
+
# Returns an enumerator if a block was not given.
|
142
|
+
|
143
|
+
def each_edge
|
144
|
+
return to_enum __callee__ unless block_given?
|
145
|
+
|
146
|
+
each_item do |item|
|
147
|
+
yield item.begin
|
148
|
+
yield item.end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Iterates over all the leading edges in the sequence. A leading edge is the
|
153
|
+
# point in time where an item begins. That time, a numeric value, will be
|
154
|
+
# yielded to the block. If a block is not given and enumerator is returned.
|
155
|
+
#
|
156
|
+
# Returns an enumerator if a block was not given.
|
157
|
+
|
158
|
+
def each_leading_edge
|
159
|
+
return to_enum __callee__ unless block_given?
|
160
|
+
|
161
|
+
each_item { |item| yield item.begin }
|
162
|
+
end
|
163
|
+
|
164
|
+
# Iterates over all the trailing edges in the sequence. A trailing edge is
|
165
|
+
# the point in time where an item begins. That time, a numeric value, will
|
166
|
+
# be yielded to the block. If a block is not given and enumerator is
|
167
|
+
# returned.
|
168
|
+
#
|
169
|
+
# Returns an enumerator if a block was not given.
|
170
|
+
|
171
|
+
def each_trailing_edge
|
172
|
+
return to_enum __callee__ unless block_given?
|
173
|
+
|
174
|
+
each_item { |item| yield item.end }
|
175
|
+
end
|
176
|
+
|
79
177
|
# This method takes a second sequence and iterates over each intersection
|
80
178
|
# between the two. If a block is given, the beginning and end of each
|
81
179
|
# intersecting period will be yielded to it. Otherwise an enumerator is
|
@@ -84,10 +182,9 @@ module Timed
|
|
84
182
|
# other - a sequence, or object that responds to #begin and #end and returns
|
85
183
|
# a Timed::Item in response to #first
|
86
184
|
#
|
87
|
-
# Returns an enumerator unless a block is given
|
88
|
-
# intersections is returned.
|
185
|
+
# Returns an enumerator unless a block is given.
|
89
186
|
|
90
|
-
def intersections(other)
|
187
|
+
def intersections(other, &block)
|
91
188
|
return to_enum __callee__, other unless block_given?
|
92
189
|
|
93
190
|
return unless during? other
|
@@ -95,16 +192,14 @@ module Timed
|
|
95
192
|
# Sort the first items from each sequence into leading
|
96
193
|
# and trailing by whichever begins first
|
97
194
|
if self.begin <= other.begin
|
98
|
-
item_l, item_t =
|
195
|
+
item_l, item_t = self.item, other.item
|
99
196
|
else
|
100
|
-
item_l, item_t = other.
|
197
|
+
item_l, item_t = other.item, self.item
|
101
198
|
end
|
102
199
|
|
103
|
-
count = 0
|
104
|
-
|
105
200
|
loop do
|
106
201
|
# Now there are three posibilities:
|
107
|
-
|
202
|
+
|
108
203
|
# 1: The leading item ends before the trailing one
|
109
204
|
# begins. In this case the items do not intersect
|
110
205
|
# at all and we do nothing.
|
@@ -114,12 +209,10 @@ module Timed
|
|
114
209
|
# ends
|
115
210
|
elsif item_l.end <= item_t.end
|
116
211
|
yield item_t.begin, item_l.end
|
117
|
-
count += 1
|
118
212
|
|
119
213
|
# 3: The leading item ends after the trailing one
|
120
214
|
else
|
121
215
|
yield item_t.begin, item_t.end
|
122
|
-
count += 1
|
123
216
|
|
124
217
|
# Swap leading and trailing
|
125
218
|
item_l, item_t = item_t, item_l
|
@@ -131,8 +224,6 @@ module Timed
|
|
131
224
|
# Swap leading and trailing if needed
|
132
225
|
item_l, item_t = item_t, item_l if item_l.begin > item_t.begin
|
133
226
|
end
|
134
|
-
|
135
|
-
count
|
136
227
|
end
|
137
228
|
|
138
229
|
# Returns a new sequence with items that make up the intersection between
|
@@ -140,15 +231,75 @@ module Timed
|
|
140
231
|
|
141
232
|
def intersect(other)
|
142
233
|
intersections(other)
|
143
|
-
.with_object(self.class.new) { |(b, e), a| a <<
|
234
|
+
.with_object(self.class.new) { |(b, e), a| a << create_item(b, e) }
|
144
235
|
end
|
145
236
|
|
146
237
|
alias & intersect
|
147
238
|
|
148
239
|
# More efficient than first calling #intersect and then #time on the result.
|
240
|
+
#
|
241
|
+
# from - a point in time to start from.
|
242
|
+
# to - a point in time to stop at.
|
243
|
+
#
|
244
|
+
# Returns the total time of the intersection between this sequence and the
|
245
|
+
# other one.
|
246
|
+
|
247
|
+
def intersect_time(other, from: nil, to: nil)
|
248
|
+
enum = intersections(other)
|
249
|
+
total = 0
|
250
|
+
|
251
|
+
if from
|
252
|
+
# Reuse the variable total. It's perhaps a bit messy
|
253
|
+
# and confusing but it works.
|
254
|
+
_, total = enum.next until total > from
|
255
|
+
total -= from
|
256
|
+
end
|
257
|
+
|
258
|
+
if to
|
259
|
+
loop do
|
260
|
+
b, e = enum.next
|
261
|
+
|
262
|
+
if e > to
|
263
|
+
total += to - b unless b >= to
|
264
|
+
break
|
265
|
+
end
|
266
|
+
|
267
|
+
total += e - b
|
268
|
+
end
|
269
|
+
else
|
270
|
+
loop do
|
271
|
+
b, e = enum.next
|
272
|
+
total += e - b
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
total
|
277
|
+
rescue StopIteration
|
278
|
+
return 0
|
279
|
+
end
|
280
|
+
|
281
|
+
# Protected factory method for creating items compatible with the sequence.
|
282
|
+
# This method is called whenever an arbitrary object is pushed or unshifted
|
283
|
+
# onto the list and need to be wraped inside an Item.
|
284
|
+
#
|
285
|
+
# args - any arguments will be passed on to Item.new.
|
286
|
+
#
|
287
|
+
# Returns a new Item.
|
288
|
+
|
289
|
+
protected def create_item(*args)
|
290
|
+
Item.new(*args)
|
291
|
+
end
|
292
|
+
|
293
|
+
# Private helper method for (re)defining method on the singleton class.
|
294
|
+
#
|
295
|
+
# name - symbol name of the method.
|
296
|
+
# body - proc that will be used as method body.
|
149
297
|
|
150
|
-
def
|
151
|
-
|
298
|
+
private def redefine_method name, body
|
299
|
+
singleton_class.send :remove_method, name
|
300
|
+
rescue NameError
|
301
|
+
ensure
|
302
|
+
singleton_class.send :define_method, name, body
|
152
303
|
end
|
153
304
|
end
|
154
305
|
end
|
data/lib/timed/version.rb
CHANGED
data/timed.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency "linked", "~> 0.
|
22
|
+
spec.add_dependency "linked", "~> 0.3"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.12"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Lindberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: linked
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: '0.3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: '0.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|