orchestrate 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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: