em-hyperdex-client 0.3.1 → 0.3.2
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.
- 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:
|