zk-eventmachine 0.1.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.
- data/.dev_extras/README +5 -0
- data/.dev_extras/rspec +1 -0
- data/.dev_extras/rvmrc +1 -0
- data/.dev_extras/slyphon-project.vimrc +18 -0
- data/.gitignore +8 -0
- data/Gemfile +9 -0
- data/LICENSE +19 -0
- data/README.markdown +8 -0
- data/Rakefile +2 -0
- data/lib/z_k/z_k_event_machine/callback.rb +256 -0
- data/lib/z_k/z_k_event_machine/client.rb +122 -0
- data/lib/z_k/z_k_event_machine/deferred.rb +39 -0
- data/lib/z_k/z_k_event_machine/event_handler_e_m.rb +34 -0
- data/lib/z_k/z_k_event_machine/iterator.rb +229 -0
- data/lib/z_k/z_k_event_machine/unixisms.rb +124 -0
- data/lib/z_k/z_k_event_machine/version.rb +5 -0
- data/lib/z_k/z_k_event_machine.rb +24 -0
- data/lib/zk-eventmachine.rb +2 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/extensions.rb +44 -0
- data/spec/support/logging.rb +9 -0
- data/spec/support/logging_progress_bar_formatter.rb +26 -0
- data/spec/z_k/z_k_event_machine/callback_spec.rb +135 -0
- data/spec/z_k/z_k_event_machine/client_spec.rb +693 -0
- data/spec/z_k/z_k_event_machine/event_handler_e_m_spec.rb +104 -0
- data/spec/z_k/z_k_event_machine/unixisms_spec.rb +122 -0
- data/zk-eventmachine.gemspec +28 -0
- metadata +212 -0
data/.dev_extras/README
ADDED
data/.dev_extras/rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.dev_extras/rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2
|
@@ -0,0 +1,18 @@
|
|
1
|
+
iabbr _ZEM ZK::ZKEventMachine
|
2
|
+
iabbr _ZCB ZK::ZKEventMachine::Callback
|
3
|
+
iabbr _ZCLI ZK::ZKEventMachine::Client
|
4
|
+
|
5
|
+
iabbr _wia i.with_indifferent_access
|
6
|
+
iabbr _lam lambda {}hi
|
7
|
+
|
8
|
+
iabbr _seq i.should ==
|
9
|
+
iabbr _srex i.should raise_exception()i
|
10
|
+
iabbr _srexarg i.should raise_exception(ArgumentError)i
|
11
|
+
iabbr _snrex i.should_not raise_exception
|
12
|
+
iabbr _sbnil i.should be_nil
|
13
|
+
|
14
|
+
iabbr _desc describe do
|
15
|
+
iabbr _bef before do
|
16
|
+
|
17
|
+
|
18
|
+
" vim:ft=vim
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (C) 2011 by Hewlett Packard Development Company, L.P.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
module ZK
|
2
|
+
module ZKEventMachine
|
3
|
+
# some improvements (one hopes) around the zookeeper gem's somewhat (ahem)
|
4
|
+
# minimal Callback class
|
5
|
+
#
|
6
|
+
module Callback
|
7
|
+
|
8
|
+
# Used by ZooKeeper to return an asynchronous result.
|
9
|
+
#
|
10
|
+
# If callbacks or errbacks are set on the instance, they will be called
|
11
|
+
# with just the data returned from the call (much like their synchronous
|
12
|
+
# versions).
|
13
|
+
#
|
14
|
+
# If a block was given to #new or #on_result, then that block is called
|
15
|
+
# with a ZK::Exceptions::KeeperException instance or nil, then the rest
|
16
|
+
# of the arguments defined for that callback type
|
17
|
+
#
|
18
|
+
# the node-style and deferred-style results are *NOT* exclusive, so if
|
19
|
+
# you use both _you will be called with results in both formats_.
|
20
|
+
#
|
21
|
+
class Base
|
22
|
+
include Deferred
|
23
|
+
include ZK::Logging
|
24
|
+
|
25
|
+
# set the result keys that should be used by node_style_result and to
|
26
|
+
# call the deferred_style_result blocks
|
27
|
+
#
|
28
|
+
def self.async_result_keys(*syms)
|
29
|
+
if syms.empty?
|
30
|
+
@async_result_keys || []
|
31
|
+
else
|
32
|
+
@async_result_keys = syms.map { |n| n.to_sym }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(prok=nil, &block)
|
37
|
+
on_result(prok, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
# register a block that should be called (node.js style) with the
|
41
|
+
# results
|
42
|
+
#
|
43
|
+
# @note replaces the block given to #new
|
44
|
+
#
|
45
|
+
def on_result(prok=nil, &block)
|
46
|
+
@block = (prok || block)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Checks the return code from the async call. If the return code was not ZOK,
|
50
|
+
# then fire the errbacks and do the node-style error call
|
51
|
+
# otherwise, does nothing
|
52
|
+
def check_async_rc(hash)
|
53
|
+
call(hash) unless success?(hash)
|
54
|
+
end
|
55
|
+
|
56
|
+
# ZK will call this instance with a hash of data, which is the result
|
57
|
+
# of the asynchronous call. Depending on the style of callback in use,
|
58
|
+
# we take the appropriate actions
|
59
|
+
#
|
60
|
+
# delegates to #deferred_style_result and #node_style_result
|
61
|
+
def call(hash)
|
62
|
+
# logger.debug { "#{self.class.name}#call hash: #{hash.inspect}" }
|
63
|
+
EM.schedule do
|
64
|
+
deferred_style_result(hash)
|
65
|
+
node_style_result(hash)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# returns true if the request was successful (if return_code was Zookeeper::ZOK)
|
70
|
+
#
|
71
|
+
# @param [Hash] hash the result of the async call
|
72
|
+
#
|
73
|
+
# @returns [true, false] for success, failure
|
74
|
+
def success?(hash)
|
75
|
+
hash[:rc] == Zookeeper::ZOK
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns an instance of a sublcass ZK::Exceptions::KeeperException
|
79
|
+
# based on the asynchronous return_code.
|
80
|
+
#
|
81
|
+
# facilitates using case statements for error handling
|
82
|
+
#
|
83
|
+
# @param [Hash] hash the result of the async call
|
84
|
+
#
|
85
|
+
# @raise [RuntimeError] if the return_code is not known by ZK (this should never
|
86
|
+
# happen and if it does, you should report a bug)
|
87
|
+
#
|
88
|
+
# @return [ZK::Exceptions::KeeperException, nil] subclass based on
|
89
|
+
# return_code if there was an error, nil otherwise
|
90
|
+
#
|
91
|
+
def exception_for(hash)
|
92
|
+
return nil if success?(hash)
|
93
|
+
return_code = hash.fetch(:rc)
|
94
|
+
ZK::Exceptions::KeeperException.by_code(return_code).new
|
95
|
+
end
|
96
|
+
|
97
|
+
# @abstract should call set_deferred_status with the appropriate args
|
98
|
+
# for the result and type of call
|
99
|
+
def deferred_style_result(hash)
|
100
|
+
# ensure this calls the callback on the reactor
|
101
|
+
|
102
|
+
if success?(hash)
|
103
|
+
vals = hash.values_at(*async_result_keys)
|
104
|
+
# logger.debug { "#{self.class.name}#deferred_style_result async_result_keys: #{async_result_keys.inspect}, vals: #{vals.inspect}" }
|
105
|
+
self.succeed(*hash.values_at(*async_result_keys))
|
106
|
+
else
|
107
|
+
self.fail(exception_for(hash))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# call the user block with the correct Exception class as the first arg
|
112
|
+
# (or nil if no error) and then the appropriate args for the type of
|
113
|
+
# asynchronous call
|
114
|
+
def node_style_result(hash)
|
115
|
+
return unless @block
|
116
|
+
vals = hash.values_at(*async_result_keys)
|
117
|
+
# logger.debug { "#{self.class.name}#node_style_result async_result_keys: #{async_result_keys.inspect}, vals: #{vals.inspect}" }
|
118
|
+
if exc = exception_for(hash)
|
119
|
+
@block.call(exc)
|
120
|
+
else
|
121
|
+
@block.call(nil, *vals)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
protected
|
126
|
+
def async_result_keys
|
127
|
+
self.class.async_result_keys
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# used with Client#get call
|
132
|
+
class DataCallback < Base
|
133
|
+
async_result_keys :data, :stat
|
134
|
+
end
|
135
|
+
|
136
|
+
# used with Client#children call
|
137
|
+
class ChildrenCallback < Base
|
138
|
+
async_result_keys :strings, :stat
|
139
|
+
end
|
140
|
+
|
141
|
+
# used with Client#create
|
142
|
+
class StringCallback < Base
|
143
|
+
async_result_keys :string
|
144
|
+
end
|
145
|
+
|
146
|
+
# used with Client#stat and Client#exists?
|
147
|
+
class StatCallback < Base
|
148
|
+
async_result_keys :stat
|
149
|
+
|
150
|
+
# stat has a different concept of 'success', stat on a node that doesn't
|
151
|
+
# exist is not an exception, it's a certain kind of stat (like a null stat).
|
152
|
+
def success?(hash)
|
153
|
+
rc = hash[:rc]
|
154
|
+
(rc == Zookeeper::ZOK) || (rc == Zookeeper::ZNONODE)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# supports the syntactic sugar exists? call
|
159
|
+
class ExistsCallback < StatCallback
|
160
|
+
async_result_keys :stat
|
161
|
+
|
162
|
+
# @abstract should call set_deferred_status with the appropriate args
|
163
|
+
# for the result and type of call
|
164
|
+
def deferred_style_result(hash)
|
165
|
+
# ensure this calls the callback on the reactor
|
166
|
+
|
167
|
+
if success?(hash)
|
168
|
+
succeed(hash[:stat].exists?)
|
169
|
+
else
|
170
|
+
fail(exception_for(hash))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# call the user block with the correct Exception class as the first arg
|
175
|
+
# (or nil if no error) and then the appropriate args for the type of
|
176
|
+
# asynchronous call
|
177
|
+
def node_style_result(hash)
|
178
|
+
return unless @block
|
179
|
+
@block.call(exception_for(hash), hash[:stat].exists?)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# set operation returns a stat object, but in this case a NONODE is an
|
184
|
+
# error (unlike with StatCallback)
|
185
|
+
class SetCallback < Base
|
186
|
+
async_result_keys :stat
|
187
|
+
end
|
188
|
+
|
189
|
+
# used with Client#delete and Client#set_acl
|
190
|
+
class VoidCallback < Base
|
191
|
+
end
|
192
|
+
|
193
|
+
# used with Client#get_acl
|
194
|
+
class ACLCallback < Base
|
195
|
+
async_result_keys :acl, :stat
|
196
|
+
end
|
197
|
+
|
198
|
+
class << self
|
199
|
+
def new_data_cb(njs_block)
|
200
|
+
DataCallback.new(njs_block).tap do |cb| # create the callback with the user-provided block
|
201
|
+
cb.check_async_rc(yield(cb)) # yield the callback to the caller, check the return result
|
202
|
+
# of the async operation (not the async result)
|
203
|
+
end # return the callback
|
204
|
+
end
|
205
|
+
alias :new_get_cb :new_data_cb # create alias so that this matches the client API name
|
206
|
+
|
207
|
+
def new_string_cb(njs_block)
|
208
|
+
StringCallback.new(njs_block).tap do |cb|
|
209
|
+
cb.check_async_rc(yield(cb))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
alias :new_create_cb :new_string_cb
|
213
|
+
|
214
|
+
def new_stat_cb(njs_block)
|
215
|
+
StatCallback.new(njs_block).tap do |cb|
|
216
|
+
cb.check_async_rc(yield(cb))
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def new_exists_cb(njs_block)
|
221
|
+
ExistsCallback.new(njs_block).tap do |cb|
|
222
|
+
cb.check_async_rc(yield(cb))
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def new_set_cb(njs_block)
|
227
|
+
SetCallback.new(njs_block).tap do |cb|
|
228
|
+
cb.check_async_rc(yield(cb))
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def new_void_cb(njs_block)
|
233
|
+
VoidCallback.new(njs_block).tap do |cb|
|
234
|
+
cb.check_async_rc(yield(cb))
|
235
|
+
end
|
236
|
+
end
|
237
|
+
alias :new_delete_cb :new_void_cb
|
238
|
+
alias :new_set_acl_cb :new_void_cb
|
239
|
+
|
240
|
+
def new_children_cb(njs_block)
|
241
|
+
ChildrenCallback.new(njs_block).tap do |cb|
|
242
|
+
cb.check_async_rc(yield(cb))
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def new_acl_cb(njs_block)
|
247
|
+
ACLCallback.new(njs_block).tap do |cb|
|
248
|
+
cb.check_async_rc(yield(cb))
|
249
|
+
end
|
250
|
+
end
|
251
|
+
alias :new_get_acl_cb :new_acl_cb
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module ZK
|
2
|
+
module ZKEventMachine
|
3
|
+
class Client < ZK::Client::Base
|
4
|
+
include ZK::Logging
|
5
|
+
include Unixisms
|
6
|
+
|
7
|
+
DEFAULT_TIMEOUT = 10
|
8
|
+
|
9
|
+
attr_reader :client
|
10
|
+
|
11
|
+
# Takes same options as ZK::Client::Base
|
12
|
+
def initialize(host, opts={})
|
13
|
+
@host = host
|
14
|
+
@close_deferred = Deferred::Default.new
|
15
|
+
@event_handler = EventHandlerEM.new(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_close(&blk)
|
19
|
+
@close_deferred.callback(&blk) if blk
|
20
|
+
@close_deferred
|
21
|
+
end
|
22
|
+
|
23
|
+
# open a ZK connection, attach it to the reactor.
|
24
|
+
# returns an EM::Deferrable that will be called when the connection is
|
25
|
+
# ready for use
|
26
|
+
def connect(&blk)
|
27
|
+
# XXX: maybe move this into initialize, need to figure out how to schedule it properly
|
28
|
+
@cnx ||= ZookeeperEM::Client.new(@host, DEFAULT_TIMEOUT, event_handler.get_default_watcher_block).tap do |c|
|
29
|
+
c.on_attached { logger.debug { "connection is attached" } }
|
30
|
+
end
|
31
|
+
|
32
|
+
@cnx.on_attached(&blk)
|
33
|
+
end
|
34
|
+
|
35
|
+
def reopen(*a)
|
36
|
+
raise NotImplementedError, "reoopen is not implemented for the eventmachine version of the client"
|
37
|
+
end
|
38
|
+
|
39
|
+
def close!(&blk)
|
40
|
+
on_close(&blk)
|
41
|
+
|
42
|
+
if @cnx
|
43
|
+
logger.debug { "#{self.class.name}: calling @cnx.close" }
|
44
|
+
@cnx.close
|
45
|
+
@cnx = nil
|
46
|
+
|
47
|
+
logger.debug { "#{self.class.name}: @cnx.close callback fired, clearing event_handler" }
|
48
|
+
event_handler.clear!
|
49
|
+
logger.debug { "firing on_close handler" }
|
50
|
+
on_close.succeed
|
51
|
+
else
|
52
|
+
on_close.succeed
|
53
|
+
end
|
54
|
+
|
55
|
+
on_close
|
56
|
+
end
|
57
|
+
alias :close :close!
|
58
|
+
|
59
|
+
# get data at path, optionally enabling a watch on the node
|
60
|
+
#
|
61
|
+
# @returns [Callback] returns a Callback which is an EM::Deferred (so you
|
62
|
+
# can assign callbacks/errbacks) see Callback::Base for discussion
|
63
|
+
#
|
64
|
+
def get(path, opts={}, &block)
|
65
|
+
Callback.new_get_cb(block) do |cb|
|
66
|
+
super(path, opts.merge(:callback => cb))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def create(path, data='', opts={}, &block)
|
71
|
+
Callback.new_create_cb(block) do |cb|
|
72
|
+
super(path, data, opts.merge(:callback => cb))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def set(path, data, opts={}, &block)
|
77
|
+
Callback.new_set_cb(block) do |cb|
|
78
|
+
super(path, data, opts.merge(:callback => cb))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def stat(path, opts={}, &block)
|
83
|
+
cb_style = opts.delete(:cb_style) { |_| 'stat' }
|
84
|
+
|
85
|
+
meth = :"new_#{cb_style}_cb"
|
86
|
+
|
87
|
+
Callback.__send__(meth, block) do |cb|
|
88
|
+
super(path, opts.merge(:callback => cb))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def exists?(path, opts={}, &block)
|
93
|
+
stat(path, opts.merge(:cb_style => 'exists'), &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
def delete(path, opts={}, &block)
|
97
|
+
Callback.new_delete_cb(block) do |cb|
|
98
|
+
super(path, opts.merge(:callback => cb))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def children(path, opts={}, &block)
|
103
|
+
Callback.new_children_cb(block) do |cb|
|
104
|
+
super(path, opts.merge(:callback => cb))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_acl(path, opts={}, &block)
|
109
|
+
Callback.new_get_acl_cb(block) do |cb|
|
110
|
+
super(path, opts.merge(:callback => cb))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_acl(path, acls, opts={}, &block)
|
115
|
+
Callback.new_set_acl_cb(block) do |cb|
|
116
|
+
super(path, acls, opts.merge(:callback => cb))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ZK
|
2
|
+
module ZKEventMachine
|
3
|
+
module Deferred
|
4
|
+
include EM::Deferrable
|
5
|
+
|
6
|
+
# slight modification to EM::Deferrable,
|
7
|
+
#
|
8
|
+
# @returns [self] to allow for chaining
|
9
|
+
#
|
10
|
+
def callback(&block)
|
11
|
+
super(&block)
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
# @see #callback
|
16
|
+
def errback(&block)
|
17
|
+
super(&block)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# adds the block to both the callback and errback chains
|
22
|
+
def ensure_that(&block)
|
23
|
+
callback(&block)
|
24
|
+
errback(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def chain_to(other_dfr, opts={})
|
28
|
+
other_dfr.callback { |*a| self.succeed(*a) }
|
29
|
+
other_dfr.errback { |*a| self.fail(*a) } unless opts[:ignore_errors]
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
class Default
|
34
|
+
include ZK::ZKEventMachine::Deferred
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ZK
|
2
|
+
module ZKEventMachine
|
3
|
+
# a small wrapper around the EventHandler instance, allowing us to
|
4
|
+
# deliver the event on the reactor thread, as opposed to calling it directly
|
5
|
+
#
|
6
|
+
class EventHandlerEM < ZK::EventHandler
|
7
|
+
include ZK::Logging
|
8
|
+
|
9
|
+
def register(path, &block)
|
10
|
+
# use the supplied block, but ensure that it gets called on the reactor
|
11
|
+
# thread
|
12
|
+
new_blk = lambda do |*a|
|
13
|
+
EM.schedule { block.call(*a) }
|
14
|
+
end
|
15
|
+
|
16
|
+
super(path, &new_blk)
|
17
|
+
end
|
18
|
+
alias :subscribe :register
|
19
|
+
|
20
|
+
def process(event)
|
21
|
+
EM.schedule { super(event) }
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
# we're running on the Reactor, don't need to synchronize (hah, hah, we'll see...)
|
26
|
+
#
|
27
|
+
def synchronize
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|