orchestrate 0.8.0 → 0.8.1
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 +4 -4
- data/README.md +4 -0
- data/lib/orchestrate/application.rb +10 -0
- data/lib/orchestrate/collection.rb +31 -12
- data/lib/orchestrate/graph.rb +9 -6
- data/lib/orchestrate/key_value.rb +40 -5
- data/lib/orchestrate/refs.rb +184 -0
- data/lib/orchestrate/version.rb +1 -1
- data/lib/orchestrate.rb +1 -0
- data/test/orchestrate/key_value_test.rb +2 -0
- data/test/orchestrate/ref_test.rb +129 -0
- data/test/test_helper.rb +6 -1
- metadata +25 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f337f0bb6cd0bea6f396f5f2cf1ac238325a5704
|
4
|
+
data.tar.gz: 5487fd1bf34564e6cc78eaad68a7d6a68d13184f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba3609ecc24d1c0d1f5d919d6450769efe77e737eee5d785c888b6b31139c1d2d3b7baab54fe7ff188083c226bf8d243280a9786cf39a8b5b416a636aa258707
|
7
|
+
data.tar.gz: 32f3f69256d28a0733d9475adeb5adfb231c4f8e44a91e49cbbf1335b66397cfb8f106e6626df1e6d169d7ac8c02ee4db797250a36919de56b572af8cde45d47
|
data/README.md
CHANGED
@@ -159,6 +159,10 @@ end
|
|
159
159
|
|
160
160
|
## Release Notes
|
161
161
|
|
162
|
+
### August 6, 2014: release 0.8.1
|
163
|
+
- Implement `KeyValue#refs`, `RefList` and `Ref` to access a KeyValue's Refs.
|
164
|
+
- Refactor `Client` api accessors on Object client to internal `#perform` methods.
|
165
|
+
|
162
166
|
### July 24, 2014: release 0.8.0
|
163
167
|
- **BACKWARDS-INCOMPATIBLE** Fix #69, `Client` will url-escape path segments. If you have keys with slashes or spaces or other
|
164
168
|
characters escaped by `URI.escape` the client will now behave as expected, however if you've used these keys with this client
|
@@ -51,10 +51,20 @@ module Orchestrate
|
|
51
51
|
results
|
52
52
|
end
|
53
53
|
|
54
|
+
# is the Application currently inside a block from `#in_parallel?`?
|
55
|
+
# @return [true, false]
|
54
56
|
def inside_parallel?
|
55
57
|
!! @inside_parallel
|
56
58
|
end
|
57
59
|
|
60
|
+
# Perform a request against the client.
|
61
|
+
# @param api_method [Symbol] The method on the client to call
|
62
|
+
# @param args [#to_s, #to_json, Hash] The arguments for the method being called.
|
63
|
+
# @return Orchestrate::API::Response
|
64
|
+
def perform(api_method, *args)
|
65
|
+
client.send(api_method, *args)
|
66
|
+
end
|
67
|
+
|
58
68
|
# @return a pretty-printed representation of the application.
|
59
69
|
def to_s
|
60
70
|
"#<Orchestrate::Application api_key=#{api_key[0..7]}...>"
|
@@ -51,7 +51,7 @@ module Orchestrate
|
|
51
51
|
# Deletes the collection.
|
52
52
|
# @return Orchestrate::API::Response
|
53
53
|
def destroy!
|
54
|
-
|
54
|
+
perform :delete_collection
|
55
55
|
end
|
56
56
|
|
57
57
|
# @!endgroup
|
@@ -90,7 +90,7 @@ module Orchestrate
|
|
90
90
|
# @raise Orchestrate::API::AlreadyPresent a false condition was provided, but a value already exists for this key
|
91
91
|
# @see #[]=
|
92
92
|
def set(key_name, value, condition=nil)
|
93
|
-
response =
|
93
|
+
response = perform(:put, key_name, value, condition)
|
94
94
|
KeyValue.from_bodyless_response(self, key_name, value, response)
|
95
95
|
end
|
96
96
|
|
@@ -101,7 +101,7 @@ module Orchestrate
|
|
101
101
|
# @note If the create is considered a success but the client is unable to parse the key
|
102
102
|
# from the location header, an API::ServiceError is raised.
|
103
103
|
def <<(value)
|
104
|
-
response =
|
104
|
+
response = perform(:post, value)
|
105
105
|
match_data = response.location.match(%r{#{name}/([^/]+)})
|
106
106
|
raise API::ServiceError.new(response) unless match_data
|
107
107
|
KeyValue.from_bodyless_response(self, match_data[1], value, response)
|
@@ -159,7 +159,7 @@ module Orchestrate
|
|
159
159
|
# @param key_name [#to_s] The name of the key
|
160
160
|
# @return [true] If the request suceeds.
|
161
161
|
def delete(key_name)
|
162
|
-
|
162
|
+
perform(:delete, key_name)
|
163
163
|
true
|
164
164
|
end
|
165
165
|
|
@@ -168,7 +168,7 @@ module Orchestrate
|
|
168
168
|
# @param key_name [#to_s] The name of the key
|
169
169
|
# @return [true] If the request suceeds.
|
170
170
|
def purge(key_name)
|
171
|
-
|
171
|
+
perform(:purge, key_name)
|
172
172
|
true
|
173
173
|
end
|
174
174
|
|
@@ -180,7 +180,10 @@ module Orchestrate
|
|
180
180
|
|
181
181
|
# Iterates over each KeyValue item in the collection. Used as the basis for Enumerable methods.
|
182
182
|
# Items are provided in lexicographically sorted order by key name.
|
183
|
-
# @
|
183
|
+
# @overload each
|
184
|
+
# @return [Enumerator]
|
185
|
+
# @overload each(&block)
|
186
|
+
# @yieldparam [Orchestrate::KeyValue] key_value The KeyValue item
|
184
187
|
# @see KeyValueList#each
|
185
188
|
# @example
|
186
189
|
# keys = collection.take(20).map(&:key)
|
@@ -190,7 +193,8 @@ module Orchestrate
|
|
190
193
|
end
|
191
194
|
|
192
195
|
# Creates a Lazy Enumerator for the Collection's KeyValue List. If called inside the app's
|
193
|
-
# `#in_parallel` block, pre-fetches results.
|
196
|
+
# `#in_parallel` block, pre-fetches the first page of results.
|
197
|
+
# @return [Enumerator::Lazy]
|
194
198
|
def lazy
|
195
199
|
KeyValueList.new(self).lazy
|
196
200
|
end
|
@@ -226,7 +230,7 @@ module Orchestrate
|
|
226
230
|
# Returns the first n items. Equivalent to Enumerable#take. Sets the
|
227
231
|
# `limit` parameter on the query to Orchestrate, so we don't ask for more than is needed.
|
228
232
|
# @param count [Integer] The number of items to limit to.
|
229
|
-
# @return [Array]
|
233
|
+
# @return [Array<KeyValue>]
|
230
234
|
def take(count)
|
231
235
|
KeyValueList.new(self).take(count)
|
232
236
|
end
|
@@ -292,7 +296,10 @@ module Orchestrate
|
|
292
296
|
|
293
297
|
# Iterates over each KeyValue item in the collection. Used as the basis for Enumerable methods.
|
294
298
|
# Items are provided in lexicographically sorted order by key name.
|
295
|
-
# @
|
299
|
+
# @overload each
|
300
|
+
# @return Enumerator
|
301
|
+
# @overload each(&block)
|
302
|
+
# @yieldparam [Orchestrate::KeyValue] key_value The KeyValue item
|
296
303
|
# @example
|
297
304
|
# keys = collection.after(:foo).take(20).map(&:key)
|
298
305
|
# # returns the first 20 keys in the collection that occur after "foo"
|
@@ -307,7 +314,7 @@ module Orchestrate
|
|
307
314
|
params[end_key] = range[:end]
|
308
315
|
end
|
309
316
|
params[:limit] = range[:limit]
|
310
|
-
@response ||= collection.
|
317
|
+
@response ||= collection.perform(:list, params)
|
311
318
|
return enum_for(:each) unless block_given?
|
312
319
|
raise ResultsNotReady.new if collection.app.inside_parallel?
|
313
320
|
loop do
|
@@ -375,7 +382,10 @@ module Orchestrate
|
|
375
382
|
|
376
383
|
# Iterates over each result from the Search. Used as the basis for Enumerable methods. Items are
|
377
384
|
# provided on the basis of score, with most relevant first.
|
378
|
-
# @
|
385
|
+
# @overload each
|
386
|
+
# @return Enumerator
|
387
|
+
# @overload each(&block)
|
388
|
+
# @yieldparam [Array<(Float, Orchestrate::KeyValue)>] score,key_value The item's score and the item.
|
379
389
|
# @example
|
380
390
|
# collection.search("trendy").take(5).each do |score, item|
|
381
391
|
# puts "#{item.key} has a trend score of #{score}"
|
@@ -383,7 +393,7 @@ module Orchestrate
|
|
383
393
|
def each
|
384
394
|
params = {limit:limit}
|
385
395
|
params[:offset] = offset if offset
|
386
|
-
@response ||= collection.
|
396
|
+
@response ||= collection.perform(:search, query, params)
|
387
397
|
return enum_for(:each) unless block_given?
|
388
398
|
raise ResultsNotReady.new if collection.app.inside_parallel?
|
389
399
|
loop do
|
@@ -429,5 +439,14 @@ module Orchestrate
|
|
429
439
|
end
|
430
440
|
end
|
431
441
|
|
442
|
+
# Tells the Applicaiton to perform a request on its client, automatically
|
443
|
+
# providing the collection name.
|
444
|
+
# @param api_method [Symbol] The method on the client to call.
|
445
|
+
# @param args [#to_s, #to_json, Hash] The arguments for the method.
|
446
|
+
# @return API::Response
|
447
|
+
def perform(api_method, *args)
|
448
|
+
app.perform(api_method, name, *args)
|
449
|
+
end
|
450
|
+
|
432
451
|
end
|
433
452
|
end
|
data/lib/orchestrate/graph.rb
CHANGED
@@ -32,10 +32,13 @@ module Orchestrate
|
|
32
32
|
# @param type_name [#to_s] the type of relation this RelationStem interacts with.
|
33
33
|
def initialize(kv_item, type_name)
|
34
34
|
@kv_item = kv_item
|
35
|
-
@client = kv_item.collection.app.client
|
36
35
|
@type = type_name.to_s
|
37
36
|
end
|
38
37
|
|
38
|
+
def perform(api_method, *args)
|
39
|
+
kv_item.perform(api_method, type, *args)
|
40
|
+
end
|
41
|
+
|
39
42
|
# [Creates a relationship between two objects](http://orchestrate.io/docs/api/#graph/put).
|
40
43
|
# Relations can span collections.
|
41
44
|
# @overload <<(key_value_item)
|
@@ -47,7 +50,7 @@ module Orchestrate
|
|
47
50
|
def <<(other_item_or_collection_name, other_key=nil)
|
48
51
|
coll, key = get_collection_and_key(kv_item, nil)
|
49
52
|
other_collection, other_key = get_collection_and_key(other_item_or_collection_name, other_key)
|
50
|
-
|
53
|
+
perform(:put_relation, other_collection, other_key)
|
51
54
|
end
|
52
55
|
alias :push :<<
|
53
56
|
|
@@ -61,7 +64,7 @@ module Orchestrate
|
|
61
64
|
def delete(other_item_or_collection_name, other_key=nil)
|
62
65
|
coll, key = get_collection_and_key(kv_item, nil)
|
63
66
|
other_collection, other_key = get_collection_and_key(other_item_or_collection_name, other_key)
|
64
|
-
|
67
|
+
perform(:delete_relation, other_collection, other_key)
|
65
68
|
end
|
66
69
|
|
67
70
|
# Adds depth to the retrieval of related items.
|
@@ -113,7 +116,6 @@ module Orchestrate
|
|
113
116
|
def initialize(kv_item, edge_names)
|
114
117
|
@kv_item = kv_item
|
115
118
|
@edges = edge_names
|
116
|
-
@client = kv_item.collection.app.client
|
117
119
|
end
|
118
120
|
|
119
121
|
# Add a new type to the depth of the graph query.
|
@@ -129,13 +131,14 @@ module Orchestrate
|
|
129
131
|
# iterates over each item in the result. Used as the basis for
|
130
132
|
# Enumerable methods.
|
131
133
|
def each(&block)
|
132
|
-
@response
|
134
|
+
@response ||= kv_item.perform(:get_relations, *edges)
|
133
135
|
return enum_for(:each) unless block
|
134
|
-
raise ResultsNotReady if
|
136
|
+
raise ResultsNotReady if kv_item.collection.app.inside_parallel?
|
135
137
|
@response.results.each do |listing|
|
136
138
|
listing_collection = kv_item.collection.app[listing['path']['collection']]
|
137
139
|
yield KeyValue.from_listing(listing_collection, listing, @response)
|
138
140
|
end
|
141
|
+
@response = nil
|
139
142
|
end
|
140
143
|
|
141
144
|
end
|
@@ -41,7 +41,8 @@ module Orchestrate
|
|
41
41
|
kv = new(collection, key, response)
|
42
42
|
kv.instance_variable_set(:@ref, ref)
|
43
43
|
kv.instance_variable_set(:@reftime, listing['reftime']) if listing['reftime']
|
44
|
-
kv.
|
44
|
+
kv.instance_variable_set(:@tombstone, path['tombstone'])
|
45
|
+
kv.value = listing.fetch('value', nil)
|
45
46
|
kv
|
46
47
|
end
|
47
48
|
|
@@ -134,7 +135,22 @@ module Orchestrate
|
|
134
135
|
# Reload this KeyValue item from Orchestrate.
|
135
136
|
# @raise Orchestrate::API::NotFound if the KeyValue no longer exists.
|
136
137
|
def reload
|
137
|
-
load_from_response(
|
138
|
+
load_from_response(perform(:get))
|
139
|
+
end
|
140
|
+
|
141
|
+
# Is this the "Current" value for this KeyValue? False for non-Ref objects.
|
142
|
+
# Use this instead of `#is_a?` or `#kind_of?`.
|
143
|
+
# @return [true, false]
|
144
|
+
def archival?
|
145
|
+
false
|
146
|
+
end
|
147
|
+
|
148
|
+
# Is this a Ref that represents a null value (created through `#destroy` or
|
149
|
+
# similar)? False for most cases -- The only way to retrieve a Ref for which
|
150
|
+
# this will return true is by enumerating trhough values from a RefList.
|
151
|
+
# @return [true, false]
|
152
|
+
def tombstone?
|
153
|
+
!! @tombstone
|
138
154
|
end
|
139
155
|
|
140
156
|
# @!group Attribute accessors
|
@@ -178,7 +194,7 @@ module Orchestrate
|
|
178
194
|
# @raise [Orchestrate::API::RequestError, Orchestrate::API::ServiceError] If there are any other problems with saving.
|
179
195
|
def save!
|
180
196
|
begin
|
181
|
-
load_from_response(
|
197
|
+
load_from_response(perform(:put, value, ref))
|
182
198
|
true
|
183
199
|
rescue API::IndexingConflict => e
|
184
200
|
@ref = e.response.headers['Location'].split('/').last
|
@@ -222,7 +238,7 @@ module Orchestrate
|
|
222
238
|
# Deletes a KeyValue item from Orchestrate using 'If-Match' with the current ref.
|
223
239
|
# @raise [Orchestrate::API::VersionMismatch] If the KeyValue item has been updated with a new ref since this KeyValue was loaded.
|
224
240
|
def destroy!
|
225
|
-
response =
|
241
|
+
response = perform(:delete, ref)
|
226
242
|
@ref = nil
|
227
243
|
@last_request_time = response.request_time
|
228
244
|
true
|
@@ -242,7 +258,7 @@ module Orchestrate
|
|
242
258
|
# Deletes a KeyValue item and its entire Ref history from Orchestrate using 'If-Match' with the current ref.
|
243
259
|
# @raise [Orchestrate::API::VersionMismatch] If the KeyValue item has been updated with a new ref since this KeyValue was loaded.
|
244
260
|
def purge!
|
245
|
-
response =
|
261
|
+
response = perform(:purge, ref)
|
246
262
|
@ref = nil
|
247
263
|
@last_request_time = response.request_time
|
248
264
|
true
|
@@ -250,6 +266,16 @@ module Orchestrate
|
|
250
266
|
|
251
267
|
# @!endgroup persistence
|
252
268
|
#
|
269
|
+
# @!group refs
|
270
|
+
|
271
|
+
# Entry point for retrieving Refs associated with this KeyValue.
|
272
|
+
# @return [Orchestrate::RefList] A RefList instance bound to this KeyValue.
|
273
|
+
def refs
|
274
|
+
@refs ||= RefList.new(self)
|
275
|
+
end
|
276
|
+
|
277
|
+
# @!endgroup refs
|
278
|
+
#
|
253
279
|
# @!group relations
|
254
280
|
|
255
281
|
# Entry point for managing the graph relationships for this KeyValue item
|
@@ -260,6 +286,15 @@ module Orchestrate
|
|
260
286
|
|
261
287
|
# @!endgroup relations
|
262
288
|
|
289
|
+
# Calls a method on the Collection's Application's client, providing the
|
290
|
+
# Collection's name and KeyValue's key.
|
291
|
+
# @param api_method [Symbol] The method on the client to call.
|
292
|
+
# @param args [#to_s, #to_json, Hash] The arguments for the method.
|
293
|
+
# @return API::Response
|
294
|
+
def perform(api_method, *args)
|
295
|
+
collection.perform(api_method, key, *args)
|
296
|
+
end
|
297
|
+
|
263
298
|
private
|
264
299
|
def load_from_response(response)
|
265
300
|
response.on_complete do
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module Orchestrate
|
2
|
+
# A Ref is a specific immutable value that has been assigned to a Key.
|
3
|
+
#
|
4
|
+
# Ref is subclassed from KeyValue and as such contains its `#save` and `#update`
|
5
|
+
# methods. Unless used on the most recent Ref, these will fail with a
|
6
|
+
# VersionMismatch error, as the #save and #update methods use Put-If-Match
|
7
|
+
# semantics.
|
8
|
+
class Ref < KeyValue
|
9
|
+
|
10
|
+
# Is this a historic ref value? True for Ref objects. Use this instead of
|
11
|
+
# `#is_a?` or `#kind_of?`.
|
12
|
+
# @return [true, false]
|
13
|
+
def archival?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# An enumerator over a query for listing Ref values in an Orchestrate::KeyValue.
|
19
|
+
class RefList
|
20
|
+
|
21
|
+
# @return [KeyValue]
|
22
|
+
attr_accessor :key_value
|
23
|
+
|
24
|
+
# Instantiates a new RefList
|
25
|
+
# @param key_value [KeyValue] The KeyValue item to retrieve Refs for.
|
26
|
+
def initialize(key_value)
|
27
|
+
@key_value = key_value
|
28
|
+
end
|
29
|
+
|
30
|
+
# Accessor for individual Refs.
|
31
|
+
# @param ref_id [#to_s] The ref of the specific immutable value to retrieve.
|
32
|
+
def [](ref_id)
|
33
|
+
response = @key_value.perform(:get, ref_id)
|
34
|
+
Ref.new(@key_value.collection, @key_value.key, response)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!group Ref Enumeration
|
38
|
+
|
39
|
+
include Enumerable
|
40
|
+
|
41
|
+
# Iterates over each Ref for the KeyValue. Used as the basis for Enumerable.
|
42
|
+
# Refs are provided in time-series order, newest to oldest.
|
43
|
+
# @overload each
|
44
|
+
# @return Enumerator
|
45
|
+
# @overload each(&block)
|
46
|
+
# @yieldparam [Ref] ref the Ref item
|
47
|
+
# @see RefList::Fetcher#each
|
48
|
+
# @example
|
49
|
+
# ref_ids = kv.refs.take(20).map(&:ref)
|
50
|
+
def each(&block)
|
51
|
+
Fetcher.new(self).each(&block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Creates a Lazy Enumerator for the RefList. If called inside the Applciation's
|
55
|
+
# `#in_parallel` block, pre-fetches the first page of results.
|
56
|
+
# @return [Enumerator::Lazy]
|
57
|
+
def lazy
|
58
|
+
Fetcher.new(self).lazy
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the first n items. Equivalent to Enumerable#take. Sets the `limit`
|
62
|
+
# parameter on the query to Orchestrate, so we don't ask for more items
|
63
|
+
# than desired.
|
64
|
+
# @return [Array<Ref>]
|
65
|
+
def take(count)
|
66
|
+
Fetcher.new(self).take(count)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Set the offset for the query to Orchestrate, so you can skip items. Does
|
70
|
+
# not fetch results. Implemented as a separate method from drop, unlike #take,
|
71
|
+
# because take is a more common use case.
|
72
|
+
# @return [RefList::Fetcher]
|
73
|
+
def offset(count)
|
74
|
+
Fetcher.new(self).offset(count)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Specifies that the query to Orchestrate should use the `values` parameter,
|
78
|
+
# and that the query should return the values for each ref.
|
79
|
+
# @return [RefList::Fetcher]
|
80
|
+
def with_values
|
81
|
+
Fetcher.new(self).with_values
|
82
|
+
end
|
83
|
+
|
84
|
+
# @!endgroup
|
85
|
+
|
86
|
+
# Responsible for actually retrieving the list of Refs from Orchestrate.
|
87
|
+
class Fetcher
|
88
|
+
|
89
|
+
# The KeyValue to retrieve Refs for.
|
90
|
+
# @return [KeyValue]
|
91
|
+
attr_accessor :key_value
|
92
|
+
|
93
|
+
# The maximum number of Refs to return.
|
94
|
+
# @return [Integer]
|
95
|
+
attr_accessor :limit
|
96
|
+
|
97
|
+
# Whether to request values for each Ref or not.
|
98
|
+
# @return [nil, true] defaults to nil
|
99
|
+
attr_accessor :values
|
100
|
+
|
101
|
+
# Instantiates a new RefList::Fetcher.
|
102
|
+
# @param reflist [RefList] The RefList to base the queries on.
|
103
|
+
def initialize(reflist)
|
104
|
+
@key_value = reflist.key_value
|
105
|
+
@limit = 100
|
106
|
+
end
|
107
|
+
|
108
|
+
include Enumerable
|
109
|
+
|
110
|
+
# @!group Ref Enumeration
|
111
|
+
|
112
|
+
# Iterates over each Ref for the KeyValue. Used as the basis for Enumerable.
|
113
|
+
# Refs are provided in time-series order, newest to oldest.
|
114
|
+
# @overload each
|
115
|
+
# @return Enumerator
|
116
|
+
# @overload each(&block)
|
117
|
+
# @yieldparam [Ref] ref the Ref item
|
118
|
+
# @example
|
119
|
+
# ref_enum = kv.refs.each
|
120
|
+
def each
|
121
|
+
params = {limit: limit}
|
122
|
+
params[:offset] = offset if offset
|
123
|
+
params[:values] = true if values
|
124
|
+
@response ||= key_value.perform(:list_refs, params)
|
125
|
+
return enum_for(:each) unless block_given?
|
126
|
+
raise ResultsNotReady.new if key_value.collection.app.inside_parallel?
|
127
|
+
loop do
|
128
|
+
@response.results.each do |doc|
|
129
|
+
yield Ref.from_listing(key_value.collection, doc, @response)
|
130
|
+
end
|
131
|
+
break unless @response.next_link
|
132
|
+
@response = @response.next_results
|
133
|
+
end
|
134
|
+
@response = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
# Creates a Lazy Enumerator for the Fetcher. If called inside the Application's
|
138
|
+
# `#in_parallel` block, pre-fetches the first page of results.
|
139
|
+
# @return [Enumerator::Lazy]
|
140
|
+
def lazy
|
141
|
+
return each.lazy if key_value.collection.app.inside_parallel?
|
142
|
+
super
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the first n items. Equivalent to Enumerable#take. Sets the `limit`
|
146
|
+
# parameter on the query to Orchestrate, so we don't ask for more items than
|
147
|
+
# desired.
|
148
|
+
# @param count [Integer] A positive integer constrained between 1 and 100.
|
149
|
+
# @return [Array<Ref>]
|
150
|
+
def take(count)
|
151
|
+
count = 1 if count < 1
|
152
|
+
count = 100 if count > 100
|
153
|
+
@limit = count
|
154
|
+
super(count)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Sets the offset for the query to Orchestrate, so you can skip items. Does
|
158
|
+
# not fetch results. Implemented as a separate method from drop, unlike #take,
|
159
|
+
# because take is a more common use case.
|
160
|
+
# @overload offset
|
161
|
+
# @return [Integer] the current offset value
|
162
|
+
# @overload offset(count)
|
163
|
+
# @param count [Integer] The number of records to skip. Constrained to positive integers.
|
164
|
+
# @return [self]
|
165
|
+
def offset(count=nil)
|
166
|
+
if count
|
167
|
+
count = 1 if count < 1
|
168
|
+
@offset = count.to_i
|
169
|
+
return self
|
170
|
+
else
|
171
|
+
@offset
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Specifies the query to Orchestrate shoul duse the `values` parameter,
|
176
|
+
# and the query should return values for each ref.
|
177
|
+
# @return [self]
|
178
|
+
def with_values
|
179
|
+
@values = true
|
180
|
+
self
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/lib/orchestrate/version.rb
CHANGED
data/lib/orchestrate.rb
CHANGED
@@ -0,0 +1,129 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RefTest < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@app, @stubs = make_application({parallel: true})
|
6
|
+
@kv = make_kv_item(@app[:items], @stubs)
|
7
|
+
@limit_to_assert = "100"
|
8
|
+
@called = false
|
9
|
+
@wants_values = false
|
10
|
+
@stubs.get("/v0/items/#{@kv.key}/refs") do |env|
|
11
|
+
@called = true
|
12
|
+
if @wants_values
|
13
|
+
assert_equal("true", env.params['values'])
|
14
|
+
else
|
15
|
+
assert_nil env.params['values']
|
16
|
+
end
|
17
|
+
assert_equal @limit_to_assert.to_s, env.params['limit']
|
18
|
+
assert_includes [nil, "100"], env.params['offset']
|
19
|
+
range = env.params['offset'] ? 101..150 : 1..100
|
20
|
+
list = range.map do |i|
|
21
|
+
make_kv_listing(@kv.collection,
|
22
|
+
{value: @wants_values, key: @kv.key,
|
23
|
+
ref: "#{i}", tombstone: i % 6 == 0,
|
24
|
+
reftime: Time.now.to_f - i * 3600_00})
|
25
|
+
end
|
26
|
+
body = {results: list, count: range.respond_to?(:size) ? range.size : range.end - range.begin}
|
27
|
+
next_link = "/v0/items/#{@kv.key}/refs?offset=100&limit=100"
|
28
|
+
next_link += "&values=true" if @wants_values
|
29
|
+
body['next'] = next_link unless env.params['offset']
|
30
|
+
[200, response_headers, body.to_json]
|
31
|
+
end
|
32
|
+
@assert_ref_listing = lambda do |ref|
|
33
|
+
assert ref.archival?
|
34
|
+
assert_equal @kv.key, ref.key
|
35
|
+
if ref.ref.to_i % 6 == 0
|
36
|
+
assert ref.tombstone?, "Ref #{ref.ref} should be tombstone, isn't"
|
37
|
+
assert_nil ref.value
|
38
|
+
else
|
39
|
+
refute ref.tombstone?, "Ref #{ref.ref} should not be tombstone, is"
|
40
|
+
if @wants_values
|
41
|
+
assert_kind_of Hash, ref.value
|
42
|
+
else
|
43
|
+
assert_equal Hash.new, ref.value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_retrieves_single_ref
|
50
|
+
ref = make_ref
|
51
|
+
path = "/v0/items/#{@kv.key}/refs/#{ref}"
|
52
|
+
value = {"hello" => "history"}
|
53
|
+
@stubs.get(path) do |env|
|
54
|
+
[ 200, response_headers({'Etag' => "\"ref\"", "Content-Location" => path}), value.to_json]
|
55
|
+
end
|
56
|
+
ref_value = @kv.refs[ref]
|
57
|
+
assert ref_value.archival?
|
58
|
+
refute ref_value.tombstone?
|
59
|
+
assert_equal value, ref_value.value
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_enumerates_over_refs
|
63
|
+
refs = @kv.refs.to_a
|
64
|
+
assert_equal 150, refs.size
|
65
|
+
refs.each(&@assert_ref_listing)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_enumerates_in_parallel_raises_not_ready_if_forced
|
69
|
+
@limit_to_assert = "5"
|
70
|
+
assert_raises Orchestrate::ResultsNotReady do
|
71
|
+
@app.in_parallel { @kv.refs.take(5) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_enumerates_in_parallel_prefetches_lazy_enums
|
76
|
+
return unless [].respond_to?(:lazy)
|
77
|
+
refs = nil
|
78
|
+
@app.in_parallel { refs = @kv.refs.lazy.map {|r| r } }
|
79
|
+
assert @called, "lazy enumerator in parallel was not prefetched"
|
80
|
+
refs = refs.force
|
81
|
+
assert_equal 150, refs.length
|
82
|
+
refs.each(&@assert_ref_listing)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_enumerator_in_parallel_fetches_enums
|
86
|
+
refs = nil
|
87
|
+
@app.in_parallel { refs = @kv.refs.each }
|
88
|
+
assert @called, "enumerator wasn't prefetched inside of parallel"
|
89
|
+
assert_equal 150, refs.to_a.size
|
90
|
+
refs.each(&@assert_ref_listing)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_enumerator_doesnt_prefetch_lazy_enums
|
94
|
+
return unless [].respond_to?(:lazy)
|
95
|
+
refs = @kv.refs.lazy.map {|r| r }
|
96
|
+
refute @called, "lazy enumerator was prefetched outside of parallel"
|
97
|
+
refs = refs.force
|
98
|
+
assert_equal 150, refs.length
|
99
|
+
refs.each(&@assert_ref_listing)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_enumerator_prefetches_enums
|
103
|
+
refs = @kv.refs.each
|
104
|
+
assert @called, "enumerator was not prefetched"
|
105
|
+
assert_equal 150, refs.to_a.size
|
106
|
+
refs.each(&@assert_ref_listing)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_take_sets_limit
|
110
|
+
@limit_to_assert = 5
|
111
|
+
refs = @kv.refs.take(5).to_a
|
112
|
+
assert_equal 5, refs.length
|
113
|
+
refs.each(&@assert_ref_listing)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_offset_sets_offset
|
117
|
+
refs = @kv.refs.offset(100).to_a
|
118
|
+
assert_equal 50, refs.size
|
119
|
+
refs.each(&@assert_ref_listing)
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_enumerates_with_values
|
123
|
+
@wants_values = true
|
124
|
+
refs = @kv.refs.with_values.to_a
|
125
|
+
assert_equal 150, refs.size
|
126
|
+
refs.each(&@assert_ref_listing)
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -97,7 +97,12 @@ def make_kv_listing(collection, opts={})
|
|
97
97
|
score = opts[:score]
|
98
98
|
body = opts[:body] || {"key" => key}
|
99
99
|
collection = collection.name if collection.kind_of?(Orchestrate::Collection)
|
100
|
-
result = { "path" => { "collection" => collection, "key" => key, "ref" => ref }
|
100
|
+
result = { "path" => { "collection" => collection, "key" => key, "ref" => ref }}
|
101
|
+
if opts[:tombstone]
|
102
|
+
result["path"]["tombstone"] = true
|
103
|
+
else
|
104
|
+
result["value"] = opts.fetch(:value, true) ? body : {}
|
105
|
+
end
|
101
106
|
result["reftime"] = reftime if reftime
|
102
107
|
result["score"] = score if score
|
103
108
|
result
|
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.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Lyon
|
@@ -10,104 +10,104 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: faraday
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '0.9'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0.9'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: net-http-persistent
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ~>
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '2.9'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ~>
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '2.9'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: bundler
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- -
|
47
|
+
- - ~>
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '1.6'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - ~>
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '1.6'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: rake
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- -
|
61
|
+
- - '>='
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0'
|
64
64
|
type: :development
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
|
-
- -
|
68
|
+
- - '>='
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: typhoeus
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- -
|
82
|
+
- - '>='
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: em-http-request
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
88
88
|
requirements:
|
89
|
-
- -
|
89
|
+
- - '>='
|
90
90
|
- !ruby/object:Gem::Version
|
91
91
|
version: '0'
|
92
92
|
type: :development
|
93
93
|
prerelease: false
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- -
|
96
|
+
- - '>='
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
- !ruby/object:Gem::Dependency
|
100
100
|
name: em-synchrony
|
101
101
|
requirement: !ruby/object:Gem::Requirement
|
102
102
|
requirements:
|
103
|
-
- -
|
103
|
+
- - '>='
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: '0'
|
106
106
|
type: :development
|
107
107
|
prerelease: false
|
108
108
|
version_requirements: !ruby/object:Gem::Requirement
|
109
109
|
requirements:
|
110
|
-
- -
|
110
|
+
- - '>='
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: '0'
|
113
113
|
description: Client for the Orchestrate REST API
|
@@ -119,9 +119,9 @@ executables: []
|
|
119
119
|
extensions: []
|
120
120
|
extra_rdoc_files: []
|
121
121
|
files:
|
122
|
-
-
|
123
|
-
-
|
124
|
-
-
|
122
|
+
- .gitignore
|
123
|
+
- .travis.yml
|
124
|
+
- .yardopts
|
125
125
|
- Gemfile
|
126
126
|
- LICENSE
|
127
127
|
- README.md
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- lib/orchestrate/collection.rb
|
137
137
|
- lib/orchestrate/graph.rb
|
138
138
|
- lib/orchestrate/key_value.rb
|
139
|
+
- lib/orchestrate/refs.rb
|
139
140
|
- lib/orchestrate/version.rb
|
140
141
|
- orchestrate.gemspec
|
141
142
|
- test/orchestrate/api/collections_api_test.rb
|
@@ -152,6 +153,7 @@ files:
|
|
152
153
|
- test/orchestrate/collection_test.rb
|
153
154
|
- test/orchestrate/key_value_persistence_test.rb
|
154
155
|
- test/orchestrate/key_value_test.rb
|
156
|
+
- test/orchestrate/ref_test.rb
|
155
157
|
- test/orchestrate/relations_test.rb
|
156
158
|
- test/test_helper.rb
|
157
159
|
homepage: https://github.com/orchestrate-io/orchestrate-ruby
|
@@ -164,17 +166,17 @@ require_paths:
|
|
164
166
|
- lib
|
165
167
|
required_ruby_version: !ruby/object:Gem::Requirement
|
166
168
|
requirements:
|
167
|
-
- -
|
169
|
+
- - '>='
|
168
170
|
- !ruby/object:Gem::Version
|
169
171
|
version: '0'
|
170
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
173
|
requirements:
|
172
|
-
- -
|
174
|
+
- - '>='
|
173
175
|
- !ruby/object:Gem::Version
|
174
176
|
version: '0'
|
175
177
|
requirements: []
|
176
178
|
rubyforge_project:
|
177
|
-
rubygems_version: 2.0.
|
179
|
+
rubygems_version: 2.0.14
|
178
180
|
signing_key:
|
179
181
|
specification_version: 4
|
180
182
|
summary: Ruby client for Orchestrate.io
|
@@ -193,6 +195,7 @@ test_files:
|
|
193
195
|
- test/orchestrate/collection_test.rb
|
194
196
|
- test/orchestrate/key_value_persistence_test.rb
|
195
197
|
- test/orchestrate/key_value_test.rb
|
198
|
+
- test/orchestrate/ref_test.rb
|
196
199
|
- test/orchestrate/relations_test.rb
|
197
200
|
- test/test_helper.rb
|
198
201
|
has_rdoc:
|