orchestrate 0.8.0 → 0.8.1

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: 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: