zkruby 3.4.3 → 3.4.4.rc3

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 ADDED
@@ -0,0 +1,11 @@
1
+ #No hidden files (except for .gitignore)
2
+ .*
3
+ !.gitignore
4
+ !.yardopts
5
+ # Gems should not checking Gemfile.lock
6
+ Gemfile.lock
7
+ #Files generated by rake
8
+ lib/jute/
9
+ *.out
10
+ doc
11
+ pkg
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ -
2
+ History.txt
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rfuse.gemspec
4
+ gemspec
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = zkruby
2
2
 
3
- * https://github.com/lwoggardner/zkruby
3
+ * http://rubygems.org/gems/zkruby
4
4
 
5
5
  == DESCRIPTION:
6
6
 
@@ -10,6 +10,9 @@ Pure ruby client for ZooKeeper (http://zookeeper.apache.org)
10
10
 
11
11
  Supports full ZooKeeper API, synchronous or asynchronous style, watches etc.. with implementations over EventMachine or plain old Ruby IO/Threads
12
12
 
13
+ Other ruby libraries for zookeeper tend to use the underlying C/Java client libraries while zkruby
14
+ implements the zookeeper wire protocol directly.
15
+
13
16
  Advantages:
14
17
  * Rubyist API - with block is asynchronous, without block is synchronous
15
18
  * Avoids conflicts between various Ruby threading models and the C/Java apis
@@ -18,9 +21,9 @@ Advantages:
18
21
 
19
22
  Disadvantages:
20
23
  * Duplicated code from Java/C libraries, particularly around herd effect protection
21
- * Maintain in parallel with breaking changes in protocol which are possibly more likely
22
- than breaking changes in the client API
23
- * Probably not as optimised in terms of performance (but your client code is ruby anyway)
24
+ * Needs to keep up with changes in wire protocol which are possibly more likely
25
+ than changes in the client API
26
+ * Possibly not as optimised in terms of performance (but your client code is ruby anyway)
24
27
  * Not production tested (yet- do you want to be the first?)
25
28
 
26
29
  == SYNOPSIS:
@@ -77,27 +80,27 @@ Disadvantages:
77
80
 
78
81
  == DEVELOPERS:
79
82
 
80
- Checkout the zookeeper source from http://zookeeper.apache.org
83
+ Download ZooKeeper from http://zookeeper.apache.org
81
84
 
82
- Checkout the zkruby source into the contrib directory
85
+ Create a conf/zoo.cfg file (copying sample.zoo.cfg is fine)
83
86
 
84
- Install hoe
87
+ Checkout the zkruby source into the contrib directory
85
88
 
86
- $ (sudo) gem install hoe
89
+ Copy (if different) src/zookeeper.jute to contrib/zkruby/src/zookeeper.jute
87
90
 
88
- Install other dependencies
91
+ Get gem dependencies
89
92
 
90
- $ rake check_extra_deps
93
+ $ bundle install
91
94
 
92
95
  Generate docs and run the tests/specs
93
96
 
94
- $ rake newb
97
+ $ rake
95
98
 
96
99
  == LICENSE:
97
100
 
98
101
  (The MIT License)
99
102
 
100
- Copyright (c) 2011
103
+ Copyright (c) 2012 Grant Gardner
101
104
 
102
105
  Permission is hereby granted, free of charge, to any person obtaining
103
106
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,39 +1,34 @@
1
- # -*- ruby -*-
2
- # hoe 2.12.5 goes looking for the plugins so we have to do it this way..
3
- $LOAD_PATH.unshift File.dirname(__FILE__) + '/jute/lib'
1
+ #!/usr/bin/env rake
2
+ $:.unshift "jute/lib"
4
3
 
5
- require 'rubygems'
6
- require 'hoe'
4
+ require 'rake/clean'
5
+ require 'yard'
6
+ require './yard_ext/enum_handler.rb'
7
+ require "bundler/gem_tasks"
8
+ require 'rspec/core/rake_task'
9
+ require 'jute/task'
7
10
 
8
- begin
9
- require './yard_ext/enum_handler'
10
- rescue LoadError => err
11
- warn "%p while trying to load yard extensions: %s" % [ err.class, err.message ]
11
+ RSpec::Core::RakeTask.new
12
+ RSpec::Core::RakeTask.new(:perf_spec) do |t|
13
+ t.rspec_opts = "--tag perf"
12
14
  end
13
-
14
15
 
16
+ YARD::Rake::YardocTask.new
15
17
 
16
- # Hoe.plugin :compiler
17
- #Hoe.plugin :gem_prelude_sucks
18
- Hoe.plugin :git
19
- # Hoe.plugin :inline
20
- # Hoe.plugin :racc
21
- # Hoe.plugin :rubyforge
22
- Hoe.plugin :yard
23
- Hoe.plugin :jute
24
-
25
- Hoe.spec 'zkruby' do
26
- self.readme_file="README.rdoc"
27
- developer('Grant Gardner', 'grant@lastweekend.com.au')
28
- dependency 'slf4r' , '~> 0.4.2'
29
- dependency 'eventmachine', '~> 0.12.10', :development
30
- dependency 'strand', '~> 0.1.0', :development
31
- dependency 'logging', '>= 1.4.1', :development
32
- dependency 'rspec', '>=2.7.0', :development
33
- dependency 'hoe-yard', '>=0.1.2', :development
34
-
35
- self.jute_modules = {
18
+ Jute::Task.new() do |t|
19
+ t.modules = {
36
20
  "org.apache.zookeeper.data" => "ZooKeeper::Data",
37
21
  "org.apache.zookeeper.proto" => "ZooKeeper::Proto"}
38
22
  end
39
- # vim: syntax=ruby
23
+
24
+ task :perf_spec => :jute
25
+ task :spec => :jute
26
+ task :build => :jute
27
+ task :install => :jute
28
+ task :release => :jute
29
+ task :yard => :jute
30
+
31
+ task :default => [:spec,:yard]
32
+
33
+ CLEAN.include "*.out","Gemfile.lock",".yardoc/"
34
+ CLOBBER.include "doc/","pkg/","lib/jute"
data/jute/lib/hoe/jute.rb CHANGED
@@ -8,8 +8,8 @@ module Hoe::Jute
8
8
  #attr_accessor :jute_compiler
9
9
  def initialize_jute
10
10
  self.jute_tasks = [:test,:spec,:package]
11
- dependency 'citrus', '~> 2.4.0', :development
12
11
  #dependency 'jute' # if jute is ever a separate gem
12
+ dependency 'citrus', '~> 2.4.0', :development
13
13
  dependency 'bindata', '~> 1.4.1'
14
14
  end
15
15
 
@@ -0,0 +1,62 @@
1
+ # coding: utf-8
2
+ require 'rake/tasklib'
3
+ require 'jute'
4
+
5
+ class Jute::Task < Rake::TaskLib
6
+
7
+ attr_accessor :modules
8
+ attr_accessor :files
9
+ attr_accessor :pathmap
10
+
11
+ def initialize name = :jute
12
+
13
+ defaults
14
+
15
+ @name = name
16
+
17
+ yield self if block_given?
18
+
19
+ define_jute_tasks
20
+ end
21
+
22
+ def defaults
23
+ @files = "src/jute/*.jute"
24
+ @pathmap = "%{src,lib}X.rb"
25
+ end
26
+
27
+ def define_jute_tasks
28
+ desc "Compile jute files to ruby classes"
29
+ task jute_task_name
30
+
31
+ raise "modules hash must be defined" unless Hash === @modules
32
+ FileList.new(@files).each do | source |
33
+ target = source.pathmap(@pathmap)
34
+
35
+ target_dir = target.pathmap("%d")
36
+ directory target_dir
37
+
38
+ file target => [source,target_dir] do
39
+ compile_jute(source,target)
40
+ end
41
+ task jute_task_name => target
42
+ end
43
+ end
44
+
45
+ def jute_task_name
46
+ @name
47
+ end
48
+
49
+ def compile_jute(source,target)
50
+
51
+ @jute_compiler = ::Jute::Compiler.new() unless @jute_compiler
52
+
53
+ File.open(source) do |input|
54
+ File.open(target,"w") do |output|
55
+ puts "Compiling #{input.inspect} to #{output.inspect}"
56
+ @jute_compiler.compile(input,output,modules)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+
data/lib/em_zkruby.rb CHANGED
@@ -1,4 +1,4 @@
1
- # This is the main require for standard ruby io/thread based binding
1
+ # This is the main require for the eventmachine based binding
2
2
 
3
3
  require 'zkruby/zkruby'
4
4
  require 'zkruby/eventmachine'
@@ -0,0 +1,193 @@
1
+ module ZooKeeper
2
+
3
+ # Returned by asynchronous calls
4
+ #
5
+ # @example
6
+ # op = zk.stat("\apath") { |stat| something_with_stat() }
7
+ #
8
+ # op.async_rescue ZK::Error::SESSION_EXPIRED do
9
+ # puts "Ignoring session expired"
10
+ # result_when_session_expired()
11
+ # end
12
+ #
13
+ # op.async_rescue ZK::Error::CONNECTION_LOST do |ex|
14
+ # puts "Retrying stat due to connection lost"
15
+ # op.async_retry()
16
+ # # the result of this block is ignored!
17
+ # end
18
+ #
19
+ # begin
20
+ # result_of_somthing_with_stat = op.value
21
+ # rescue StandardError => ex
22
+ # puts "Oops"
23
+ # end
24
+ #
25
+ #
26
+ class AsyncOp
27
+
28
+ # @api private
29
+ def initialize(event_loop,callback,&operation)
30
+ @event_loop = event_loop
31
+ @operation = operation
32
+ @callback = callback
33
+ @mutex,@cv = Strand::Mutex.new(), Strand::ConditionVariable.new()
34
+ begin
35
+ execute()
36
+ rescue ZooKeeper::Error => ex
37
+ # Capture any initial exception
38
+ # The only expected condition is :session_expired
39
+ @op_error = ex
40
+ logger.debug { "Error during initial call #{ex}" }
41
+ end
42
+ end
43
+
44
+ # @param [Proc,#to_proc] block the error callback as a Proc
45
+ # @deprecated use {#async_rescue}
46
+ def errback=(block)
47
+ async_rescue(&block)
48
+ end
49
+
50
+ #Rescue asynchronous exceptions in a similar manner to normal
51
+ #ruby. Unfortunately rescue is a reserved word
52
+ # @param matches [Class,...] subclasses of Exception to match, defaults to {::StandardError}
53
+ # @param errblock the block to call if an error matches
54
+ # @return self
55
+ # @yieldparam [Exception] ex the exception raised by the async operation OR by its callback
56
+ def async_rescue(*matches,&errblock)
57
+ matches << StandardError if matches.empty?
58
+ rescue_blocks << [ matches ,errblock ]
59
+ if @op_error && matches.any? { |m| m === @op_error }
60
+ begin
61
+ @allow_retry = true
62
+ @op_rescue= [ nil, errblock.call(@op_error) ]
63
+ rescue StandardError => ex
64
+ @operation_rescue_result = [ ex, nil ]
65
+ ensure
66
+ @resumed = true
67
+ @op_error = nil
68
+ @allow_retry = false
69
+ end
70
+ end
71
+ self
72
+ end
73
+ alias :on_error :async_rescue
74
+ alias :op_rescue :async_rescue
75
+
76
+ # @deprecated
77
+ alias :errback :async_rescue
78
+
79
+
80
+ # Must only be called inside a block supplied to {#async_rescue}
81
+ # Common case is to retry on connection lost
82
+ # Retrying :session_expired is guaranteed to give infinite loops!
83
+ def async_retry()
84
+ raise ProtocolError "cannot retry outside of a rescue block" unless @allow_retry
85
+ begin
86
+ execute()
87
+ nil
88
+ rescue ZooKeeper::Error => ex
89
+ error,result = process_response(ex,nil)
90
+ if resumed?
91
+ raise error if error
92
+ return result
93
+ end
94
+ end
95
+ end
96
+
97
+ alias :op_retry :async_retry
98
+ alias :try_again :async_retry
99
+
100
+ # Wait for the async op to finish and returns its value
101
+ # @return result of the operation's callback or matched rescue handler
102
+ # @raise [StandardError] any unrescued exception
103
+ def value();
104
+ if @op_error
105
+ raise @op_error
106
+ elsif @op_rescue
107
+ error, result = @op_rescue
108
+ raise error if error
109
+ return result
110
+ else
111
+ wait_value()
112
+ end
113
+ end
114
+
115
+ # @api private
116
+ attr_accessor :backtrace
117
+
118
+ # @api private
119
+ def resume(op_error,response)
120
+ mutex.synchronize do
121
+ # remember this mutex is only used to wait for this response anyway
122
+ # so synchronizing here is not harmful even if processing the response
123
+ # includes a long running callback. (which can't create deadlocks
124
+ # by referencing this op!
125
+ @resumed = true
126
+ @error, @result = process_response(op_error,response)
127
+ cv.signal() if resumed?
128
+ end
129
+ end
130
+
131
+ private
132
+ attr_reader :callback, :operation, :event_loop
133
+ attr_reader :mutex, :cv, :error, :result
134
+
135
+ def execute()
136
+ @op_error = nil
137
+ @op_rescue = nil
138
+ @resumed = false
139
+ operation.call(self)
140
+ true
141
+ end
142
+
143
+ def resumed?
144
+ @resumed
145
+ end
146
+
147
+ def rescue_blocks
148
+ @rescue_blocks ||= []
149
+ end
150
+
151
+ def process_response(op_error,response)
152
+ logger.debug { "Processing response #{op_error} #{response}" }
153
+
154
+ # For ZooKeeper errors, set the backtrace to the original caller, rather than the ZK event loop
155
+ op_error.set_backtrace(@backtrace) if @backtrace && ZooKeeper::Error === op_error
156
+
157
+ begin
158
+ return [ nil, callback.call(response) ] unless op_error
159
+ rescue Exception => ex #enable clients to rescue Exceptions
160
+ op_error = ex
161
+ end
162
+
163
+ matches,rb = rescue_blocks.detect() { |matches,errblock| matches.any? { |m| m === op_error } }
164
+ return [ op_error, nil ] unless rb
165
+
166
+ begin
167
+ @allow_retry = true
168
+ return [ nil, rb.call(op_error) ]
169
+ rescue StandardError => ex
170
+ return [ ex , nil ]
171
+ ensure
172
+ @allow_retry = false
173
+ end
174
+ end
175
+
176
+ def wait_value()
177
+ if event_loop.current?
178
+ #Waiting in the event loop (eg made a synchronous call inside a callback)
179
+ #Keep processing events until we are resumed
180
+ until resumed? || event_loop.dead?
181
+ event_loop.pop_event_queue()
182
+ end
183
+ else
184
+ mutex.synchronize {
185
+ cv.wait(mutex) unless resumed?
186
+ }
187
+ end
188
+
189
+ raise error if error
190
+ result
191
+ end
192
+ end
193
+ end
data/lib/zkruby/client.rb CHANGED
@@ -48,7 +48,7 @@ module ZooKeeper
48
48
 
49
49
 
50
50
  # Combine permissions constants
51
- # @param [Perms] perms... list of permissions to combine, can be {Perms} constants, symbols or ints
51
+ # @param [Perms...] perms list of permissions to combine, can be {Perms} constants, symbols or ints
52
52
  # @return [Fixnum] integer representing the combined permission
53
53
  def self.perms(*perms)
54
54
  perms.inject(0) { | result, perm | result = result | Perms.get(perm) }
@@ -56,7 +56,7 @@ module ZooKeeper
56
56
 
57
57
  # Convenience method to create a zk Identity
58
58
  # @param [String] scheme
59
- # @param [String] identity
59
+ # @param [String] id
60
60
  # @return [Data::Identity] the encapsulated identity for the given scheme
61
61
  def self.id(scheme,id)
62
62
  Data::Identity.new(:scheme => scheme, :identity => id)
@@ -65,7 +65,7 @@ module ZooKeeper
65
65
  # Convenience method to create a zk ACL
66
66
  # ZK.acl(ZK.id("world","anyone"), ZK::Perms.DELETE, ZL::Perms.WRITE)
67
67
  # @param [Data::Identity] id
68
- # @param [Perms] *perms list of permissions
68
+ # @param [Perms...] perms list of permissions
69
69
  # @return [Data::ACL] an access control list
70
70
  # @see #perms
71
71
  #
@@ -108,8 +108,6 @@ module ZooKeeper
108
108
  CURRENT = :zookeeper_current
109
109
  # Main method for connecting to a client
110
110
  # @param addresses [Array<String>] list of host:port for the ZK cluster as Array or comma separated String
111
- # @option options [Class] :binding binding optional implementation class
112
- # either {EventMachine::Binding} or {RubyIO::Binding} but normally autodiscovered
113
111
  # @option options [String] :chroot chroot path.
114
112
  # All client calls will be made relative to this path
115
113
  # @option options [Watcher] :watch the default watcher
@@ -118,37 +116,48 @@ module ZooKeeper
118
116
  # @yieldparam [Client]
119
117
  # @return [Client]
120
118
  def self.connect(addresses,options={},&block)
121
- if options.has_key?(:binding)
122
- binding_type = options[:binding]
119
+
120
+ binding_module = if Strand.event_machine?
121
+ require 'zkruby/eventmachine'
122
+ ZooKeeper::EventMachine::Binding
123
123
  else
124
- binding_type = @bindings.detect { |b| b.available? }
125
- raise ProtocolError,"No available binding" unless binding_type
124
+ require 'zkruby/rubyio'
125
+ ZooKeeper::RubyIO::Binding
126
126
  end
127
- binding = binding_type.new()
128
- session = Session.new(binding,addresses,options)
129
- client = Client.new(binding)
130
- binding.start(client,session)
131
127
 
128
+ logger.debug { "Using binding #{binding_module}" }
129
+ session = Session.new(addresses,options)
130
+ # Extend the appropriate #connect method into the session
131
+ session.extend(binding_module)
132
+
133
+ client = Client.new(session)
134
+
135
+ session.start(client)
136
+
132
137
  return client unless block_given?
133
138
 
134
- binding_type.context() do |storage|
135
- @binding_storage = storage
136
- storage.current[CURRENT] ||= []
137
- storage.current[CURRENT].push(client)
138
- begin
139
- block.call(client)
140
- ensure
141
- storage.current[CURRENT].pop
142
- client.close() unless session.closed?
143
- end
139
+ storage = Strand.current[CURRENT] ||= []
140
+ storage.push(client)
141
+ begin
142
+ yield client
143
+ ensure
144
+ storage.pop
145
+ #TODO this will throw an exception if expired
146
+ client.close()
144
147
  end
145
148
  end
146
149
 
147
- # within the block supplied to {#connect} this will return the
150
+ # within the block supplied to {ZooKeeper.connect} this will return the
148
151
  # current ZK client
149
152
  def self.current
150
153
  #We'd use if key? here if strand supported it
151
- @binding_storage.current[CURRENT].last if @binding_storage.current[CURRENT]
154
+ Strand.current[CURRENT].last if Strand.current[CURRENT]
155
+ end
156
+
157
+ # Allow ZK a chance to send its data/ping
158
+ # particularly required for the eventmachine binding
159
+ def self.pass
160
+ Strand.pass
152
161
  end
153
162
 
154
163
  class WatchEvent
@@ -175,7 +184,8 @@ module ZooKeeper
175
184
  end
176
185
 
177
186
 
178
- # @abstract.
187
+ # @abstract
188
+ # The watch interface
179
189
  class Watcher
180
190
  # @param [KeeperState] state representing the session state
181
191
  # (:connected, :disconnected, :auth_failed, :session_expired)
@@ -243,7 +253,7 @@ module ZooKeeper
243
253
  # All calls operate asynchronously or synchronously based on whether a block is supplied
244
254
  #
245
255
  # Without a block, requests are executed synchronously and either return results directly or raise
246
- # a {Error}
256
+ # an {Error}
247
257
  #
248
258
  # With a block, the request returns immediately with a {AsyncOp}. When the server responds the
249
259
  # block is passed the results. Errors will be sent to an error callback if registered on the {AsyncOp}
@@ -257,35 +267,36 @@ module ZooKeeper
257
267
  # or with state :expired and event :none when the session is finalised
258
268
  class Client
259
269
 
270
+ attr_reader :session
260
271
  include Operations
261
272
  # @api private
262
273
  # See {::ZooKeeper.connect}
263
- def initialize(binding)
264
- @binding = binding
274
+ def initialize(session)
275
+ @session = session
265
276
  end
266
277
 
267
278
  # Session timeout, initially as supplied, but once connected is the negotiated
268
279
  # timeout with the server.
269
280
  def timeout
270
- @binding.session.timeout
281
+ session.timeout
271
282
  end
272
283
 
273
284
  # The currently registered default watcher
274
285
  def watcher
275
- @binding.session.watcher
286
+ session.watcher
276
287
  end
277
288
 
278
289
  # Assign the watcher to the session. This watcher will receive session connect/disconnect/expired
279
290
  # events as well as any path based watches registered to the API calls using the literal value "true"
280
- # @param [Watcher|Proc] watcher
291
+ # @param [Watcher,#process_watch,Proc] watcher
281
292
  def watcher=(watcher)
282
- @binding.session.watcher=watcher
293
+ session.watcher=watcher
283
294
  end
284
295
 
285
296
  # Retrieve the list of children at the given path
286
297
  # @overload children(path,watch=nil)
287
298
  # @param [String] path
288
- # @param [Watcher] if supplied sets a child watch on the given path
299
+ # @param [Watcher,#process_watch,Proc] if supplied sets a child watch on the given path
289
300
  # @return [Data::Stat,Array<String>] stat,children stat of path and the list of child nodes
290
301
  # @raise [Error]
291
302
  # @overload children(path,watch=nil)
@@ -321,7 +332,7 @@ module ZooKeeper
321
332
  # Retrieve data
322
333
  # @overload get(path,watch=nil)
323
334
  # @param [String] path
324
- # @param [Watcher] watch optional data watch to set on this path
335
+ # @param [Watcher,#process_watch,Proc] watch optional data watch to set on this path
325
336
  # @return [Data::Stat,String] stat,data at path
326
337
  # @raise [Error]
327
338
  # @overload get(path,watch=nil)
@@ -342,19 +353,19 @@ module ZooKeeper
342
353
  # Retrieve the {Data::Stat} of a path, or nil if the path does not exist
343
354
  # @overload exists(path,watch=nil)
344
355
  # @param [String] path
345
- # @param [Watcher] wath optional exists watch to set on this path
356
+ # @param [Watcher,#process_watch,Proc] watch optional exists watch to set on this path
346
357
  # @return [Data::Stat] Stat of the path or nil if the path does not exist
347
358
  # @raise [Error]
348
359
  # @overload exists(path,watch=nil)
349
360
  # @return [AsyncOp] asynchronous operation
350
361
  # @yieldparam [Data:Stat] stat Stat of the path or nil if the path did not exist
351
- def exists(path,watch=nil,&blk)
362
+ def exists(path,watch=nil,&callback)
352
363
  return synchronous_call(:exists,path,watch)[0] unless block_given?
353
364
  path = chroot(path)
354
365
 
355
366
  req = Proto::ExistsRequest.new(:path => path, :watch => watch)
356
367
  queue_request(req,:exists,3,Proto::ExistsResponse,:exists,watch,ExistsPacket) do | response |
357
- blk.call( response.nil? ? nil : response.stat )
368
+ callback.call( response.nil? ? nil : response.stat )
358
369
  end
359
370
  end
360
371
  alias :exists? :exists
@@ -445,13 +456,13 @@ module ZooKeeper
445
456
 
446
457
  # Close the session
447
458
  # @overload close()
448
- # @raise [Error]
459
+ # @raise [Error]
449
460
  # @overload close()
450
461
  # @return [AsyncOp] asynchronous operation
451
462
  # @yield [] callback invoked when session is closed
452
463
  def close(&blk)
453
464
  return synchronous_call(:close) unless block_given?
454
- @binding.close(&blk)
465
+ session.close(&blk)
455
466
  end
456
467
 
457
468
  # @api private
@@ -501,23 +512,26 @@ module ZooKeeper
501
512
  yield txn
502
513
  txn.commit
503
514
  end
504
- private
505
515
 
506
- def session
507
- @binding.session
508
- end
509
516
 
517
+ private
518
+
519
+ # This is where the magic happens!
510
520
  def synchronous_call(method,*args)
521
+ # Re-enter the calling method in asynchronous style
511
522
  op = self.send(method,*args) do |*results|
512
523
  results
513
524
  end
525
+
526
+ # Remove this call from the stored backtrace
514
527
  op.backtrace = op.backtrace[2..-1] if op.backtrace
515
528
 
529
+ # Wait for the asynchronous op to finish and return its value
516
530
  op.value
517
531
  end
518
532
 
519
533
  def queue_request(*args,&blk)
520
- op = @binding.queue_request(*args,&blk)
534
+ op = session.request(*args,&blk)
521
535
  op.backtrace = caller[1..-1]
522
536
  op
523
537
  end
@@ -540,7 +554,8 @@ module ZooKeeper
540
554
  # If the transaction fails none of these callbacks will be executed.
541
555
  class Transaction
542
556
  include Operations
543
- #:nodoc
557
+ # @api private
558
+ # See {Client#transaction}
544
559
  def initialize(client,session)
545
560
  @client = client
546
561
  @session = session