orchestrate 0.8.1 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f337f0bb6cd0bea6f396f5f2cf1ac238325a5704
4
- data.tar.gz: 5487fd1bf34564e6cc78eaad68a7d6a68d13184f
3
+ metadata.gz: 260fdf369f126768067a8bae1f0dd3671a5a9779
4
+ data.tar.gz: ee9677aa33a74fd362a538084ff05e98188afb89
5
5
  SHA512:
6
- metadata.gz: ba3609ecc24d1c0d1f5d919d6450769efe77e737eee5d785c888b6b31139c1d2d3b7baab54fe7ff188083c226bf8d243280a9786cf39a8b5b416a636aa258707
7
- data.tar.gz: 32f3f69256d28a0733d9475adeb5adfb231c4f8e44a91e49cbbf1335b66397cfb8f106e6626df1e6d169d7ac8c02ee4db797250a36919de56b572af8cde45d47
6
+ metadata.gz: 621a858872aaf62e4ed91ed6cf32313a014cf52f1300886220b5976acffec67d65afa99c76ba18b03b084e25d419f83b91bcc2dd07553b4084a67d1119f431e6
7
+ data.tar.gz: 2c086b71f2be610e25c99d8893bcfbe068ecf0e5afd82429aa0b9d4716a5a8963a7e806ada86d581fc03087d7f48b933d9fabef778dd48b88724cd550105a818
data/README.md CHANGED
@@ -159,6 +159,10 @@ end
159
159
 
160
160
  ## Release Notes
161
161
 
162
+ ### September 1, 2014: release 0.9.0
163
+ - Implement `KeyValue#events`, `EventList` and `Events` to access events associated with a KeyValue.
164
+ - Removed `KeyValue#loaded` attr reader, it pointed to an instance variable no longer in use. Use `#loaded?` instead.
165
+
162
166
  ### August 6, 2014: release 0.8.1
163
167
  - Implement `KeyValue#refs`, `RefList` and `Ref` to access a KeyValue's Refs.
164
168
  - Refactor `Client` api accessors on Object client to internal `#perform` methods.
data/lib/orchestrate.rb CHANGED
@@ -4,6 +4,8 @@ require "orchestrate/application"
4
4
  require "orchestrate/collection"
5
5
  require "orchestrate/key_value"
6
6
  require "orchestrate/refs"
7
+ require "orchestrate/event"
8
+ require "orchestrate/event_source"
7
9
  require "orchestrate/graph"
8
10
  require "orchestrate/version"
9
11
  #
@@ -0,0 +1,232 @@
1
+ require 'uri'
2
+ module Orchestrate
3
+
4
+ # Events are a way to associate Time-ordered data with a key.
5
+ class Event
6
+
7
+ # Instantiates an Event from a value and response without a body.
8
+ # @param stem [Orchestrate::EventType] The associated EventType.
9
+ # @param value [Hash] The value for the event.
10
+ # @param response [API::Response] The associated response for creating the event.
11
+ # @return [Event]
12
+ def self.from_bodyless_response(stem, value, response)
13
+ event = new(stem, response)
14
+ event.value = value
15
+ event
16
+ end
17
+
18
+ # Instantiates an Event from a value in a listing and an associated response.
19
+ # @param stem [Orchestrate::EventType] The associated EventType
20
+ # @param listing [Hash] The entry in the LIST result
21
+ # @param response [API::Response] The associated response for the list query.
22
+ # @return [Event]
23
+ def self.from_listing(stem, listing, response=nil)
24
+ event = new(stem)
25
+ event.value = listing['value']
26
+ event.instance_variable_set(:@timestamp, listing['timestamp'])
27
+ event.instance_variable_set(:@ordinal, listing['ordinal'])
28
+ event.instance_variable_set(:@ref, listing['path']['ref'])
29
+ event.instance_variable_set(:@last_request_time, response.request_time) if response
30
+ event
31
+ end
32
+
33
+ # @return [Orchestrate::EventType] The associated EventType.
34
+ attr_reader :type
35
+
36
+ # @return [String] The associated name of the event type.
37
+ attr_reader :type_name
38
+
39
+ # @return [Orchestrate::KeyValue] The assoicated KeyValue item.
40
+ attr_reader :key
41
+
42
+ # @return [String] The key of the associated item.
43
+ attr_reader :key_name
44
+
45
+ # @return [String] The immutable ref for the current value of this event.
46
+ attr_reader :ref
47
+
48
+ # @return [Integer] The timestamp of the event, in number of milliseconds since epoch.
49
+ # @see #time
50
+ attr_reader :timestamp
51
+
52
+ # @return [Integer] The ordinal of the event.
53
+ attr_reader :ordinal
54
+
55
+ # @return [Time] The time which the event was loaded.
56
+ attr_reader :last_request_time
57
+
58
+ # @return [Hash] The value for the event.
59
+ attr_accessor :value
60
+
61
+ # Instantiates a new Event item. You generally don't want to call this
62
+ # yourself, but use accessor methods from the KeyValue and EventType.
63
+ # @param stem [Orchestrate::EventType] The associated EventType.
64
+ # @param response [nil, Orchestrate::API::Response]
65
+ # If an API::Response, used to load attributes and value.
66
+ # @return Orchestrate::Event
67
+ def initialize(stem, response=nil)
68
+ @type = stem
69
+ @type_name = type.type
70
+ @key = stem.kv_item
71
+ @key_name = key.key
72
+ @value = nil
73
+ load_from_response(response) if response
74
+ end
75
+
76
+ # Equivalent to `String#==`. Compares by KeyValue, Type, Timestamp and Ordinal.
77
+ # @param other [Orchestrate::Event] the Event to compare against.
78
+ # @return [true, false]
79
+ def ==(other)
80
+ other.kind_of?(Orchestrate::Event) && \
81
+ other.type == type && \
82
+ other.timestamp == timestamp && \
83
+ other.ordinal == ordinal
84
+ end
85
+ alias :eql? :==
86
+
87
+ # Equivalent to `String#<=>`. Compares by KeyValue, Type, Timestamp and
88
+ # Ordinal. Sorts newer timestamps before older timestamps. If timestamps
89
+ # are equivalent, sorts by ordinal. This behavior emulates the order which
90
+ # events are returned in a list events query from the Orchestrate API.
91
+ # @param other [Orchestrate::Event] the Event to compare against.
92
+ # @return [nil, -1, 0, 1]
93
+ def <=>(other)
94
+ return nil unless other.kind_of?(Orchestrate::Event)
95
+ return nil unless other.type == type
96
+ if other.timestamp == timestamp
97
+ other.ordinal <=> ordinal
98
+ else
99
+ other.timestamp > timestamp ? -1 : 1
100
+ end
101
+ end
102
+
103
+ # @return [String] A pretty-printed representation of the event.
104
+ def to_s
105
+ "#<Orchestrate::Event collection=#{key.collection_name} key=#{key_name} type=#{type_name} time=#{time} ordinal=#{ordinal} ref=#{ref} last_request_time=#{last_request_time}>"
106
+ end
107
+
108
+ # @group Attributes
109
+
110
+ # @return [String] The full path of the event.
111
+ def path
112
+ @path ||= "/#{key.path}/events/#{URI.escape(type_name)}/#{timestamp}/#{ordinal}"
113
+ end
114
+
115
+ # @return [Time] The timestamp of the event as a Time object.
116
+ def time
117
+ @time ||= Time.at(@timestamp / 1000.0)
118
+ end
119
+
120
+ # Accessor for a property on the event's value.
121
+ # @param attr_name [#to_s] the attribute name to retrieve.
122
+ # @return [nil, true, false, Numeric, String, Array, Hash] The value for the attribute.
123
+ def [](attr_name)
124
+ value[attr_name.to_s]
125
+ end
126
+
127
+ # Set an attribute on the event's value to a specified value.
128
+ # @param attr_name [#to_s] The name of the attribute.
129
+ # @param attr_val [#to_json] The new value for the attribute
130
+ # @return [attr_val]
131
+ def []=(attr_name, attr_val)
132
+ value[attr_name.to_s] = attr_val
133
+ end
134
+
135
+ # @endgroup
136
+ # @group Persistence
137
+
138
+ # Saves the Event item to Orchestrate using 'If-Match' with the current ref.
139
+ # Sets the new ref for this value to the ref attribute.
140
+ # Returns false on failure to save, and rescues from all Orchestrate::API errors.
141
+ # @return [true, false]
142
+ def save
143
+ begin
144
+ save!
145
+ rescue API::RequestError, API::ServiceError
146
+ false
147
+ end
148
+ end
149
+
150
+ # Saves the Event item to Orchestrate using 'If-Match' with the current ref.
151
+ # Sets the new ref for this value to the ref attribute.
152
+ # Raises an exception on failure to save.
153
+ # @return [true]
154
+ # @raise [Orchestrate::API::VersionMismatch] If the Event has been updated with a new value since this Event was loaded.
155
+ # @raise [Orchestrate::API::RequestError, Orchestrate::API::ServiceError] If there are any other problems with saving.
156
+ def save!
157
+ response = perform(:put_event, value, ref)
158
+ load_from_response(response)
159
+ true
160
+ end
161
+
162
+ # Merges a set of values into the event's existing value and saves.
163
+ # @param merge [#each_pair] The Hash-like to merge into #value. Keys will be stringified.
164
+ # @return [true, false]
165
+ def update(merge)
166
+ begin
167
+ update!(merge)
168
+ rescue API::RequestError, API::ServiceError
169
+ false
170
+ end
171
+ end
172
+
173
+ # Merges a set of values into the event's existing value and saves.
174
+ # @param merge [#each_pair] The Hash-like to merge into #value. Keys will be stringified.
175
+ # @return [true]
176
+ # @raise [Orchestrate::API::VersionMismatch] If the Event has been updated with a new value since this Event was loaded.
177
+ # @raise [Orchestrate::API::RequestError, Orchestrate::API::ServiceError] If there are any other problems with saving.
178
+ def update!(merge)
179
+ merge.each_pair {|key, value| @value[key.to_s] = value }
180
+ save!
181
+ end
182
+
183
+ # Deletes the event from Orchesrate using 'If-Match' with the current ref.
184
+ # Returns false if the event failed to delete because a new ref has been created since this Event was loaded.
185
+ # @return [true, false]
186
+ def purge
187
+ begin
188
+ purge!
189
+ rescue API::RequestError
190
+ false
191
+ end
192
+ end
193
+
194
+ # Deletes the event from Orchesrate using 'If-Match' with the current ref.
195
+ # @return [true]
196
+ # @raise [Orchestrate::API::VersionMismatch] If the Event has been updated with a new ref since this Event was loaded.
197
+ def purge!
198
+ response = perform(:purge_event, ref)
199
+ @ref = nil
200
+ @last_request_time = response.request_time
201
+ true
202
+ end
203
+
204
+ # @endgroup persistence
205
+
206
+ # Performs a request using the associated API client, with collection_name,
207
+ # key, event_type, timestamp, and ordinal pre-filled.
208
+ # @param api_method [Symbol] the method on API::Client to call.
209
+ # @param args [#to_s, #to_json, Hash] The remaining arguments for the method being called.
210
+ # @return [API::Request]
211
+ def perform(api_method, *args)
212
+ type.perform(api_method, timestamp, ordinal, *args)
213
+ end
214
+
215
+ private
216
+ def load_from_response(response)
217
+ response.on_complete do
218
+ @ref = response.ref if response.respond_to?(:ref)
219
+ if response.headers['Location']
220
+ loc = response.headers['Location'].split('/')
221
+ @timestamp = loc[6].to_i
222
+ @ordinal = loc[7].to_i
223
+ elsif response.body.kind_of?(Hash)
224
+ @value = response.body['value']
225
+ @timestamp = response.body['timestamp']
226
+ @ordinal = response.body['ordinal']
227
+ end
228
+ @last_request_time = response.request_time
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,330 @@
1
+ module Orchestrate
2
+
3
+ # Manages Event Types for a KeyValue item.
4
+ class EventSource
5
+
6
+ # @return [Orchestrate::KeyValue] The KeyValue to which the sourced events belong.
7
+ attr_reader :kv_item
8
+
9
+ # Instantiages a new EventSource manager.
10
+ # @param kv_item [Orchestrate::KeyValue] The KeyValue item to which events belong.
11
+ def initialize(kv_item)
12
+ @kv_item = kv_item
13
+ @types = {}
14
+ end
15
+
16
+ # Accessor for event types.
17
+ # @param event_type [#to_s] The type key for the events.
18
+ # @return [EventType]
19
+ def [](event_type)
20
+ @types[event_type.to_s] ||= EventType.new(@kv_item, event_type)
21
+ end
22
+
23
+ # Equivalent to `String#==`. Compares by kv_item.
24
+ # @param other [Orchestrate::EventSource] the EventSource to compare against.
25
+ # @return [true, false]
26
+ def ==(other)
27
+ other.kind_of?(Orchestrate::EventSource) && other.kv_item == kv_item
28
+ end
29
+ alias :eql? :==
30
+
31
+ # @return Pretty-Printed string representation
32
+ def to_s
33
+ "#<Orchestrate::EventSource key_value=#{kv_item}>"
34
+ end
35
+ end
36
+
37
+ # Manages events of a specific type that belong to a specific KeyValue item.
38
+ class EventType
39
+
40
+ # @return [Orchestrate::KeyValue] The KeyValue this EventType is managing events for.
41
+ attr_reader :kv_item
42
+
43
+ # @return [String] The name for the type of events this EventType manages.
44
+ attr_reader :type
45
+
46
+ # Instantiates a new EventType.
47
+ # @param kv_item [Orchestrate::KeyValue] The KeyValue this EventType manages events for.
48
+ # @param event_type [#to_s] The type of events this EventType manages.
49
+ def initialize(kv_item, event_type)
50
+ @kv_item = kv_item
51
+ @type = event_type.to_s
52
+ end
53
+
54
+ # Equivalent to `String#==`. Compares by KeyValue and Type.
55
+ # @param other [Orchestrate::EventType] The EventType to compare against.
56
+ # @return [true, false]
57
+ def ==(other)
58
+ other.kind_of?(Orchestrate::EventType) && \
59
+ other.kv_item == kv_item && \
60
+ other.type == type
61
+ end
62
+ alias :eql? :==
63
+
64
+ # Equivalent to `String#<=>`. Comapres by key_value and type.
65
+ # @param other [Orchestrate::EventType] The EventType to compare against.
66
+ # @return [nil, -1, 0, 1]
67
+ def <=>(other)
68
+ return nil unless other.kind_of?(Orchestrate::EventType)
69
+ return nil unless other.kv_item == kv_item
70
+ other.type <=> type
71
+ end
72
+
73
+ # @return A pretty-printed string representation of the EventType
74
+ def to_s
75
+ "#<Orchestrate::EventType key_value=#{kv_item} type=#{type}>"
76
+ end
77
+
78
+ # Calls a method on the KeyValue's Collection's API Client, providing the event type.
79
+ # @param api_method [Symbol] The method on the client to call.
80
+ # @param args [#to_s, #to_json, Hash] The remaining arguments for the specified method.
81
+ # @return [API::Response]
82
+ def perform(api_method, *args)
83
+ kv_item.perform(api_method, type, *args)
84
+ end
85
+
86
+ # Creates a new Event of the given type for the associated KeyValue.
87
+ # @param body [#to_json] The value for the event.
88
+ # @return [Orchestrate::Event] The event that was created.
89
+ def <<(body)
90
+ TimeSlice.new(self).push(body)
91
+ end
92
+ alias :push :<<
93
+
94
+ # Instantiates a new EventType::List with the given bounds. Can be used to
95
+ # access single events or create events with a specific timestamp.
96
+ # @param bounds [#to_s, Time, Date, Integer, Hash] The bounds value for the List.
97
+ # @see EventType::List#initialize
98
+ # @example Accessing an existing event by timestamp/ordinal
99
+ # kv.events[:checkins][timestamp][1]
100
+ # @example Creating an Event with a specified timestamp
101
+ # kv.events[:checkins][Time.now - 3600] << {"place" => "home"}
102
+ # @example Listing events within a time range
103
+ # kv.events[:checkins][{before: Time.now - 3600, after: Time.now - 24 * 3600}].to_a
104
+ # # equivalent to:
105
+ # # kv.events[:checkins].before(Time.now - 3600).after(Time.now - 24 * 3600).to_a
106
+ def [](bounds)
107
+ TimeSlice.new(self, bounds)
108
+ end
109
+
110
+ include Enumerable
111
+
112
+ # Iterates over events belonging to the KeyValue of the specified Type.
113
+ # Used as the basis for enumerable methods. Events are provided in reverse
114
+ # chronological order by timestamp and ordinal value.
115
+ # @overlaod each
116
+ # @return Enumerator
117
+ # @overload each(&block)
118
+ # @yieldparam [Orchestrate::Event] event The Event item
119
+ def each(&block)
120
+ TimeSlice.new(self).each(&block)
121
+ end
122
+
123
+ # Creates a Lazy Enumerator for the EventList. If called inside the app's
124
+ # `#in_parallel` block, will prefetch results.
125
+ # @return Enumerator::Lazy
126
+ def lazy
127
+ TimeSlice.new(self).lazy
128
+ end
129
+
130
+ # Returns the first n items. Equivalent to Enumerable#take. Sets the `limit`
131
+ # parameter on the query to Orchestrate, so we don't ask for more than is needed.
132
+ # @param count [Integer] The number of events to limit to.
133
+ # @return [Array]
134
+ def take(count)
135
+ TimeSlice.new(self).take(count)
136
+ end
137
+
138
+ # Sets the inclusive start boundary for enumeration over events.
139
+ # Overwrites any value given to #before.
140
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The inclusive start of the event range.
141
+ # @return [EventType::TimeSlice]
142
+ def start(bound)
143
+ TimeSlice.new(self).start(bound)
144
+ end
145
+
146
+ # Sets the exclusive start boundary for enumeration over events.
147
+ # Overwrites any value given to #start
148
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The exclusive start of the event range.
149
+ # @return [EventType::TimeSlice]
150
+ def after(bound)
151
+ TimeSlice.new(self).after(bound)
152
+ end
153
+
154
+ # Sets the exclusive end boundary for enumeration over events.
155
+ # Overwrites any value given to #end
156
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The exclusive end of the event range.
157
+ # @return [EventType::TimeSlice]
158
+ def before(bound)
159
+ TimeSlice.new(self).before(bound)
160
+ end
161
+
162
+ # Sets the inclusive end boundary for enumeration over events.
163
+ # Overwrites any value given to #before
164
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The inclusive end of the event range.
165
+ # @return [EventType::TimeSlice]
166
+ def end(bound)
167
+ TimeSlice.new(self).start(bound)
168
+ end
169
+
170
+ # Manages events in a specified duration of time.
171
+ class TimeSlice
172
+
173
+ # @return [EventType] The associated EventType
174
+ attr_reader :type
175
+
176
+ # @return [String] The associated event type name
177
+ attr_reader :type_name
178
+
179
+ # @return [KeyValue] The associated KeyValue
180
+ attr_reader :kv_item
181
+
182
+ # Instantiates a new TimeSlice
183
+ # @param type [EventType] The associated EventType
184
+ # @param bounds [nil, String, Integer, Time, Date, Hash] Boundaries for the Time
185
+ # If `nil`, no boundaries are set. Used to enumerate over all events.
186
+ # If `String`, `Integer`, `Time`, `Date`, used as `:start` option, below.
187
+ # If `Hash`, see options.
188
+ # @option bounds [nil, String, Integer, Time, Date] :start Used as the 'startEvent' key.
189
+ # @option bounds [nil, String, Integer, Time, Date] :after Used as the 'afterEvent' key.
190
+ # @option bounds [nil, String, Integer, Time, Date] :before Used as the 'beforeEvent' key.
191
+ # @option bounds [nil, String, Integer, Time, Date] :end Used as the 'endEvent' key.
192
+ # @option bounds [Integer] :limit (100) The number of results to return.
193
+ def initialize(type, bounds=nil)
194
+ @type = type
195
+ @type_name = type.type
196
+ @kv_item = type.kv_item
197
+ if bounds.kind_of?(Hash)
198
+ @bounds = bounds
199
+ else
200
+ @bounds = {}
201
+ @bounds[:start] = bounds if bounds
202
+ end
203
+ @bounds[:limit] ||= 100
204
+ end
205
+
206
+ # Calls a method on the associated API Client, with collection, key and
207
+ # event_type being provided by EventType.
208
+ # @param api_method [Symbol] The method to call
209
+ # @param args [#to_s, #to_json, Hash] The arguments for the method being called.
210
+ # @return [API::Response]
211
+ def perform(api_method, *args)
212
+ type.perform(api_method, *args)
213
+ end
214
+
215
+ # Creates a new Event of the given type for the associated KeyValue.
216
+ # @param body [#to_json] The value for the event.
217
+ # @return [Orchestrate::Event] The event that was created.
218
+ def <<(body)
219
+ response = perform(:post_event, body, @bounds[:start])
220
+ Event.from_bodyless_response(type, body, response)
221
+ end
222
+ alias :push :<<
223
+
224
+ # Retrieves a single ID by timestamp and ordinal. Uses the timestamp
225
+ # value from the associated bounds, by `start`, `before`, `after`, or
226
+ # `end` in that order.
227
+ # @param ordinal [Integer, #to_s] The ordinal value for the event to retrieve.
228
+ # @return [Orchestrate::Event]
229
+ def [](ordinal)
230
+ begin
231
+ timestamp = @bounds[:start] || @bounds[:before] || @bounds[:after] || @bounds[:end]
232
+ response = perform(:get_event, timestamp, ordinal)
233
+ Event.new(type, response)
234
+ rescue API::NotFound
235
+ nil
236
+ end
237
+ end
238
+
239
+ include Enumerable
240
+
241
+ # Iterates over events belonging to the KeyValue of the specified Type
242
+ # within the given bounds. Used as the basis for enumerable methods.
243
+ # Events are provided in reverse chronological order by timestamp and
244
+ # ordinal value.
245
+ # @overlaod each
246
+ # @return Enumerator
247
+ # @overload each(&block)
248
+ # @yieldparam [Orchestrate::Event] event The Event item
249
+ def each(&block)
250
+ @response = perform(:list_events, @bounds)
251
+ return enum_for(:each) unless block_given?
252
+ raise ResultsNotReady.new if type.kv_item.collection.app.inside_parallel?
253
+ loop do
254
+ @response.results.each do |listing|
255
+ yield Event.from_listing(type, listing, @response)
256
+ end
257
+ break unless @response.next_link
258
+ @response = @response.next_results
259
+ end
260
+ @response = nil
261
+ end
262
+
263
+ # Creates a Lazy Enumerator for the EventList. If called inside the app's
264
+ # `#in_parallel` block, will prefetch results.
265
+ # @return Enumerator::Lazy
266
+ def lazy
267
+ return each.lazy if type.kv_item.collection.app.inside_parallel?
268
+ super
269
+ end
270
+
271
+ # Returns the first n items. Equivalent to Enumerable#take. Sets the `limit`
272
+ # parameter on the query to Orchestrate, so we don't ask for more than is needed.
273
+ # @param count [Integer] The number of events to limit to.
274
+ # @return [Array]
275
+ def take(count)
276
+ count = 1 if count < 1
277
+ @bounds[:limit] = count > 100 ? 100 : count
278
+ super(count)
279
+ end
280
+
281
+ # Sets the inclusive start boundary for enumeration over events.
282
+ # Overwrites any value given to #before.
283
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The inclusive start of the event range.
284
+ # @return [EventType::TimeSlice]
285
+ def start(bound)
286
+ @bounds[:start] = extract_bound_from(bound)
287
+ @bounds.delete(:after)
288
+ self
289
+ end
290
+
291
+ # Sets the exclusive start boundary for enumeration over events.
292
+ # Overwrites any value given to #start
293
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The exclusive start of the event range.
294
+ # @return [EventType::TimeSlice]
295
+ def after(bound)
296
+ @bounds[:after] = extract_bound_from(bound)
297
+ @bounds.delete(:start)
298
+ self
299
+ end
300
+
301
+ # Sets the exclusive end boundary for enumeration over events.
302
+ # Overwrites any value given to #end
303
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The exclusive end of the event range.
304
+ # @return [EventType::TimeSlice]
305
+ def before(bound)
306
+ @bounds[:before] = extract_bound_from(bound)
307
+ @bounds.delete(:end)
308
+ self
309
+ end
310
+
311
+ # Sets the inclusive end boundary for enumeration over events.
312
+ # Overwrites any value given to #before
313
+ # @param bound [Orchestrate::Event, Time, Date, Integer, #to_s] The inclusive end of the event range.
314
+ # @return [EventType::TimeSlice]
315
+ def end(bound)
316
+ @bounds[:end] = extract_bound_from(bound)
317
+ @bounds.delete(:before)
318
+ self
319
+ end
320
+
321
+ private
322
+ def extract_bound_from(bound)
323
+ if bound.kind_of?(Event)
324
+ "#{bound.timestamp}/#{bound.ordinal}"
325
+ else bound
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
@@ -35,6 +35,10 @@ module Orchestrate
35
35
  @type = type_name.to_s
36
36
  end
37
37
 
38
+ # Calls a method on the KeyValue's Collection's API Client, providing the relation type.
39
+ # @param api_method [Symbol] The method on the client to call.
40
+ # @param args [#to_s, #to_json, Hash] The remaining arguments for the specified method.
41
+ # @return [API::Response]
38
42
  def perform(api_method, *args)
39
43
  kv_item.perform(api_method, type, *args)
40
44
  end
@@ -1,3 +1,4 @@
1
+ require 'uri'
1
2
  module Orchestrate
2
3
  # Key/Value pairs are pieces of data identified by a unique key for
3
4
  # a collection and have corresponding value.
@@ -58,6 +59,10 @@ module Orchestrate
58
59
  # @return [String]
59
60
  attr_reader :key
60
61
 
62
+ # The 'address' of this KeyValue item, representated as #[collection_name]/#[key]
63
+ # @return [String]
64
+ attr_reader :path
65
+
61
66
  # For comparison purposes only, the 'address' of this KeyValue item.
62
67
  # Represented as "[collection_name]/[key]"
63
68
  # @return [String]
@@ -75,10 +80,6 @@ module Orchestrate
75
80
  # @return [#to_json]
76
81
  attr_accessor :value
77
82
 
78
- # Whether the KeyValue has been loaded from Orchestrate or not.
79
- # @return boolean
80
- attr_reader :loaded
81
-
82
83
  # When the KeyValue was last loaded from Orchestrate.
83
84
  # @return [Time]
84
85
  attr_reader :last_request_time
@@ -88,7 +89,7 @@ module Orchestrate
88
89
  # @param coll [Orchestrate::Collection] The collection to which this KeyValue belongs.
89
90
  # @param key_name [#to_s] The name of the key
90
91
  # @param associated_response [nil, Orchestrate::API::Response]
91
- # If an API::Request, used to load attributes and value.
92
+ # If an API::Response, used to load attributes and value.
92
93
  # @return Orchestrate::KeyValue
93
94
  def initialize(coll, key_name, associated_response=nil)
94
95
  @collection = coll
@@ -96,6 +97,7 @@ module Orchestrate
96
97
  @app = coll.app
97
98
  @key = key_name.to_s
98
99
  @id = "#{collection_name}/#{key}"
100
+ @path = "#{URI.escape(collection_name)}/#{URI.escape(key)}"
99
101
  @value = {}
100
102
  @ref = false
101
103
  load_from_response(associated_response) if associated_response
@@ -285,6 +287,15 @@ module Orchestrate
285
287
  end
286
288
 
287
289
  # @!endgroup relations
290
+ # @!group events
291
+
292
+ # Entry point for managing events associated with this KeyValue item.
293
+ # @return [Orchestrate::EventSource]
294
+ def events
295
+ @events ||= EventSource.new(self)
296
+ end
297
+
298
+ # @!endgroup events
288
299
 
289
300
  # Calls a method on the Collection's Application's client, providing the
290
301
  # Collection's name and KeyValue's key.
@@ -1,4 +1,4 @@
1
1
  module Orchestrate
2
2
  # @return [String] The version number of the Orchestrate Gem
3
- VERSION = "0.8.1"
3
+ VERSION = "0.9.0"
4
4
  end
@@ -1,6 +1,6 @@
1
1
  require_relative '../../test_helper'
2
2
 
3
- class EventTest < MiniTest::Unit::TestCase
3
+ class EventAPITest < MiniTest::Unit::TestCase
4
4
 
5
5
  def setup
6
6
  @collection = 'tests'
@@ -0,0 +1,165 @@
1
+ require "test_helper"
2
+
3
+ class EventEnumerationTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @app, @stubs = make_application({parallel: true})
6
+ @kv = make_kv_item(@app[:items], @stubs)
7
+ @type = :events
8
+ @limit = "100"
9
+ @called = false
10
+ @stubs.get("/v0/items/#{@kv.key}/events/#{@type}") do |env|
11
+ @called = true
12
+ assert_equal @limit, env.params['limit']
13
+ body = case env.params['beforeEvent']
14
+ when nil
15
+ events = 100.times.map{|i| make_event({ts: @start_time - i}) }
16
+ next_event = events.last.values_at("timestamp", "ordinal").join('/')
17
+ { "results" => events, "count" => 100,
18
+ "next" => "/v0/items/#{@kv.key}/events/#{@type}?beforeEvent=#{next_event}&limit=100" }
19
+ when "#{@start_time - 99}/1"
20
+ events = 10.times.map{|i| make_event({ts: @start_time - 100 - i})}
21
+ { "results" => events, "count" => 10 }
22
+ else
23
+ raise ArgumentError.new("unexpected beforeEvent: #{env.params['beforeEvent']}")
24
+ end
25
+ [200, response_headers, body.to_json]
26
+ end
27
+ @start_time = (Time.now.to_f * 1000).to_i
28
+ end
29
+
30
+ def make_event(opts={})
31
+ timestamp = opts[:ts] || @start_time
32
+ ordinal = opts[:ord] || 1
33
+ ref = opts[:ref] || make_ref
34
+ value = opts[:val] || { "msg" => "hello world" }
35
+ { "path" => { "collection" => @kv.collection_name, "key" => @kv.key, "type" => @type.to_s,
36
+ "timestamp" => timestamp, "ordinal" => ordinal, "ref" => ref },
37
+ "value" => value,
38
+ "timestamp" => timestamp, "ordinal" => ordinal }
39
+ end
40
+
41
+ def test_enumerates_all_from_event_type
42
+ events = @kv.events[@type].to_a
43
+ assert_equal 110, events.length
44
+ events.each_with_index do |event, index|
45
+ assert_equal @kv, event.key
46
+ assert_equal @type.to_s, event.type_name
47
+ assert_equal @start_time - index, event.timestamp
48
+ assert_equal 1, event.ordinal
49
+ assert event.ref
50
+ assert event.value
51
+ assert_in_delta Time.now.to_f, event.last_request_time.to_f, 1.1
52
+ end
53
+ end
54
+
55
+ def test_enumerates_over_range_from_start_end
56
+ app, stubs = make_application
57
+ kv = make_kv_item(app[:items], stubs)
58
+ start_event = Orchestrate::Event.new(kv.events[@type])
59
+ start_event.instance_variable_set(:@timestamp, @start_time - 1000)
60
+ start_event.instance_variable_set(:@ordinal, 5)
61
+ end_event = @start_time
62
+ stubs.get("/v0/items/#{@kv.key}/events/#{@type}") do |env|
63
+ assert_equal "#{start_event.timestamp}/#{start_event.ordinal}", env.params['startEvent']
64
+ assert_equal "#{end_event}", env.params['endEvent']
65
+ [200, response_headers, {"count" => 0, "results" => []}.to_json]
66
+ end
67
+ response = kv.events[@type].start(start_event).end(end_event).to_a
68
+ assert_equal [], response
69
+ end
70
+
71
+ def test_enumerates_over_range_from_after_before
72
+ app, stubs = make_application
73
+ kv = make_kv_item(app[:items], stubs)
74
+ after_event = Orchestrate::Event.new(kv.events[@type])
75
+ after_event.instance_variable_set(:@timestamp, @start_time - 1000)
76
+ after_event.instance_variable_set(:@ordinal, 5)
77
+ before_event = Time.at(@start_time)
78
+ stubs.get("/v0/items/#{@kv.key}/events/#{@type}") do |env|
79
+ assert_equal "#{after_event.timestamp}/#{after_event.ordinal}", env.params['afterEvent']
80
+ assert_equal "#{(before_event.to_f * 1000).to_i}", env.params['beforeEvent']
81
+ [200, response_headers, {"count" => 0, "results" => []}.to_json]
82
+ end
83
+ response = kv.events[@type].before(before_event).after(after_event).to_a
84
+ assert_equal [], response
85
+ end
86
+
87
+ def test_enumerator_sets_limit_from_limit
88
+ @limit = "5"
89
+ events = @kv.events[@type].take(5)
90
+ assert_equal 5, events.length
91
+ end
92
+
93
+ def test_enumerator_in_parallel_raises_not_ready_if_forced
94
+ assert_raises Orchestrate::ResultsNotReady do
95
+ @app.in_parallel { @kv.events[@type].to_a }
96
+ end
97
+ end
98
+
99
+ def test_enumerator_in_parallel_prefetches_lazy_enums
100
+ return unless [].respond_to?(:lazy)
101
+ events = nil
102
+ @app.in_parallel { events = @kv.events[@type].lazy.map{|e| e } }
103
+ assert @called, "lazy enumerator wasn't prefetched inside of parallel"
104
+ events = events.force
105
+ assert_equal 110, events.to_a.size
106
+ events.each_with_index do |event, index|
107
+ assert_equal @kv, event.key
108
+ assert_equal @type.to_s, event.type_name
109
+ assert_equal @start_time - index, event.timestamp
110
+ assert_equal 1, event.ordinal
111
+ assert event.ref
112
+ assert event.value
113
+ assert_in_delta Time.now.to_f, event.last_request_time.to_f, 1.1
114
+ end
115
+ end
116
+
117
+ def test_enumerator_in_parallel_prefetches_enums
118
+ events = nil
119
+ @app.in_parallel { events = @kv.events[@type].each }
120
+ assert @called, "enumerator wasn't prefetched inside of parallel"
121
+ assert_equal 110, events.to_a.size
122
+ events.each_with_index do |event, index|
123
+ assert_equal @kv, event.key
124
+ assert_equal @type.to_s, event.type_name
125
+ assert_equal @start_time - index, event.timestamp
126
+ assert_equal 1, event.ordinal
127
+ assert event.ref
128
+ assert event.value
129
+ assert_in_delta Time.now.to_f, event.last_request_time.to_f, 1.1
130
+ end
131
+ end
132
+
133
+ def test_enumerator_doesnt_prefetch_lazy_enums
134
+ return unless [].respond_to?(:lazy)
135
+ events = @kv.events[@type].lazy.map{|e| e }
136
+ refute @called, "lazy enumerator was prefetched"
137
+ events = events.force
138
+ assert_equal 110, events.to_a.size
139
+ events.each_with_index do |event, index|
140
+ assert_equal @kv, event.key
141
+ assert_equal @type.to_s, event.type_name
142
+ assert_equal @start_time - index, event.timestamp
143
+ assert_equal 1, event.ordinal
144
+ assert event.ref
145
+ assert event.value
146
+ assert_in_delta Time.now.to_f, event.last_request_time.to_f, 1.1
147
+ end
148
+ end
149
+
150
+ def test_enumerator_prefetches_enums
151
+ events = @kv.events[@type].each
152
+ assert @called, "enumerator wasn't prefetched"
153
+ assert_equal 110, events.to_a.size
154
+ events.each_with_index do |event, index|
155
+ assert_equal @kv, event.key
156
+ assert_equal @type.to_s, event.type_name
157
+ assert_equal @start_time - index, event.timestamp
158
+ assert_equal 1, event.ordinal
159
+ assert event.ref
160
+ assert event.value
161
+ assert_in_delta Time.now.to_f, event.last_request_time.to_f, 1.1
162
+ end
163
+ end
164
+
165
+ end
@@ -0,0 +1,266 @@
1
+ require "test_helper"
2
+
3
+ class EventTest < MiniTest::Unit::TestCase
4
+
5
+ def setup
6
+ @app, @stubs = make_application
7
+ @kv = make_kv_item(@app[:items], @stubs)
8
+ @body = {"place" => 'Home'}
9
+ @time = Time.now
10
+ @timestamp = (@time.to_f * 1000).to_i
11
+ @ord = 6
12
+ @type = 'checkins'
13
+ @ref = nil
14
+ end
15
+
16
+ def make_event
17
+ @stubs.get("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do
18
+ @ref = make_ref
19
+ body = {value: @body, timestamp: @timestamp, ordinal: @ord}
20
+ [200, response_headers({"Etag" => %|"#{@ref}"|}), body.to_json]
21
+ end
22
+ @kv.events[@type][@timestamp][@ord]
23
+ end
24
+
25
+ def assert_event_data(event)
26
+ assert_kind_of Orchestrate::Event, event
27
+ assert_equal @kv.key, event.key_name
28
+ assert_equal @kv, event.key
29
+ assert_equal @type, event.type_name
30
+ assert_equal @kv.events[:checkins], event.type
31
+ assert_equal @ref, event.ref
32
+ assert_kind_of Time, event.time
33
+ assert_in_delta @time.to_f, event.time.to_f, 1.1
34
+ assert_equal @timestamp, event.timestamp
35
+ assert_equal @ord, event.ordinal
36
+ assert_equal "/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}", event.path
37
+ assert_equal @body, event.value
38
+ @body.each do |key, value|
39
+ assert_equal value, event[key]
40
+ end
41
+ assert_in_delta Time.now.to_f, event.last_request_time.to_f, 1.1
42
+ end
43
+
44
+ def test_creates_event_without_timestamp
45
+ @stubs.post("/v0/items/#{@kv.key}/events/#{@type}") do |env|
46
+ assert_equal @body, JSON.parse(env.body)
47
+ loc = "/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}"
48
+ @ref = make_ref
49
+ [201, response_headers({"Location" => loc, "Etag" => %|"#{@ref}"|}), '']
50
+ end
51
+ event = @kv.events[:checkins] << @body
52
+ @stubs.verify_stubbed_calls
53
+ assert_event_data(event)
54
+ end
55
+
56
+ def test_creates_event_with_time
57
+ @stubs.post("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}") do |env|
58
+ assert_equal @body, JSON.parse(env.body)
59
+ loc = "/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}"
60
+ @ref = make_ref
61
+ [201, response_headers({"Location" => loc, "Etag" => %|"#{@ref}"|}), '']
62
+ end
63
+ event = @kv.events[:checkins][@time].push(@body)
64
+ @stubs.verify_stubbed_calls
65
+ assert_event_data(event)
66
+ end
67
+
68
+ def test_retrieves_single_event
69
+ @stubs.get("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
70
+ @ref = make_ref
71
+ body = {value: @body, timestamp: @timestamp, ordinal: @ord}
72
+ [200, response_headers({"Etag" => %|"#{@ref}-gzip"|}), body.to_json]
73
+ end
74
+ event = @kv.events[:checkins][@time][@ord]
75
+ assert_event_data(event)
76
+ end
77
+
78
+ def test_retrieves_single_event_returns_nil_on_not_found
79
+ @stubs.get("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
80
+ [404, response_headers, response_not_found('')]
81
+ end
82
+ assert_nil @kv.events[@type][@time][@ord]
83
+ end
84
+
85
+ def test_save_on_success_returns_true_and_updates
86
+ event = make_event
87
+ new_body = @body.merge({'foo' => 'bar'})
88
+ event[:foo] = "bar"
89
+ assert_equal new_body, event.value
90
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
91
+ assert_equal new_body, JSON.parse(env.body)
92
+ assert_header 'If-Match', %|"#{event.ref}"|, env
93
+ @ref = make_ref
94
+ [ 204, response_headers({"Etag" => %|"#{@ref}"|}), '' ]
95
+ end
96
+ assert_equal true, event.save
97
+ assert_equal @ref, event.ref
98
+ end
99
+
100
+ def test_save_on_error_returns_false
101
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do
102
+ error_response(:version_mismatch)
103
+ end
104
+ event = make_event
105
+ assert_equal false, event.save
106
+ end
107
+
108
+ def test_save_bang_on_success_returns_true_and_updates
109
+ event = make_event
110
+ new_body = @body.merge({'foo' => 'bar'})
111
+ event[:foo] = "bar"
112
+ assert_equal new_body, event.value
113
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
114
+ assert_equal new_body, JSON.parse(env.body)
115
+ assert_header 'If-Match', %|"#{event.ref}"|, env
116
+ @ref = make_ref
117
+ [ 204, response_headers({"Etag" => %|"#{@ref}"|}), '' ]
118
+ end
119
+ assert_equal true, event.save!
120
+ assert_equal @ref, event.ref
121
+ end
122
+
123
+ def test_save_bang_on_error_raises
124
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do
125
+ error_response(:version_mismatch)
126
+ end
127
+ event = make_event
128
+ assert_raises Orchestrate::API::VersionMismatch do
129
+ event.save!
130
+ end
131
+ end
132
+
133
+ def test_update_modifies_values_and_saves
134
+ event = make_event
135
+ update = {'foo' => 'bar'}
136
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
137
+ assert_equal @body.update(update), JSON.parse(env.body)
138
+ assert_header 'If-Match', %|"#{event.ref}"|, env
139
+ @ref = make_ref
140
+ [ 204, response_headers({"Etag" => %|"#{@ref}"|}), '' ]
141
+ end
142
+ assert_equal true, event.update(update)
143
+ assert_equal @body.update(update), event.value
144
+ assert_equal @ref, event.ref
145
+ end
146
+
147
+ def test_update_modifies_values_and_returns_false_on_error
148
+ event = make_event
149
+ update = {'foo' => 'bar'}
150
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
151
+ assert_equal @body.update(update), JSON.parse(env.body)
152
+ assert_header 'If-Match', %|"#{event.ref}"|, env
153
+ error_response(:version_mismatch)
154
+ end
155
+ assert_equal false, event.update(update)
156
+ assert_equal @body.update(update), event.value
157
+ end
158
+
159
+ def test_update_bang_modifies_values_and_saves
160
+ event = make_event
161
+ update = {'foo' => 'bar'}
162
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
163
+ assert_equal @body.update(update), JSON.parse(env.body)
164
+ assert_header 'If-Match', %|"#{event.ref}"|, env
165
+ @ref = make_ref
166
+ [ 204, response_headers({"Etag" => %|"#{@ref}"|}), '' ]
167
+ end
168
+ assert_equal true, event.update!(update)
169
+ assert_equal @body.update(update), event.value
170
+ assert_equal @ref, event.ref
171
+ end
172
+
173
+ def test_update_bang_modifies_value_and_raises_on_error
174
+ event = make_event
175
+ update = {'foo' => 'bar'}
176
+ @stubs.put("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
177
+ assert_equal @body.update(update), JSON.parse(env.body)
178
+ assert_header 'If-Match', %|"#{event.ref}"|, env
179
+ error_response(:version_mismatch)
180
+ end
181
+ assert_raises Orchestrate::API::VersionMismatch do
182
+ event.update!(update)
183
+ end
184
+ assert_equal @body.update(update), event.value
185
+ end
186
+
187
+ def test_purge_performs_delete_if_match_returns_true_on_success
188
+ event = make_event
189
+ @stubs.delete("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
190
+ assert_header 'If-Match', %|"#{event.ref}"|, env
191
+ [204, response_headers, '']
192
+ end
193
+ assert_equal true, event.purge
194
+ assert_nil event.ref
195
+ end
196
+
197
+ def test_purge_performs_delete_if_match_returns_false_on_error
198
+ event = make_event
199
+ @stubs.delete("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
200
+ assert_header 'If-Match', %|"#{event.ref}"|, env
201
+ error_response(:version_mismatch)
202
+ end
203
+ assert_equal false, event.purge
204
+ assert_equal @ref, event.ref
205
+ end
206
+
207
+ def test_purge_bang_performs_delete_if_match_raises_on_error
208
+ event = make_event
209
+ @stubs.delete("/v0/items/#{@kv.key}/events/#{@type}/#{@timestamp}/#{@ord}") do |env|
210
+ assert_header 'If-Match', %|"#{event.ref}"|, env
211
+ error_response(:version_mismatch)
212
+ end
213
+ assert_raises Orchestrate::API::VersionMismatch do
214
+ event.purge!
215
+ end
216
+ assert_equal @ref, event.ref
217
+ end
218
+
219
+ def test_equality_and_comparison
220
+ app, stubs = make_application
221
+ items = app[:items]
222
+
223
+ foo = Orchestrate::KeyValue.new(items, :foo)
224
+ bar = Orchestrate::KeyValue.new(items, :bar)
225
+ assert_equal foo.events, Orchestrate::KeyValue.new(items, :foo).events
226
+ assert foo.events.eql?(Orchestrate::KeyValue.new(items, :foo).events)
227
+ refute_equal foo.events, bar.events
228
+ refute foo.events.eql?(bar.events)
229
+
230
+ tweets = foo.events[:tweets]
231
+ assert_equal tweets, Orchestrate::KeyValue.new(items, :foo).events['tweets']
232
+ assert tweets.eql?(Orchestrate::KeyValue.new(items, :foo).events['tweets'])
233
+ refute_equal tweets, foo.events[:checkins]
234
+ refute tweets.eql?(foo.events[:checkins])
235
+ assert_equal(1, foo.events[:checkins] <=> tweets)
236
+ assert_equal(0, tweets <=> foo.events[:tweets])
237
+ assert_equal(-1, tweets <=> foo.events[:checkins])
238
+ refute_equal tweets, bar.events['tweets']
239
+ refute tweets.eql?(bar.events['tweets'])
240
+ assert_nil tweets <=> bar.events['tweets']
241
+
242
+ ts = (Time.now.to_f * 1000).floor
243
+ make_tweet_1 = lambda { Orchestrate::Event.from_listing(tweets, {"path" => {"ref" => make_ref}, "timestamp" => ts, "ordinal" => 10}) }
244
+ tweet1 = make_tweet_1.call
245
+ assert_equal tweet1, make_tweet_1.call
246
+ assert tweet1.eql?(make_tweet_1.call)
247
+ assert_equal 0, tweet1 <=> make_tweet_1.call
248
+
249
+ tweet2 = Orchestrate::Event.from_listing(tweets, {"path" => {"ref" => make_ref}, "timestamp" => ts, "ordinal" => 11})
250
+ refute_equal tweet1, tweet2
251
+ refute tweet1.eql?(tweet2)
252
+ assert_equal 1, tweet1 <=> tweet2
253
+ assert_equal(-1, tweet2 <=> tweet1)
254
+
255
+ tweet3 = Orchestrate::Event.from_listing(tweets, {"path" => {"ref" => make_ref}, "timestamp" => ts - 1, "ordinal" => 2})
256
+ assert_equal(1, tweet1 <=> tweet3)
257
+ assert_equal(-1, tweet3 <=> tweet1)
258
+ assert_equal(1, tweet2 <=> tweet3)
259
+ assert_equal(-1, tweet3 <=> tweet2)
260
+
261
+ checkin = Orchestrate::Event.from_listing(foo.events[:checkins], {"path" => {"ref" => make_ref}, "timestamp" => ts, "ordinal" => 40})
262
+ refute_equal tweet1, checkin
263
+ refute tweet1.eql?(checkin)
264
+ assert_nil tweet1 <=> checkin
265
+ end
266
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orchestrate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Lyon
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-08-06 00:00:00.000000000 Z
13
+ date: 2014-09-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday
@@ -134,6 +134,8 @@ files:
134
134
  - lib/orchestrate/application.rb
135
135
  - lib/orchestrate/client.rb
136
136
  - lib/orchestrate/collection.rb
137
+ - lib/orchestrate/event.rb
138
+ - lib/orchestrate/event_source.rb
137
139
  - lib/orchestrate/graph.rb
138
140
  - lib/orchestrate/key_value.rb
139
141
  - lib/orchestrate/refs.rb
@@ -151,6 +153,8 @@ files:
151
153
  - test/orchestrate/collection_kv_accessors_test.rb
152
154
  - test/orchestrate/collection_searching_test.rb
153
155
  - test/orchestrate/collection_test.rb
156
+ - test/orchestrate/event_enumeration_test.rb
157
+ - test/orchestrate/event_test.rb
154
158
  - test/orchestrate/key_value_persistence_test.rb
155
159
  - test/orchestrate/key_value_test.rb
156
160
  - test/orchestrate/ref_test.rb
@@ -193,9 +197,10 @@ test_files:
193
197
  - test/orchestrate/collection_kv_accessors_test.rb
194
198
  - test/orchestrate/collection_searching_test.rb
195
199
  - test/orchestrate/collection_test.rb
200
+ - test/orchestrate/event_enumeration_test.rb
201
+ - test/orchestrate/event_test.rb
196
202
  - test/orchestrate/key_value_persistence_test.rb
197
203
  - test/orchestrate/key_value_test.rb
198
204
  - test/orchestrate/ref_test.rb
199
205
  - test/orchestrate/relations_test.rb
200
206
  - test/test_helper.rb
201
- has_rdoc: