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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9ab618bc8f0fd8e7d46d7621b4378a894ec9150
4
- data.tar.gz: 4869b43cead19ce0c710d135f6551b20e0ef991a
3
+ metadata.gz: f337f0bb6cd0bea6f396f5f2cf1ac238325a5704
4
+ data.tar.gz: 5487fd1bf34564e6cc78eaad68a7d6a68d13184f
5
5
  SHA512:
6
- metadata.gz: b22bac7d118ed9f23c25f6f3f0dd8e624d99d3f0eb7da3c85b25fedb59fa280494087945eee367d08cb730ef78d8f39a5beac2c3bcddd179d808e3e07f77bc05
7
- data.tar.gz: f72465848c71bbcbde91f021ce3f6097d006d5da3bd4177a3867534b1e201d01ea3d042ed3b158049852fa38107694af611397519c774572afd30f947be2d486
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
- app.client.delete_collection(name)
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 = app.client.put(name, key_name, value, condition)
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 = app.client.post(name, value)
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
- app.client.delete(name, key_name)
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
- app.client.purge(name, key_name)
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
- # @yieldparam [Orchestrate::KeyValue] key_value The KeyValue item
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
- # @yieldparam [Orchestrate::KeyValue] key_value The KeyValue item
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.app.client.list(collection.name, params)
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
- # @yieldparam [Array<(Float, Orchestrate::KeyValue)>] score,key_value The item's score and the item.
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.app.client.search(collection.name, query, params)
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
@@ -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
- @client.put_relation(coll, key, type, other_collection, other_key)
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
- @client.delete_relation(coll, key, type, other_collection, other_key)
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 = @client.get_relations(kv_item.collection_name, kv_item.key, *edges)
134
+ @response ||= kv_item.perform(:get_relations, *edges)
133
135
  return enum_for(:each) unless block
134
- raise ResultsNotReady if @client.http.parallel_manager
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.value = listing.fetch('value')
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(@app.client.get(collection_name, key))
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(@app.client.put(collection_name, key, value, ref))
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 = @app.client.delete(collection_name, key, ref)
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 = @app.client.purge(collection_name, key, ref)
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
@@ -1,4 +1,4 @@
1
1
  module Orchestrate
2
2
  # @return [String] The version number of the Orchestrate Gem
3
- VERSION = "0.8.0"
3
+ VERSION = "0.8.1"
4
4
  end
data/lib/orchestrate.rb CHANGED
@@ -3,6 +3,7 @@ require "orchestrate/client"
3
3
  require "orchestrate/application"
4
4
  require "orchestrate/collection"
5
5
  require "orchestrate/key_value"
6
+ require "orchestrate/refs"
6
7
  require "orchestrate/graph"
7
8
  require "orchestrate/version"
8
9
  #
@@ -13,6 +13,8 @@ class KeyValueTest < MiniTest::Unit::TestCase
13
13
  assert_equal "items/hello", kv.id
14
14
  assert_equal body, kv.value
15
15
  assert kv.loaded?
16
+ refute kv.archival?
17
+ refute kv.tombstone?
16
18
  assert_in_delta Time.now.to_f, kv.last_request_time.to_f, 1.1
17
19
  end
18
20
 
@@ -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 }, "value" => body }
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.0
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-07-24 00:00:00.000000000 Z
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
- - ".gitignore"
123
- - ".travis.yml"
124
- - ".yardopts"
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.3
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: