timeline-manager 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8da990d4583655335ad676d2a17af7a6474f1a14c49707c313c7089ad0af3fea
4
+ data.tar.gz: d817ddc51240341c721631ea9203d9f13a73f1d36e8b453d35f435e405393a0c
5
+ SHA512:
6
+ metadata.gz: a07e9f3627aed54ffe1c952ed061b82c55add602d6ef08fda4f3449bb77c07e571aa0c049e4d6781d87f7b0d8aae846e45573f6ce6478e95bad7c9481e559810
7
+ data.tar.gz: 44d7b7ecd229e12269cd507f9d7389ea9f8a23a66099b13100e9511f110c2618d82fd10d3a617d389238e0f27d988a1dbd1eaff194f06ccc976a244cdc105745
@@ -0,0 +1,25 @@
1
+ class EventedArray < Array
2
+ # The calling context knows whether an element has been
3
+ # created, deleted or updated.
4
+ # This class is to be used in an append-only environment.
5
+
6
+ def initialize(post_delete, post_create, post_update)
7
+ @post_delete = post_delete
8
+ @post_create = post_create
9
+ @post_update = post_update
10
+ end
11
+
12
+ def delete(data)
13
+ @post_destroy.call(data)
14
+ end
15
+
16
+ def create(data)
17
+ self << data
18
+ @post_create.call(data)
19
+ end
20
+
21
+ def update(data)
22
+ self << data
23
+ @post_update.call(data)
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ require 'active_support/core_ext/object'
2
+
3
+ class TimeObj
4
+ # An interface to deal with passed-in time objects which
5
+ # may have a variety of interfaces.
6
+
7
+ include Comparable
8
+
9
+ attr_reader :data
10
+ attr_reader :start_method
11
+ attr_reader :end_method
12
+
13
+ def initialize(data, start_method=:start, end_method=:end)
14
+ if data.respond_to? :each
15
+ @data = data
16
+ else
17
+ @data = [data]
18
+ end
19
+ if @data.class == Range || @data.class == Array
20
+ start_method = :first
21
+ end_method = :last
22
+ end
23
+ @start_method = start_method
24
+ @end_method = end_method
25
+ end
26
+
27
+ def <=>(other)
28
+ @data <=> other.data
29
+ end
30
+
31
+ def start
32
+ @data[@start_method]
33
+ end
34
+
35
+ def end
36
+ @data[@end_method]
37
+ end
38
+
39
+ def merge(params)
40
+ if @data.respond_to? :attributes
41
+ return @data.class.new(@data.attributes.merge(params))
42
+ elsif @data.respond_to? :merge
43
+ return @data.merge(params)
44
+ end
45
+ raise ArgumentError.new('Objects that are represented as times must respond to :attributes or :merge.')
46
+ end
47
+
48
+ def duration
49
+ self.end - start
50
+ end
51
+ end
@@ -0,0 +1,132 @@
1
+ require 'active_support/core_ext/object'
2
+ require 'TimeObj'
3
+ require 'EventedArray'
4
+
5
+ module TimelineManager
6
+ class Timeline
7
+
8
+ def initialize(times=[], time_diff: Proc.new { 1.day }, start_method: :start,
9
+ end_method: :end, post_delete: Proc.new{}, post_create: Proc.new{},
10
+ post_update: Proc.new{})
11
+ @times = times.map { |t| t.class == TimeObj ? t : TimeObj.new(t, start_method, end_method) }
12
+ @time_diff = time_diff
13
+ @start_method = start_method
14
+ @end_method = end_method
15
+ @post_delete = post_delete
16
+ @post_create = post_create
17
+ @post_update = post_update
18
+ end
19
+
20
+ def to_s
21
+ data
22
+ end
23
+
24
+ def inspect
25
+ "TimelineManager::Timeline##{self.object_id} #{to_s}"
26
+ end
27
+
28
+ def data
29
+ @times.map { |t| t.data }
30
+ end
31
+
32
+ def insert(time, time_diff: Proc.new { 1.day }, start_method: :start,
33
+ end_method: :end, post_delete: nil, post_create: nil,
34
+ post_update: nil)
35
+ # Produce a new Timeline where time's start through end is unique in @times.
36
+ time = TimeObj.new(time, start_method, end_method)
37
+ new_times = EventedArray.new((post_delete || @post_delete), (post_create || @post_create), (post_update || @post_update))
38
+ @times.each do |t|
39
+ if t.start >= time.start && t.end <= time.end
40
+ # The new time covers the old one completely; remove it.
41
+ new_times.delete t
42
+ elsif t.start < time.start && t.end > time.end
43
+ # The new time splits the old one in half.
44
+ new_times.update t.merge({"#{t.end_method}": time.start - time_diff.call})
45
+ new_times.update t.merge({"#{t.start_method}": time.end + time_diff.call})
46
+ elsif t.start < time.start
47
+ # The new time cuts off the old time's end.
48
+ new_times.update t.merge({"#{t.end_method}": time.start - time_diff.call })
49
+ else
50
+ # The new time cuts off the old time's beginning.
51
+ new_times.update t.merge({"#{t.start_method}": time.end + time_diff.call })
52
+ end
53
+ end
54
+ new_times.create time
55
+ Timeline.new(new_times, time_diff: @time_diff, start_method: @start_method,
56
+ end_method: @end_method, post_delete: @post_delete, post_create: @post_create,
57
+ post_update: @post_update)
58
+ end
59
+
60
+ def overlap(time, time_diff: Proc.new { 1.day }, start_method: :start,
61
+ end_method: :end, post_delete: nil, post_create: nil,
62
+ post_update: nil)
63
+ # Beginning and end overlap, but middle is replaced.
64
+ time = TimeObj.new(time, start_method, end_method)
65
+ new_times = EventedArray.new((post_delete || @post_delete), (post_create || @post_create), (post_update || @post_update))
66
+ @times.each do |t|
67
+ if t.start > time.start && t.end < time.end
68
+ # The new time covers the old one completely; remove it.
69
+ new_times.delete t
70
+ elsif t.start <= time.start && t.end >= time.end
71
+ # The new time splits the old one in half.
72
+ if time.duration > time_diff.call
73
+ # The new time is more than one time_diff long
74
+ new_times.update t.merge({"#{t.start_method}": time.end})
75
+ new_times.update t.merge({"#{t.end_method}": time.start})
76
+ end
77
+ elsif t.start < time.start
78
+ # The new time cuts off the old time's end.
79
+ new_times.update t.merge({"#{t.end_method}": time.start})
80
+ elsif t.start == time.start && time.end >= t.end
81
+ # The new time starts on the old time's start, but the new time continues past the time's end
82
+ new_times.update t.merge({"#{t.end_method}": t.start})
83
+ else
84
+ # The new time cuts off the old time's beginning.
85
+ new_times.update t.merge({"#{t.start_method}": time.end})
86
+ end
87
+ end
88
+ new_times.create time
89
+ Timeline.new(new_times, time_diff: @time_diff, start_method: @start_method,
90
+ end_method: @end_method, post_delete: @post_delete, post_create: @post_create,
91
+ post_update: @post_update)
92
+ end
93
+
94
+ def overlay(time, start_method: :start, end_method: :end, post_delete: nil)
95
+ # Simply add the new time to the time line; allows complete overlap.
96
+ new_times = EventedArray.new((post_delete || @post_delete), (post_create || @post_create), (post_update || @post_update))
97
+ new_times << @times
98
+ new_times.create time
99
+ Timeline.new(new_times, time_diff: time_diff, start_method: @start_method,
100
+ end_method: @end_method, post_delete: @post_delete, post_create: @post_create,
101
+ post_update: @post_update)
102
+ end
103
+
104
+ def split(date, time_diff: Proc.new { 1.day }, post_update: nil)
105
+ # Splits timelines in half given a point in time.
106
+ time = TimeObj.new({start: date, end: date + time_diff.call})
107
+ new_times = EventedArray.new(@post_delete, @post_create, (post_update || @post_update))
108
+ @times.each do |t|
109
+ if t.start > time.start && t.end < time.end
110
+ # The new time covers the old one completely.
111
+ new_times << t
112
+ elsif t.start <= time.start && t.end >= time.end
113
+ # The new time splits the old one in half.
114
+ new_times.update t.merge({"#{t.start_method}": time.end})
115
+ new_times.update t.merge({"#{t.end_method}": time.start})
116
+ elsif t.start < time.start
117
+ # The new time cuts off the old time's end.
118
+ new_times.update t.merge({"#{t.end_method}": time.start})
119
+ elsif t.start == time.start && time.end >= t.end
120
+ # The new time starts on the old time's start, but the new time continues past the time's end
121
+ new_times.update t.merge({"#{t.end_method}": t.start})
122
+ else
123
+ # The new time cuts off the old time's beginning.
124
+ new_times.update t.merge({"#{t.start_method}": time.end})
125
+ end
126
+ end
127
+ Timeline.new(new_times, time_diff: @time_diff, start_method: @start_method,
128
+ end_method: @end_method, post_delete: @post_delete, post_create: @post_create,
129
+ post_update: @post_update)
130
+ end
131
+ end
132
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: timeline-manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tyler Brothers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: ''
14
+ email: tylerbrothers1@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/EventedArray.rb
20
+ - lib/TimeObj.rb
21
+ - lib/timeline-manager.rb
22
+ homepage: https://github.com/possibly/timeline-manager
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.7.7
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Timeline Manager helps order and transform grouped temporal data
46
+ test_files: []