slyphon-zookeeper 0.3.0-java → 0.8.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +34 -0
- data/Rakefile +25 -5
- data/ext/c_zookeeper.rb +214 -0
- data/ext/dbg.h +18 -2
- data/ext/depend +1 -2
- data/ext/zookeeper_base.rb +73 -99
- data/ext/zookeeper_c.c +52 -41
- data/ext/zookeeper_lib.c +90 -78
- data/ext/zookeeper_lib.h +0 -1
- data/java/zookeeper_base.rb +72 -154
- data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
- data/lib/zookeeper/common.rb +73 -9
- data/lib/zookeeper/constants.rb +28 -0
- data/lib/zookeeper/em_client.rb +13 -138
- data/lib/zookeeper/exceptions.rb +4 -1
- data/lib/zookeeper.rb +51 -15
- data/slyphon-zookeeper.gemspec +1 -1
- data/spec/c_zookeeper_spec.rb +50 -0
- data/spec/chrooted_connection_spec.rb +121 -0
- data/spec/em_spec.rb +0 -61
- data/spec/shared/all_success_return_values.rb +10 -0
- data/spec/shared/connection_examples.rb +1007 -0
- data/spec/spec_helper.rb +42 -19
- data/spec/zookeeper_spec.rb +8 -1033
- metadata +16 -6
data/lib/zookeeper/em_client.rb
CHANGED
@@ -13,6 +13,7 @@ module ZookeeperEM
|
|
13
13
|
@em_connection = nil
|
14
14
|
logger.debug { "ZookeeperEM::Client obj_id %x: init" % [object_id] }
|
15
15
|
super(*a, &b)
|
16
|
+
on_attached.succeed
|
16
17
|
end
|
17
18
|
|
18
19
|
# EM::DefaultDeferrable that will be called back when our em_connection has been detached
|
@@ -29,152 +30,26 @@ module ZookeeperEM
|
|
29
30
|
@on_attached
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
|
-
# has been shut down
|
34
|
-
#
|
35
|
-
# if a block is given, it will be registered as a callback when the
|
36
|
-
# connection has been closed
|
37
|
-
#
|
38
|
-
def close(&block)
|
39
|
-
on_close(&block)
|
40
|
-
|
41
|
-
logger.debug { "#{self.class.name}: close called, closed? #{closed?} running? #{running?}" }
|
42
|
-
|
43
|
-
if @_running
|
44
|
-
@start_stop_mutex.synchronize do
|
45
|
-
@_running = false
|
46
|
-
end
|
47
|
-
|
48
|
-
if @em_connection
|
49
|
-
EM.next_tick do
|
50
|
-
@em_connection.detach do
|
51
|
-
logger.debug { "#{self.class.name}: connection unbound, continuing with shutdown" }
|
52
|
-
finish_closing
|
53
|
-
end
|
54
|
-
end
|
55
|
-
else
|
56
|
-
logger.debug { "#{self.class.name}: em_connection was never set up, finish closing" }
|
57
|
-
finish_closing
|
58
|
-
end
|
59
|
-
else
|
60
|
-
logger.debug { "#{self.class.name}: we are not running, so returning on_close deferred" }
|
61
|
-
end
|
62
|
-
|
63
|
-
on_close
|
64
|
-
end
|
65
|
-
|
66
|
-
# make this public as the ZKConnection object needs to call it
|
67
|
-
public :dispatch_next_callback
|
68
|
-
|
69
|
-
protected
|
70
|
-
# instead of setting up a dispatch thread here, we instead attach
|
71
|
-
# the #selectable_io to the event loop
|
72
|
-
def setup_dispatch_thread!
|
33
|
+
def dispatch_next_callback(hash)
|
73
34
|
EM.schedule do
|
74
35
|
if running? and not closed?
|
75
|
-
|
76
|
-
|
77
|
-
@em_connection = EM.watch(selectable_io, ZKConnection, self) { |cnx| cnx.notify_readable = true }
|
78
|
-
rescue Exception => e
|
79
|
-
$stderr.puts "caught exception from EM.watch(): #{e.inspect}"
|
80
|
-
end
|
36
|
+
logger.debug { "#{self.class}##{__method__} dispatch_next_callback: #{hash.inspect}: reactor_thread? #{EM.reactor_thread?}, running? #{running?}, closed? #{closed?}" }
|
37
|
+
super(hash)
|
81
38
|
end
|
82
39
|
end
|
83
40
|
end
|
84
41
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
logger.debug { "closing handle" }
|
89
|
-
close_handle
|
90
|
-
end
|
91
|
-
|
92
|
-
unless selectable_io.closed?
|
93
|
-
logger.debug { "calling close on selectable_io: #{selectable_io.inspect}" }
|
94
|
-
selectable_io.close
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
42
|
+
def close(&block)
|
43
|
+
on_close(&block)
|
44
|
+
super()
|
98
45
|
on_close.succeed
|
99
46
|
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# this class is handed to EventMachine.watch to handle event dispatching
|
103
|
-
# when the queue has a message waiting. There's a pipe shared between
|
104
|
-
# the event thread managed by the queue implementation in C. It's made
|
105
|
-
# available to the ruby-space through the Zookeeper#selectable_io method.
|
106
|
-
# When the pipe is readable, that means there's an event waiting. We call
|
107
|
-
# dispatch_next_event and read a single byte off the pipe.
|
108
|
-
#
|
109
|
-
class ZKConnection < EM::Connection
|
110
|
-
|
111
|
-
def initialize(zk_client)
|
112
|
-
@zk_client = zk_client
|
113
|
-
end
|
114
47
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
@on_unbind = EM::DefaultDeferrable.new.tap do |d|
|
120
|
-
d.callback do
|
121
|
-
logger.debug { "on_unbind deferred fired" }
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
# probably because of the way post_init works, unless we fire this
|
126
|
-
# callback in next_tick @em_connection in the client may not be set
|
127
|
-
# (which on_attached callbacks may be relying on)
|
128
|
-
EM.next_tick do
|
129
|
-
logger.debug { "firing on_attached callback" }
|
130
|
-
@zk_client.on_attached.succeed
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# EM::DefaultDeferrable that will be called back when our em_connection has been detached
|
135
|
-
# and we've completed the close operation
|
136
|
-
def on_unbind(&block)
|
137
|
-
@on_unbind.callback(&block) if block
|
138
|
-
@on_unbind
|
139
|
-
end
|
140
|
-
|
141
|
-
def attached?
|
142
|
-
@attached
|
143
|
-
end
|
144
|
-
|
145
|
-
def unbind
|
146
|
-
on_unbind.succeed
|
147
|
-
end
|
148
|
-
|
149
|
-
def detach(&blk)
|
150
|
-
on_unbind(&blk)
|
151
|
-
return unless @attached
|
152
|
-
@attached = false
|
153
|
-
rval = super()
|
154
|
-
logger.debug { "#{self.class.name}: detached, rval: #{rval.inspect}" }
|
48
|
+
# Because eventmachine is single-threaded, and events are dispatched on the
|
49
|
+
# reactor thread we just delegate this to EM.reactor_thread?
|
50
|
+
def event_dispatch_thread?
|
51
|
+
EM.reactor_thread?
|
155
52
|
end
|
156
|
-
|
157
|
-
|
158
|
-
def notify_readable
|
159
|
-
if @zk_client.running?
|
160
|
-
|
161
|
-
read_io_nb if @zk_client.dispatch_next_callback(false)
|
162
|
-
|
163
|
-
elsif attached?
|
164
|
-
logger.debug { "#{self.class.name}: @zk_client was not running? and attached? #{attached?}, detaching!" }
|
165
|
-
detach
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
private
|
170
|
-
def read_io_nb(size=1)
|
171
|
-
@io.read_nonblock(1)
|
172
|
-
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, IOError
|
173
|
-
end
|
174
|
-
|
175
|
-
def logger
|
176
|
-
Zookeeper.logger
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
53
|
+
end # Client
|
54
|
+
end # ZookeeperEM
|
180
55
|
|
data/lib/zookeeper/exceptions.rb
CHANGED
@@ -27,7 +27,7 @@ module ZookeeperExceptions
|
|
27
27
|
ZNOTHING = -117
|
28
28
|
ZSESSIONMOVED = -118
|
29
29
|
|
30
|
-
class ZookeeperException <
|
30
|
+
class ZookeeperException < StandardError
|
31
31
|
class EverythingOk < ZookeeperException; end
|
32
32
|
class SystemError < ZookeeperException; end
|
33
33
|
class RunTimeInconsistency < ZookeeperException; end
|
@@ -58,6 +58,9 @@ module ZookeeperExceptions
|
|
58
58
|
class NotConnected < ZookeeperException; end
|
59
59
|
class ShuttingDownException < ZookeeperException; end
|
60
60
|
class DataTooLargeException < ZookeeperException; end
|
61
|
+
|
62
|
+
# yes, make an alias, this is the way zookeeper refers to it
|
63
|
+
ExpiredSession = SessionExpired
|
61
64
|
|
62
65
|
def self.by_code(code)
|
63
66
|
case code
|
data/lib/zookeeper.rb
CHANGED
@@ -14,7 +14,6 @@ if defined?(::JRUBY_VERSION)
|
|
14
14
|
$LOAD_PATH.unshift(File.expand_path('../java', File.dirname(__FILE__))).uniq!
|
15
15
|
else
|
16
16
|
$LOAD_PATH.unshift(File.expand_path('../ext', File.dirname(__FILE__))).uniq!
|
17
|
-
require 'zookeeper_c'
|
18
17
|
end
|
19
18
|
|
20
19
|
require 'zookeeper_base'
|
@@ -33,6 +32,7 @@ class Zookeeper < ZookeeperBase
|
|
33
32
|
end
|
34
33
|
|
35
34
|
def reopen(timeout=10, watcher=nil)
|
35
|
+
warn "WARN: ZookeeperBase#reopen watcher argument is now ignored" if watcher
|
36
36
|
super
|
37
37
|
end
|
38
38
|
|
@@ -121,6 +121,24 @@ class Zookeeper < ZookeeperBase
|
|
121
121
|
{ :req_id => req_id, :rc => rc }
|
122
122
|
end
|
123
123
|
|
124
|
+
# this method is *only* asynchronous
|
125
|
+
#
|
126
|
+
# @note There is a discrepancy between the zkc and java versions. zkc takes
|
127
|
+
# a string_callback_t, java takes a VoidCallback. You should most likely use
|
128
|
+
# the ZookeeperCallbacks::VoidCallback and not rely on the string value.
|
129
|
+
#
|
130
|
+
def sync(options = {})
|
131
|
+
assert_open
|
132
|
+
assert_supported_keys(options, [:path, :callback, :callback_context])
|
133
|
+
assert_required_keys(options, [:path, :callback])
|
134
|
+
|
135
|
+
req_id = setup_call(:sync, options)
|
136
|
+
|
137
|
+
rc = super(req_id, options[:path]) # we don't pass options[:callback] here as this method is *always* async
|
138
|
+
|
139
|
+
{ :req_id => req_id, :rc => rc }
|
140
|
+
end
|
141
|
+
|
124
142
|
def set_acl(options = {})
|
125
143
|
assert_open
|
126
144
|
assert_supported_keys(options, [:path, :acl, :version, :callback, :callback_context])
|
@@ -165,12 +183,40 @@ class Zookeeper < ZookeeperBase
|
|
165
183
|
def associating?
|
166
184
|
super
|
167
185
|
end
|
186
|
+
|
187
|
+
# There are some operations that are dangerous in the context of the event
|
188
|
+
# dispatch thread (because they would block further event delivery). This
|
189
|
+
# method allows clients to know if they're currently executing in the context of an
|
190
|
+
# event.
|
191
|
+
#
|
192
|
+
# @returns [true,false] true if the current thread is the event dispatch thread
|
193
|
+
def event_dispatch_thread?
|
194
|
+
super
|
195
|
+
end
|
168
196
|
|
169
197
|
# for expert use only. set the underlying debug level for the C layer, has no
|
170
198
|
# effect in java
|
171
199
|
#
|
200
|
+
# @private
|
172
201
|
def self.set_debug_level(val)
|
173
|
-
|
202
|
+
if defined?(::CZookeeper)
|
203
|
+
CZookeeper.set_debug_level(val.to_i)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# @private
|
208
|
+
def self.get_debug_level
|
209
|
+
if defined?(::CZookeeper)
|
210
|
+
CZookeeper.get_debug_level
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class << self
|
215
|
+
# @private
|
216
|
+
alias :debug_level= :set_debug_level
|
217
|
+
|
218
|
+
# @private
|
219
|
+
alias :debug_level :get_debug_level
|
174
220
|
end
|
175
221
|
|
176
222
|
# DEPRECATED: use the class-level method instead
|
@@ -188,18 +234,6 @@ class Zookeeper < ZookeeperBase
|
|
188
234
|
super
|
189
235
|
end
|
190
236
|
|
191
|
-
# returns an IO object that will be readable when an event is ready for dispatching
|
192
|
-
# (for internal use only)
|
193
|
-
def selectable_io
|
194
|
-
super
|
195
|
-
end
|
196
|
-
|
197
|
-
# closes the underlying connection object
|
198
|
-
# (for internal use only)
|
199
|
-
def close_handle
|
200
|
-
super
|
201
|
-
end
|
202
|
-
|
203
237
|
# return the session id of the current connection as an Fixnum
|
204
238
|
def session_id
|
205
239
|
super
|
@@ -244,8 +278,10 @@ protected
|
|
244
278
|
private
|
245
279
|
# TODO: Sanitize user mistakes by unregistering watchers from ops that
|
246
280
|
# don't return ZOK (except wexists)? Make users clean up after themselves for now.
|
281
|
+
#
|
282
|
+
# XXX: is this dead code?
|
247
283
|
def unregister_watcher(req_id)
|
248
|
-
@
|
284
|
+
@mutex.synchronize {
|
249
285
|
@watcher_reqs.delete(req_id)
|
250
286
|
}
|
251
287
|
end
|
data/slyphon-zookeeper.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "slyphon-zookeeper"
|
6
|
-
s.version = '0.
|
6
|
+
s.version = '0.8.0'
|
7
7
|
|
8
8
|
s.authors = ["Phillip Pearson", "Eric Maland", "Evan Weaver", "Brian Wickman", "Neil Conway", "Jonathan D. Simms"]
|
9
9
|
s.email = ["slyphon@gmail.com"]
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# tests the CZookeeper, obviously only available when running under MRI
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
if Module.const_defined?(:CZookeeper)
|
5
|
+
describe CZookeeper do
|
6
|
+
def pop_all_events
|
7
|
+
[].tap do |rv|
|
8
|
+
begin
|
9
|
+
rv << @event_queue.pop(non_blocking=true)
|
10
|
+
rescue ThreadError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def wait_until_connected(timeout=2)
|
16
|
+
wait_until(timeout) { @czk.state == ZookeeperConstants::ZOO_CONNECTED_STATE }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe do
|
20
|
+
before do
|
21
|
+
@event_queue = ZookeeperCommon::QueueWithPipe.new
|
22
|
+
@czk = CZookeeper.new('localhost:2181', @event_queue)
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
@czk.close rescue Exception
|
27
|
+
@event_queue.close rescue Exception
|
28
|
+
end
|
29
|
+
|
30
|
+
it %[should be in connected state within a reasonable amount of time] do
|
31
|
+
wait_until_connected.should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
describe :after_connected do
|
35
|
+
before do
|
36
|
+
wait_until_connected.should be_true
|
37
|
+
end
|
38
|
+
|
39
|
+
it %[should have a connection event after being connected] do
|
40
|
+
event = wait_until(2) { @event_queue.pop }
|
41
|
+
event.should be
|
42
|
+
event[:req_id].should == ZookeeperCommon::ZKRB_GLOBAL_CB_REQ
|
43
|
+
event[:type].should == ZookeeperConstants::ZOO_SESSION_EVENT
|
44
|
+
event[:state].should == ZookeeperConstants::ZOO_CONNECTED_STATE
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared/connection_examples'
|
3
|
+
|
4
|
+
describe 'Zookeeper chrooted' do
|
5
|
+
let(:path) { "/_zkchroottest_" }
|
6
|
+
let(:data) { "underpants" }
|
7
|
+
let(:chroot_path) { '/slyphon-zookeeper-chroot' }
|
8
|
+
|
9
|
+
let(:connection_string) { "localhost:2181#{chroot_path}" }
|
10
|
+
|
11
|
+
before do
|
12
|
+
@zk = Zookeeper.new(connection_string)
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
@zk and @zk.close
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def zk
|
21
|
+
@zk
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_open_zk(host='localhost:2181')
|
25
|
+
z = Zookeeper.new(host)
|
26
|
+
yield z
|
27
|
+
ensure
|
28
|
+
z.close
|
29
|
+
|
30
|
+
wait_until do
|
31
|
+
begin
|
32
|
+
!z.connected?
|
33
|
+
rescue RuntimeError
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# this is not as safe as the one in ZK, just to be used to clean up
|
40
|
+
# when we're the only one adjusting a particular path
|
41
|
+
def rm_rf(z, path)
|
42
|
+
z.get_children(:path => path).tap do |h|
|
43
|
+
if h[:rc].zero?
|
44
|
+
h[:children].each do |child|
|
45
|
+
rm_rf(z, File.join(path, child))
|
46
|
+
end
|
47
|
+
elsif h[:rc] == ZookeeperExceptions::ZNONODE
|
48
|
+
# no-op
|
49
|
+
else
|
50
|
+
raise "Oh noes! unexpected return value! #{h.inspect}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
rv = z.delete(:path => path)
|
55
|
+
|
56
|
+
unless (rv[:rc].zero? or rv[:rc] == ZookeeperExceptions::ZNONODE)
|
57
|
+
raise "oh noes! failed to delete #{path}"
|
58
|
+
end
|
59
|
+
|
60
|
+
path
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'non-existent' do
|
64
|
+
describe 'with existing parent' do
|
65
|
+
let(:chroot_path) { '/one-level' }
|
66
|
+
|
67
|
+
describe 'create' do
|
68
|
+
before do
|
69
|
+
with_open_zk do |z|
|
70
|
+
rm_rf(z, chroot_path)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it %[should successfully create the path] do
|
75
|
+
rv = zk.create(:path => '/', :data => '')
|
76
|
+
rv[:rc].should be_zero
|
77
|
+
rv[:path].should == ''
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'with missing parent' do
|
83
|
+
let(:chroot_path) { '/deeply/nested/path' }
|
84
|
+
|
85
|
+
describe 'create' do
|
86
|
+
before do
|
87
|
+
with_open_zk do |z|
|
88
|
+
rm_rf(z, chroot_path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it %[should return ZNONODE] do
|
93
|
+
rv = zk.create(:path => '/', :data => '')
|
94
|
+
rv[:rc].should_not be_zero
|
95
|
+
rv[:rc].should == ZookeeperExceptions::ZNONODE
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
describe do
|
103
|
+
before :all do
|
104
|
+
Zookeeper.logger.warn "running before :all"
|
105
|
+
|
106
|
+
with_open_zk do |z|
|
107
|
+
z.create(:path => chroot_path, :data => '')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
after :all do
|
112
|
+
with_open_zk do |z|
|
113
|
+
rm_rf(z, chroot_path)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it_should_behave_like "connection"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
data/spec/em_spec.rb
CHANGED
@@ -28,66 +28,6 @@ describe 'ZookeeperEM' do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
describe 'selectable_io' do
|
32
|
-
it %[should return an IO object] do
|
33
|
-
setup_zk do
|
34
|
-
@zk.selectable_io.should be_instance_of(IO)
|
35
|
-
teardown_and_done
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
it %[should not be closed] do
|
40
|
-
setup_zk do
|
41
|
-
@zk.selectable_io.should_not be_closed
|
42
|
-
teardown_and_done
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
before do
|
47
|
-
@data_cb = ZookeeperCallbacks::DataCallback.new do
|
48
|
-
logger.debug { "cb called: #{@data_cb.inspect}" }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
it %[should be read-ready if there's an event waiting] do
|
53
|
-
setup_zk do
|
54
|
-
@zk.get(:path => "/", :callback => @data_cb)
|
55
|
-
|
56
|
-
r, *_ = IO.select([@zk.selectable_io], [], [], 2)
|
57
|
-
|
58
|
-
r.should be_kind_of(Array)
|
59
|
-
|
60
|
-
teardown_and_done
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe 'em_connection' do
|
66
|
-
before do
|
67
|
-
@zk = ZookeeperEM::Client.new('localhost:2181')
|
68
|
-
end
|
69
|
-
|
70
|
-
it %[should be nil before the reactor is started] do
|
71
|
-
@zk.em_connection.should be_nil
|
72
|
-
|
73
|
-
em do
|
74
|
-
teardown_and_done
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
it %[should fire off the on_attached callbacks once the reactor is managing us] do
|
79
|
-
@zk.on_attached do |*|
|
80
|
-
@zk.em_connection.should_not be_nil
|
81
|
-
@zk.em_connection.should be_instance_of(ZookeeperEM::ZKConnection)
|
82
|
-
teardown_and_done
|
83
|
-
end
|
84
|
-
|
85
|
-
em do
|
86
|
-
EM.reactor_running?.should be_true
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
31
|
describe 'callbacks' do
|
92
32
|
it %[should be called on the reactor thread] do
|
93
33
|
cb = lambda do |h|
|
@@ -105,7 +45,6 @@ describe 'ZookeeperEM' do
|
|
105
45
|
end
|
106
46
|
end
|
107
47
|
end
|
108
|
-
|
109
48
|
end
|
110
49
|
end
|
111
50
|
|