em-hyperdex-client 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.yardopts +1 -0
- data/Rakefile +3 -6
- data/em-hyperdex-client.gemspec +2 -1
- data/lib/em-hyperdex-client.rb +236 -41
- data/spec/get_spec.rb +88 -0
- data/spec/search_spec.rb +48 -0
- data/spec/spec_helper.rb +98 -0
- metadata +33 -6
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/Rakefile
CHANGED
@@ -17,13 +17,10 @@ task :release do
|
|
17
17
|
sh "git release"
|
18
18
|
end
|
19
19
|
|
20
|
-
require '
|
20
|
+
require 'yard'
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
rd.title = 'em-pg-client-helper'
|
25
|
-
rd.markup = "markdown"
|
26
|
-
rd.rdoc_files.include("README.md", "lib/**/*.rb")
|
22
|
+
YARD::Rake::YardocTask.new :doc do |yardoc|
|
23
|
+
yardoc.files = %w{lib/**/*.rb - README.md}
|
27
24
|
end
|
28
25
|
|
29
26
|
desc "Run guard"
|
data/em-hyperdex-client.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
|
18
18
|
s.add_runtime_dependency "eventmachine", "~> 1.0"
|
19
19
|
s.add_runtime_dependency "git-version-bump", "~> 0.10"
|
20
|
-
s.add_runtime_dependency "hyperdex", '>= 1.4.
|
20
|
+
s.add_runtime_dependency "hyperdex", '>= 1.4.5.pre', '< 2'
|
21
21
|
|
22
22
|
s.add_development_dependency 'bundler'
|
23
23
|
s.add_development_dependency 'eventmachine'
|
@@ -30,4 +30,5 @@ Gem::Specification.new do |s|
|
|
30
30
|
s.add_development_dependency 'rake'
|
31
31
|
s.add_development_dependency 'rdoc'
|
32
32
|
s.add_development_dependency 'rspec'
|
33
|
+
s.add_development_dependency 'yard', '~> 0.7'
|
33
34
|
end
|
data/lib/em-hyperdex-client.rb
CHANGED
@@ -1,19 +1,51 @@
|
|
1
1
|
require 'hyperdex'
|
2
2
|
require 'eventmachine'
|
3
3
|
|
4
|
-
module EM; end
|
5
|
-
module EM::HyperDex; end
|
4
|
+
module EM; end #:nodoc:
|
5
|
+
module EM::HyperDex; end #:nodoc:
|
6
6
|
|
7
|
+
# An EventMachine-enabled client for [HyperDex](http://hyperdex.org/).
|
8
|
+
#
|
9
|
+
# This is a fairly straightforward async-friendly interface to the hyperdex
|
10
|
+
# NoSQL data store. All of the normal (synchronous) methods that you are
|
11
|
+
# used to using are available, except that they're automatically async.
|
12
|
+
# Schweeeeeet.
|
13
|
+
#
|
14
|
+
# Using it is quite simple (for an EM client, anyway...). You just create
|
15
|
+
# an instance of `EM::HyperDex::Client`, and then call (almost) any of the
|
16
|
+
# standard HyperDex methods you know and love against it, with the same
|
17
|
+
# arguments. The only difference is that you can pass a block to the
|
18
|
+
# method, indicating what you want to do once the request is complete, and
|
19
|
+
# the method returns a Deferrable which you can define callbacks and
|
20
|
+
# errbacks. The callback will be passed whatever the HyperDex data method
|
21
|
+
# would ordinarily return, in a synchronous world.
|
22
|
+
#
|
23
|
+
# Searching methods ({#search}, {#sorted_search}) work *slightly*
|
24
|
+
# differently. Instead of the entire resultset being passed back in a
|
25
|
+
# single callback, the callback is executed *after* the result set has been
|
26
|
+
# processed. Each item in the search result is accessed via
|
27
|
+
# {EnumerableDeferrable#each} on the deferrable that is returned from the
|
28
|
+
# {#search} method. See the docs for {#search} and {#sorted_search} for
|
29
|
+
# examples of what this looks like.
|
30
|
+
#
|
7
31
|
class EM::HyperDex::Client
|
8
|
-
|
32
|
+
# Create a new `EM::HyperDex::Client` for you to play with.
|
33
|
+
#
|
34
|
+
# This method does **not** take a callback block; the client is created
|
35
|
+
# synchronously. However, this isn't the problem you might otherwise
|
36
|
+
# expect it to be, because initializing a client doesn't make any network
|
37
|
+
# connections or otherwise potentially-blocking calls; rather, it simply
|
38
|
+
# initializes some data structures and then returns.
|
39
|
+
#
|
40
|
+
# @param host [String] A hostname or IP address (v4 or v6) of a coordinator
|
41
|
+
# within the cluster to make initial contact.
|
42
|
+
#
|
43
|
+
# @param port [Integer] The port of the coordinator you wish to contact.
|
44
|
+
#
|
45
|
+
def initialize(host = 'localhost', port = 1982)
|
9
46
|
@client = HyperDex::Client::Client.new(host, port)
|
10
47
|
@failed = false
|
11
48
|
@outstanding = {}
|
12
|
-
|
13
|
-
if ::EM.reactor_running?
|
14
|
-
@em_conn = ::EM.watch(@client.poll_fd, Watcher, self)
|
15
|
-
@em_conn.notify_readable = true
|
16
|
-
end
|
17
49
|
end
|
18
50
|
|
19
51
|
ASYNC_METHODS = HyperDex::Client::Client.
|
@@ -21,22 +53,22 @@ class EM::HyperDex::Client
|
|
21
53
|
map(&:to_s).
|
22
54
|
grep(/^async_/).
|
23
55
|
map { |m| m.gsub(/^async_/, '') }
|
56
|
+
private_constant :ASYNC_METHODS
|
24
57
|
|
25
58
|
ASYNC_METHODS.each do |m|
|
26
59
|
hyperdex_method = "async_#{m}"
|
27
60
|
|
28
61
|
define_method(m) do |*args, &block|
|
29
|
-
df = ::EM::DefaultDeferrable.new
|
30
|
-
|
31
62
|
if @failed
|
32
|
-
return
|
33
|
-
df.fail(RuntimeError.new("This client has failed. Please open a new one."))
|
34
|
-
end
|
63
|
+
return failed_deferrable("This client has failed. Please open a new one.")
|
35
64
|
end
|
36
65
|
|
66
|
+
df = ::EM::Completion.new
|
67
|
+
df.callback(&block) if block
|
68
|
+
|
37
69
|
begin
|
38
70
|
if ::EM.reactor_running?
|
39
|
-
@
|
71
|
+
add_outstanding(@client.__send__(hyperdex_method, *args), df)
|
40
72
|
else
|
41
73
|
df.succeed(@client.__send__(m, *args))
|
42
74
|
end
|
@@ -49,34 +81,106 @@ class EM::HyperDex::Client
|
|
49
81
|
end
|
50
82
|
|
51
83
|
ITERATOR_METHODS = %w{search sorted_search}
|
84
|
+
private_constant :ITERATOR_METHODS
|
85
|
+
|
86
|
+
# @!macro search_params
|
87
|
+
# @param spacename [#to_s] The name of the hyperdex space to search
|
88
|
+
#
|
89
|
+
# @param predicates [Hash<#to_s, HyperDex::Client::Predicate>] A
|
90
|
+
# collection of predicates to apply to the search.
|
91
|
+
#
|
92
|
+
# @return [EnumerableDeferrable] a deferrable which responds to
|
93
|
+
# {EnumerableDeferrable#each each} to return the search results.
|
94
|
+
#
|
95
|
+
#
|
96
|
+
# @!method search(spacename, predicates)
|
97
|
+
#
|
98
|
+
# Perform a search for all objects in the specified space that match the
|
99
|
+
# predicates provided.
|
100
|
+
#
|
101
|
+
# @macro search_params
|
102
|
+
#
|
103
|
+
# @example iterating through search results
|
104
|
+
# c = EM::HyperDex::Client.new
|
105
|
+
# c.search(:test, :towels => HyperDex::Client::GreaterThan.new(42)).each do |r|
|
106
|
+
# puts "This is an object with a high towel count: #{r.inspect}"
|
107
|
+
# end.callback do
|
108
|
+
# puts "Towel search complete"
|
109
|
+
# end.errback do |ex|
|
110
|
+
# puts "Towel search failed: #{ex.class}: #{ex.message}"
|
111
|
+
# end
|
112
|
+
#
|
52
113
|
|
114
|
+
# @!method sorted_search(spacename, predicates, sortby, limit, maxmin)
|
115
|
+
#
|
116
|
+
# Perform a search for all objects in the specified space that match
|
117
|
+
# the predicates provided, sorting the results and optionally returning
|
118
|
+
# only a subset of the results.
|
119
|
+
#
|
120
|
+
# @macro search_params
|
121
|
+
#
|
122
|
+
# @param sortby [#to_s] the attribute to sort the results by
|
123
|
+
#
|
124
|
+
# @param limit [Integer] the maximum number of results to return
|
125
|
+
#
|
126
|
+
# @param maxmin [String] Maximize (`"max"`) or minimize (`"min"`)
|
127
|
+
# (I shit you not, that's what the upstream docs say)
|
128
|
+
#
|
129
|
+
# @todo Document the `maxmin` parameter *properly*.
|
130
|
+
#
|
131
|
+
# @see #search
|
132
|
+
#
|
53
133
|
ITERATOR_METHODS.each do |m|
|
54
134
|
define_method(m) do |*args, &block|
|
55
135
|
if @failed
|
56
|
-
return
|
57
|
-
|
136
|
+
return failed_deferrable("This client has failed. Please open a new one.")
|
137
|
+
end
|
138
|
+
|
139
|
+
begin
|
140
|
+
iter = @client.__send__(m, *args)
|
141
|
+
rescue StandardError => ex
|
142
|
+
return EM::Completion.new.tap do |df|
|
143
|
+
df.fail(ex)
|
58
144
|
end
|
59
145
|
end
|
60
146
|
|
61
|
-
|
62
|
-
df
|
63
|
-
df.callback(&block) if block_given?
|
147
|
+
df = EnumerableDeferrable.new(iter)
|
148
|
+
df.callback(&block) if block
|
64
149
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
150
|
+
if ::EM.reactor_running?
|
151
|
+
add_outstanding(iter, df)
|
152
|
+
else
|
153
|
+
begin
|
154
|
+
until (item = iter.next).nil?
|
155
|
+
df.item_available(item)
|
156
|
+
end
|
157
|
+
df.item_available(nil)
|
158
|
+
rescue Exception => ex
|
159
|
+
df.fail(ex)
|
160
|
+
end
|
69
161
|
end
|
162
|
+
|
163
|
+
df
|
70
164
|
end
|
71
165
|
end
|
72
166
|
|
167
|
+
# Callback from the `EM.watch` system, to poke us when a response is
|
168
|
+
# ready to be processed.
|
169
|
+
#
|
170
|
+
# @api private
|
171
|
+
#
|
73
172
|
def handle_response
|
74
173
|
begin
|
75
|
-
|
174
|
+
op = @client.loop(0)
|
76
175
|
rescue HyperDex::Client::HyperDexClientException
|
77
176
|
# Something has gone wrong, and we're just going to take our bat
|
78
177
|
# and ball and go home.
|
79
178
|
@outstanding.values.each { |op| op.fail(ex) }
|
179
|
+
@outstanding = {}
|
180
|
+
|
181
|
+
# Call a dummy get_outstanding so EM stops watching the socket
|
182
|
+
get_outstanding(nil)
|
183
|
+
|
80
184
|
@failed = true
|
81
185
|
return
|
82
186
|
end
|
@@ -90,9 +194,14 @@ class EM::HyperDex::Client
|
|
90
194
|
return
|
91
195
|
end
|
92
196
|
|
197
|
+
df = get_outstanding(op)
|
198
|
+
|
93
199
|
begin
|
94
200
|
if df.respond_to?(:item_available)
|
95
201
|
df.item_available
|
202
|
+
# Put the deferrable back in the outstanding ops hash if
|
203
|
+
# there's more items to go
|
204
|
+
add_outstanding(op, df) unless df.completed?
|
96
205
|
else
|
97
206
|
df.succeed(op.wait)
|
98
207
|
end
|
@@ -101,47 +210,133 @@ class EM::HyperDex::Client
|
|
101
210
|
end
|
102
211
|
end
|
103
212
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
213
|
+
# Associate an in-progress operation with the deferrable that will be
|
214
|
+
# completed when the operation completes. Also handles telling EM to
|
215
|
+
# start watching the client's `poll_fd` if necessary.
|
216
|
+
#
|
217
|
+
def add_outstanding(op, df)
|
218
|
+
# If we don't have any operations already in progress, then
|
219
|
+
# we aren't watching the poll_fd, so we probably want to start
|
220
|
+
# doing that now
|
221
|
+
if @outstanding.empty?
|
222
|
+
@em_conn = ::EM.watch(@client.poll_fd, Watcher, self)
|
223
|
+
@em_conn.notify_readable = true
|
224
|
+
end
|
225
|
+
|
226
|
+
@outstanding[op] = df
|
227
|
+
end
|
228
|
+
|
229
|
+
# Retrieve the deferrable associated with the specified operation, if
|
230
|
+
# one exists. Also handles telling EM to stop watching the `poll_fd`,
|
231
|
+
# if there are now no more outstanding operations.
|
232
|
+
def get_outstanding(op)
|
233
|
+
@outstanding.delete(op).tap do
|
234
|
+
if @outstanding.empty?
|
235
|
+
@em_conn.detach
|
236
|
+
@em_conn = nil
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
# Return a new deferrable that has failed with a `RuntimeError` with the
|
243
|
+
# given message.
|
244
|
+
#
|
245
|
+
def failed_deferrable(msg)
|
246
|
+
::EM::Completion.new.tap do |df|
|
247
|
+
df.fail(RuntimeError.new(msg))
|
248
|
+
end
|
108
249
|
end
|
109
250
|
|
251
|
+
# Mix-in module for EM.watch.
|
252
|
+
#
|
253
|
+
# @api private
|
254
|
+
#
|
110
255
|
module Watcher
|
256
|
+
# Create the watcher.
|
257
|
+
#
|
258
|
+
# @api private
|
111
259
|
def initialize(em_client)
|
112
260
|
@em_client = em_client
|
113
261
|
end
|
114
262
|
|
263
|
+
# Handle the fact that more data is available.
|
264
|
+
#
|
265
|
+
# @api private
|
115
266
|
def notify_readable
|
116
267
|
@em_client.handle_response
|
117
268
|
end
|
118
269
|
end
|
119
270
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
271
|
+
# A deferrable that can be enumerated.
|
272
|
+
#
|
273
|
+
# This is a really *freaky* kind of a deferrable. It accepts the usual
|
274
|
+
# `callback` and `errback` blocks, but it *also* has a special {#each}
|
275
|
+
# callback, which will cause the block provided for each item in the
|
276
|
+
# search result set. Once the result set has been enumerated, the
|
277
|
+
# `callback` will be called.
|
278
|
+
#
|
279
|
+
# @todo Define a set of other Enumerable methods that can be usefully
|
280
|
+
# used asynchronously (if there are any), like perhaps `#map`,
|
281
|
+
# `#inject`, etc.
|
282
|
+
#
|
283
|
+
class EnumerableDeferrable < EM::Completion
|
284
|
+
# Create a new EnumerableDeferrable
|
285
|
+
#
|
286
|
+
# @param iter [HyperDex::Client::Iterator] What to call `#next` on
|
287
|
+
# to get the next item.
|
288
|
+
#
|
289
|
+
# @api private
|
290
|
+
#
|
124
291
|
def initialize(iter)
|
125
292
|
@iter = iter
|
293
|
+
@items = []
|
294
|
+
super()
|
126
295
|
end
|
127
296
|
|
297
|
+
# Define a block to call for each item in the result set.
|
298
|
+
#
|
299
|
+
# @yield the next available item in the result set
|
300
|
+
#
|
301
|
+
# @yieldparam item [Hash<Symbol, Object>] the hyperdex value object
|
302
|
+
#
|
128
303
|
def each(&blk)
|
129
|
-
return self unless block_given?
|
304
|
+
return Enumeration.new(self) unless block_given?
|
130
305
|
|
131
|
-
|
306
|
+
if state == :succeeded
|
307
|
+
@items.each { |i| blk.call(i) }
|
308
|
+
else
|
309
|
+
@each_block = blk
|
310
|
+
end
|
311
|
+
|
312
|
+
self
|
132
313
|
end
|
133
314
|
|
134
|
-
|
135
|
-
|
315
|
+
# Trigger whenever we know there's an item available.
|
316
|
+
#
|
317
|
+
# @api private
|
318
|
+
#
|
319
|
+
def item_available(val = NoValue)
|
320
|
+
if val == NoValue
|
321
|
+
val = @iter.next
|
322
|
+
end
|
323
|
+
|
136
324
|
if val.nil?
|
137
|
-
succeed
|
325
|
+
self.succeed
|
138
326
|
else
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
327
|
+
if @each_block
|
328
|
+
begin
|
329
|
+
@each_block.call(val)
|
330
|
+
rescue Exception => ex
|
331
|
+
fail(ex)
|
332
|
+
end
|
333
|
+
else
|
334
|
+
@items << val
|
143
335
|
end
|
144
336
|
end
|
145
337
|
end
|
338
|
+
|
339
|
+
NoValue = Object.new
|
340
|
+
private_constant :NoValue
|
146
341
|
end
|
147
342
|
end
|
data/spec/get_spec.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
describe "#get" do
|
4
|
+
let(:client) { EM::HyperDex::Client.new }
|
5
|
+
|
6
|
+
it "works when standalone" do
|
7
|
+
expect(mock_client).
|
8
|
+
to receive(:get).
|
9
|
+
with(:foo, 42).
|
10
|
+
and_return("towels")
|
11
|
+
|
12
|
+
client.get(:foo, 42).callback do |v|
|
13
|
+
expect(v).to eq("towels")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "works inside EM" do
|
18
|
+
in_em do
|
19
|
+
expect_op(:get, [:foo, 42], "towels")
|
20
|
+
|
21
|
+
df = client.get(:foo, 42) do |v|
|
22
|
+
expect(v).to eq("towels")
|
23
|
+
EM.stop
|
24
|
+
end
|
25
|
+
|
26
|
+
expect(df).to be_a(::EM::Completion)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "works multiple times" do
|
31
|
+
in_em do
|
32
|
+
expect_op(:get, [:foo, 42], "towels")
|
33
|
+
expect_op(:get, [:bar, "baz"], "wombat")
|
34
|
+
|
35
|
+
client.get(:foo, 42) do |v|
|
36
|
+
expect(v).to eq("towels")
|
37
|
+
client.get(:bar, "baz") do |v|
|
38
|
+
expect(v).to eq("wombat")
|
39
|
+
EM.stop
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "works in parallel" do
|
46
|
+
in_em do
|
47
|
+
expect_op(:get, [:foo, 42], "towels")
|
48
|
+
expect_op(:get, [:bar, "baz"], "wombat", 0.1)
|
49
|
+
op_count = 2
|
50
|
+
|
51
|
+
client.get(:foo, 42) do |v|
|
52
|
+
expect(v).to eq("towels")
|
53
|
+
op_count -= 1
|
54
|
+
end
|
55
|
+
|
56
|
+
client.get(:bar, "baz") do |v|
|
57
|
+
expect(v).to eq("wombat")
|
58
|
+
op_count -= 1
|
59
|
+
end
|
60
|
+
|
61
|
+
EM.add_periodic_timer(0.0001) do
|
62
|
+
EM.stop if op_count == 0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "works with out-of-order responses" do
|
68
|
+
in_em do
|
69
|
+
expect_op(:get, [:foo, 42], "towels", 0.1)
|
70
|
+
expect_op(:get, [:bar, "baz"], "wombat")
|
71
|
+
op_count = 2
|
72
|
+
|
73
|
+
client.get(:foo, 42) do |v|
|
74
|
+
expect(v).to eq("towels")
|
75
|
+
op_count -= 1
|
76
|
+
end
|
77
|
+
|
78
|
+
client.get(:bar, "baz") do |v|
|
79
|
+
expect(v).to eq("wombat")
|
80
|
+
op_count -= 1
|
81
|
+
end
|
82
|
+
|
83
|
+
EM.add_periodic_timer(0.0001) do
|
84
|
+
EM.stop if op_count == 0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/spec/search_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
describe "#search" do
|
4
|
+
let(:client) { EM::HyperDex::Client.new }
|
5
|
+
|
6
|
+
it "works when standalone" do
|
7
|
+
expect(mock_client).
|
8
|
+
to receive(:search).
|
9
|
+
with(:foo, 42).
|
10
|
+
and_return(mock_iter = double(HyperDex::Client::Iterator))
|
11
|
+
|
12
|
+
resultset = %w{alpha beta gamma delta}
|
13
|
+
|
14
|
+
expect(mock_iter).
|
15
|
+
to receive(:next).
|
16
|
+
with(no_args).
|
17
|
+
and_return(*(resultset + [nil]))
|
18
|
+
|
19
|
+
client.search(:foo, 42).each do |v|
|
20
|
+
expect(v).to eq(resultset.shift)
|
21
|
+
end.errback do
|
22
|
+
# Derpy way of saying "this should not happen"
|
23
|
+
expect(false).to be(true)
|
24
|
+
end
|
25
|
+
|
26
|
+
expect(resultset).to be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
it "works inside EM" do
|
30
|
+
resultset = %w{alpha beta gamma delta}
|
31
|
+
in_em do
|
32
|
+
expect_iter(:search, [:foo, 42], resultset)
|
33
|
+
|
34
|
+
df = client.search(:foo, 42).each do |v|
|
35
|
+
expect(v).to eq(resultset.shift)
|
36
|
+
end.callback do
|
37
|
+
EM.stop
|
38
|
+
end.errback do
|
39
|
+
# This should not happen
|
40
|
+
expect(false).to be(true)
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(df).to be_a(EM::HyperDex::Client::EnumerableDeferrable)
|
44
|
+
end
|
45
|
+
|
46
|
+
expect(resultset).to be_empty
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spork'
|
2
|
+
|
3
|
+
Spork.prefork do
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.setup(:default, :test)
|
6
|
+
require 'rspec/core'
|
7
|
+
require 'rspec/mocks'
|
8
|
+
|
9
|
+
require 'pry'
|
10
|
+
|
11
|
+
module EventMachineTestHelper
|
12
|
+
def in_em(timeout = 1)
|
13
|
+
Timeout::timeout(timeout) do
|
14
|
+
::EM.run do
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
rescue Timeout::Error
|
19
|
+
EM.stop
|
20
|
+
raise RuntimeError,
|
21
|
+
"EM didn't finish before the timeout"
|
22
|
+
end
|
23
|
+
|
24
|
+
def expect_op(op, args, rv, response_delay = 0.0001)
|
25
|
+
expect(mock_client).
|
26
|
+
to receive("async_#{op}".to_sym).
|
27
|
+
with(*args).
|
28
|
+
and_return(mock_op = double(HyperDex::Client::Deferred))
|
29
|
+
|
30
|
+
expect(mock_op).
|
31
|
+
to receive(:wait).
|
32
|
+
with(no_args).
|
33
|
+
and_return(rv)
|
34
|
+
|
35
|
+
::EM.add_timer(response_delay) do
|
36
|
+
expect(mock_client).
|
37
|
+
to receive(:loop).
|
38
|
+
with(0).
|
39
|
+
and_return(mock_op)
|
40
|
+
|
41
|
+
client.__send__(:handle_response)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def expect_iter(op, args, results, response_delay = 0.0001)
|
46
|
+
expect(mock_client).
|
47
|
+
to receive(op).
|
48
|
+
with(*args).
|
49
|
+
and_return(mock_iter = double(HyperDex::Client::Iterator))
|
50
|
+
|
51
|
+
expect(mock_iter).
|
52
|
+
to receive(:next).
|
53
|
+
with(no_args).
|
54
|
+
and_return(*(results + [nil]))
|
55
|
+
|
56
|
+
(results.length + 1).times do |i|
|
57
|
+
::EM.add_timer(response_delay * (i+1)) do
|
58
|
+
expect(mock_client).
|
59
|
+
to receive(:loop).
|
60
|
+
with(0).
|
61
|
+
and_return(mock_iter)
|
62
|
+
|
63
|
+
client.__send__(:handle_response)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def mock_client(host = 'localhost', port = 1982)
|
69
|
+
@mock_client ||= begin
|
70
|
+
expect(HyperDex::Client::Client).
|
71
|
+
to receive(:new).
|
72
|
+
with(host, port).
|
73
|
+
and_return(mock = double(HyperDex::Client::Client))
|
74
|
+
|
75
|
+
allow(mock).
|
76
|
+
to receive(:poll_fd).
|
77
|
+
with(no_args).
|
78
|
+
and_return(0)
|
79
|
+
mock
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
RSpec.configure do |config|
|
85
|
+
config.fail_fast = true
|
86
|
+
# config.full_backtrace = true
|
87
|
+
|
88
|
+
config.include EventMachineTestHelper
|
89
|
+
|
90
|
+
config.expect_with :rspec do |c|
|
91
|
+
c.syntax = :expect
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Spork.each_run do
|
97
|
+
require 'em-hyperdex-client'
|
98
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-hyperdex-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-11-
|
12
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -50,7 +50,10 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 1.4.
|
53
|
+
version: 1.4.5.pre
|
54
|
+
- - <
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '2'
|
54
57
|
type: :runtime
|
55
58
|
prerelease: false
|
56
59
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,7 +61,10 @@ dependencies:
|
|
58
61
|
requirements:
|
59
62
|
- - ! '>='
|
60
63
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.4.
|
64
|
+
version: 1.4.5.pre
|
65
|
+
- - <
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2'
|
62
68
|
- !ruby/object:Gem::Dependency
|
63
69
|
name: bundler
|
64
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -219,6 +225,22 @@ dependencies:
|
|
219
225
|
- - ! '>='
|
220
226
|
- !ruby/object:Gem::Version
|
221
227
|
version: '0'
|
228
|
+
- !ruby/object:Gem::Dependency
|
229
|
+
name: yard
|
230
|
+
requirement: !ruby/object:Gem::Requirement
|
231
|
+
none: false
|
232
|
+
requirements:
|
233
|
+
- - ~>
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
version: '0.7'
|
236
|
+
type: :development
|
237
|
+
prerelease: false
|
238
|
+
version_requirements: !ruby/object:Gem::Requirement
|
239
|
+
none: false
|
240
|
+
requirements:
|
241
|
+
- - ~>
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0.7'
|
222
244
|
description:
|
223
245
|
email:
|
224
246
|
executables: []
|
@@ -227,6 +249,7 @@ extra_rdoc_files:
|
|
227
249
|
- README.md
|
228
250
|
files:
|
229
251
|
- .gitignore
|
252
|
+
- .yardopts
|
230
253
|
- Gemfile
|
231
254
|
- Guardfile
|
232
255
|
- LICENCE
|
@@ -234,6 +257,9 @@ files:
|
|
234
257
|
- Rakefile
|
235
258
|
- em-hyperdex-client.gemspec
|
236
259
|
- lib/em-hyperdex-client.rb
|
260
|
+
- spec/get_spec.rb
|
261
|
+
- spec/search_spec.rb
|
262
|
+
- spec/spec_helper.rb
|
237
263
|
homepage: http://github.com/mpalmer/em-hyperdex-client
|
238
264
|
licenses: []
|
239
265
|
post_install_message:
|
@@ -248,7 +274,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
248
274
|
version: '0'
|
249
275
|
segments:
|
250
276
|
- 0
|
251
|
-
hash: -
|
277
|
+
hash: -1612000962635766686
|
252
278
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
253
279
|
none: false
|
254
280
|
requirements:
|
@@ -257,7 +283,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
257
283
|
version: '0'
|
258
284
|
segments:
|
259
285
|
- 0
|
260
|
-
hash: -
|
286
|
+
hash: -1612000962635766686
|
261
287
|
requirements: []
|
262
288
|
rubyforge_project:
|
263
289
|
rubygems_version: 1.8.23
|
@@ -265,3 +291,4 @@ signing_key:
|
|
265
291
|
specification_version: 3
|
266
292
|
summary: EventMachine bindings for HyperDex
|
267
293
|
test_files: []
|
294
|
+
has_rdoc:
|