timed 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/timed.svg)](https://badge.fury.io/rb/timed)
|
3
4
|
[![Build Status](https://travis-ci.org/seblindberg/ruby-timed.svg?branch=master)](https://travis-ci.org/seblindberg/ruby-timed)
|
4
5
|
[![Coverage Status](https://coveralls.io/repos/github/seblindberg/ruby-timed/badge.svg?branch=master)](https://coveralls.io/github/seblindberg/ruby-timed?branch=master)
|
5
6
|
[![Inline docs](http://inch-ci.org/github/seblindberg/ruby-timed.svg?branch=master)](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
|