timeline-manager 1.0.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.
@@ -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: []