rethinkdb 1.16.0.1 → 2.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3f11a0723b23f0f85750e89da95f68a9bc50f70c
4
- data.tar.gz: fc81d994d1b54b887a7228f7d80b8c5a07a9016c
3
+ metadata.gz: dae3d5d3b038eca2d30595a269cb259351d5bd98
4
+ data.tar.gz: 7a03e7c5f271bbb2a7e2e63f74a2757864c4cdbb
5
5
  SHA512:
6
- metadata.gz: ef85ef58f60f5ab17790c61dc4121943cc4d29fad9cea95f16dda9c6338865be98bbdab496ec1cbbc7671728fd10f8be397be3c07ba39548d0ceda4efc306681
7
- data.tar.gz: d8a59acf79a53e9ccc8d9e20b4ebe8f84d804003270be43e203e5204589ab435ceb4b167e732a216cf8dcddfa4406b727579dddddf3fa9d5249fb69f3af01091
6
+ metadata.gz: 308251a1089f50555e2e8177af64235bea9b5a36d35615aca7852f8f72530f17cb0c70569d6a727012aad0f273c6912262cedd9e6b6e1748d60d59e5ec4feb91
7
+ data.tar.gz: 6f31e8e16757cdaa9fc98137fd732323fa32c7d03c0af3edc2fcad299dfa9dff0c3bad2db838e2e7db1426c07bf3ed3729b8c723b38d3f784396722b3ccfbe5a
data/lib/exc.rb CHANGED
@@ -1,30 +1,6 @@
1
1
  module RethinkDB
2
- class RqlError < RuntimeError; end
3
- class RqlRuntimeError < RqlError; end
4
- class RqlDriverError < RqlError; end
5
- class RqlCompileError < RqlError; end
6
-
7
- # class Error < StandardError
8
- # def initialize(msg, bt)
9
- # @msg = msg
10
- # @bt = bt
11
- # @pp_query_bt = "UNIMPLEMENTED #{bt.inspect}"
12
- # super inspect
13
- # end
14
- # def inspect
15
- # "#{@msg}\n#{@pp_query_bt}"
16
- # end
17
- # attr_accessor :msg, :bt, :pp_query_bt
18
- # end
19
-
20
- # class Connection_Error < Error; end
21
-
22
- # class Compile_Error < Error; end
23
- # class Malformed_Protobuf_Error < Compile_Error; end
24
- # class Malformed_Query_Error < Compile_Error; end
25
-
26
- # class Runtime_Error < Error; end
27
- # class Data_Error < Runtime_Error; end
28
- # class Type_Error < Data_Error; end
29
- # class Resource_Error < Runtime_Error; end
2
+ RqlError = Class.new(RuntimeError)
3
+ RqlRuntimeError = Class.new(RqlError)
4
+ RqlDriverError = Class.new(RqlError)
5
+ RqlCompileError = Class.new(RqlError)
30
6
  end
data/lib/func.rb CHANGED
@@ -60,8 +60,8 @@ module RethinkDB
60
60
  :mul => :*,
61
61
  :div => :/,
62
62
  :mod => :%,
63
- :any => [:"|", :or],
64
- :all => [:"&", :and],
63
+ :or => :"|",
64
+ :and => :"&",
65
65
  :order_by => :orderby,
66
66
  :concat_map => :concatmap,
67
67
  :for_each => :foreach,
@@ -82,7 +82,7 @@ module RethinkDB
82
82
 
83
83
  if [:<, :<=, :>, :>=, :+, :-, :*, :/, :%].include?(__method__)
84
84
  a.each {|arg|
85
- if arg.class == RQL && arg.bitop
85
+ if arg.is_a?(RQL) && arg.bitop
86
86
  err = "Calling #{__method__} on result of infix bitwise operator:\n" +
87
87
  "#{arg.inspect}.\n" +
88
88
  "This is almost always a precedence error.\n" +
@@ -94,7 +94,7 @@ module RethinkDB
94
94
  end
95
95
 
96
96
  if (opt_offset = @@optarg_offsets[termtype.downcase])
97
- if opt_offset.class == Hash
97
+ if opt_offset.is_a?(Hash)
98
98
  opt_offset = opt_offset[b ? :with_block : :without]
99
99
  end
100
100
  # TODO: This should drop the Hash comparison or at least
@@ -102,7 +102,7 @@ module RethinkDB
102
102
  # Any time one of these operations is changed to support a
103
103
  # hash argument, we'll have to remember to fix
104
104
  # @@optarg_offsets, otherwise.
105
- optargs = a.delete_at(opt_offset) if a[opt_offset].class == Hash
105
+ optargs = a.delete_at(opt_offset) if a[opt_offset].is_a?(Hash)
106
106
  end
107
107
 
108
108
  args = ((@body != RQL) ? [self] : []) + a + (b ? [new_func(&b)] : [])
@@ -131,7 +131,7 @@ module RethinkDB
131
131
  def -@; RQL.new.sub(0, self); end
132
132
 
133
133
  def [](ind)
134
- if ind.class == Range
134
+ if ind.is_a?(Range)
135
135
  return slice(ind.begin, ind.end, :right_bound =>
136
136
  (ind.exclude_end? ? 'open' : 'closed'))
137
137
  else
data/lib/net.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'monitor'
2
+ require 'set'
1
3
  require 'socket'
2
4
  require 'thread'
3
5
  require 'timeout'
@@ -9,14 +11,285 @@ module RethinkDB
9
11
  end
10
12
  end
11
13
 
14
+ class EM_Guard
15
+ @@mutex = Mutex.new
16
+ @@registered = false
17
+ @@conns = Set.new
18
+ def self.register(conn)
19
+ @@mutex.synchronize {
20
+ if !@@registered
21
+ @@registered = true
22
+ EM.add_shutdown_hook {
23
+ EM_Guard.remove_em_waiters
24
+ }
25
+ end
26
+ @@conns += [conn]
27
+ }
28
+ end
29
+ def self.unregister(conn)
30
+ @@mutex.synchronize {
31
+ @@conns -= [conn]
32
+ }
33
+ end
34
+ def self.remove_em_waiters
35
+ old_conns = Set.new
36
+ @@mutex.synchronize {
37
+ @@registered = false
38
+ @@conns, old_conns = old_conns, @@conns
39
+ }
40
+ # This function acquires `@mon` on the connections, so it's
41
+ # safer to do this outside our own synchronization.
42
+ old_conns.each {|conn|
43
+ conn.remove_em_waiters
44
+ }
45
+ end
46
+ end
47
+
48
+ class Handler
49
+ def initialize
50
+ @stopped = false
51
+ end
52
+ def handle(m, args, caller)
53
+ if !stopped?
54
+ if method(m).arity == args.size
55
+ send(m, *args)
56
+ else
57
+ send(m, *args, caller)
58
+ end
59
+ end
60
+ end
61
+
62
+ def on_open(caller)
63
+ end
64
+ def on_close(caller)
65
+ end
66
+ def on_wait_complete(caller)
67
+ end
68
+
69
+ def on_error(err, caller)
70
+ raise err
71
+ end
72
+ def on_val(val, caller)
73
+ end
74
+ def on_array(arr, caller)
75
+ if method(:on_atom).owner != Handler
76
+ handle(:on_atom, [arr], caller)
77
+ else
78
+ arr.each {|x|
79
+ break if stopped?
80
+ handle(:on_stream_val, [x], caller)
81
+ }
82
+ end
83
+ end
84
+ def on_atom(val, caller)
85
+ handle(:on_val, [val], caller)
86
+ end
87
+ def on_stream_val(val, caller)
88
+ handle(:on_val, [val], caller)
89
+ end
90
+
91
+ def on_unhandled_change(val, caller)
92
+ handle(:on_stream_val, [val], caller)
93
+ end
94
+
95
+ def stop
96
+ @stopped = true
97
+ end
98
+ def stopped?
99
+ @stopped
100
+ end
101
+ end
102
+
103
+ class CallbackHandler < Handler
104
+ def initialize(callback)
105
+ if callback.arity > 2 || callback.arity < -3
106
+ raise ArgumentError, "Wrong number of arguments for callback (callback " +
107
+ "accepts #{callback.arity} arguments, but it should accept 0, 1 or 2)."
108
+ end
109
+ @callback = callback
110
+ end
111
+ def do_call(err, val)
112
+ if @callback.arity == 0
113
+ raise err if err
114
+ @callback.call
115
+ elsif @callback.arity == 1
116
+ raise err if err
117
+ @callback.call(val)
118
+ else
119
+ @callback.call(err, val)
120
+ end
121
+ end
122
+ def on_val(x)
123
+ do_call(nil, x)
124
+ end
125
+ def on_error(err)
126
+ do_call(err, nil)
127
+ end
128
+ end
129
+
130
+ class QueryHandle
131
+ def initialize(handler, msg, all_opts, token, conn)
132
+ @handler = handler
133
+ @msg = msg
134
+ @all_opts = all_opts
135
+ @token = token
136
+ @conn = conn
137
+ @opened = false
138
+ @closed = false
139
+ end
140
+ def closed?
141
+ @closed
142
+ end
143
+ def close
144
+ if !@closed
145
+ handle_close
146
+ return @conn.stop(@token)
147
+ end
148
+ return false
149
+ end
150
+ def handle(m, *args)
151
+ @handler.handle(m, args, self)
152
+ end
153
+ def handle_open
154
+ if !@opened
155
+ @opened = true
156
+ handle(:on_open)
157
+ end
158
+ end
159
+ def handle_close
160
+ if !@closed
161
+ @closed = true
162
+ handle(:on_close)
163
+ end
164
+ end
165
+ def safe_next_tick(&b)
166
+ EM.next_tick {
167
+ b.call if !@closed
168
+ }
169
+ end
170
+ def callback(res)
171
+ begin
172
+ if @handler.stopped? || !EM.reactor_running?
173
+ @closed = true
174
+ @conn.stop(@token)
175
+ return
176
+ elsif res
177
+ is_cfeed = (res['n'] & [Response::ResponseNote::SEQUENCE_FEED,
178
+ Response::ResponseNote::ATOM_FEED,
179
+ Response::ResponseNote::ORDER_BY_LIMIT_FEED,
180
+ Response::ResponseNote::UNIONED_FEED]) != []
181
+ if (res['t'] == Response::ResponseType::SUCCESS_PARTIAL) ||
182
+ (res['t'] == Response::ResponseType::SUCCESS_SEQUENCE)
183
+ safe_next_tick {
184
+ handle_open
185
+ if res['t'] == Response::ResponseType::SUCCESS_PARTIAL
186
+ @conn.register_query(@token, @all_opts, self) if !@conn.closed?
187
+ @conn.dispatch([Query::QueryType::CONTINUE], @token) if !@conn.closed?
188
+ end
189
+ Shim.response_to_native(res, @msg, @all_opts).each {|row|
190
+ if is_cfeed
191
+ if (row.has_key?('new_val') && row.has_key?('old_val') &&
192
+ @handler.respond_to?(:on_change))
193
+ handle(:on_change, row['old_val'], row['new_val'])
194
+ elsif (row.has_key?('new_val') && !row.has_key?('old_val') &&
195
+ @handler.respond_to?(:on_initial_val))
196
+ handle(:on_initial_val, row['new_val'])
197
+ elsif row.has_key?('error') && @handler.respond_to?(:on_change_error)
198
+ handle(:on_change_error, row['error'])
199
+ elsif row.has_key?('state') && @handler.respond_to?(:on_state)
200
+ handle(:on_state, row['state'])
201
+ else
202
+ handle(:on_unhandled_change, row)
203
+ end
204
+ else
205
+ handle(:on_stream_val, row)
206
+ end
207
+ }
208
+ if (res['t'] == Response::ResponseType::SUCCESS_SEQUENCE ||
209
+ @conn.closed?)
210
+ handle_close
211
+ end
212
+ }
213
+ elsif res['t'] == Response::ResponseType::SUCCESS_ATOM
214
+ safe_next_tick {
215
+ return if @closed
216
+ handle_open
217
+ val = Shim.response_to_native(res, @msg, @all_opts)
218
+ if val.is_a?(Array)
219
+ handle(:on_array, val)
220
+ else
221
+ handle(:on_atom, val)
222
+ end
223
+ handle_close
224
+ }
225
+ elsif res['t'] == Response::ResponseType::WAIT_COMPLETE
226
+ safe_next_tick {
227
+ return if @closed
228
+ handle_open
229
+ handle(:on_wait_complete)
230
+ handle_close
231
+ }
232
+ else
233
+ exc = nil
234
+ begin
235
+ exc = Shim.response_to_native(res, @msg, @all_opts)
236
+ rescue Exception => e
237
+ exc = e
238
+ end
239
+ safe_next_tick {
240
+ return if @closed
241
+ handle_open
242
+ handle(:on_error, e)
243
+ handle_close
244
+ }
245
+ end
246
+ else
247
+ safe_next_tick {
248
+ return if @closed
249
+ handle_close
250
+ }
251
+ end
252
+ rescue Exception => e
253
+ safe_next_tick {
254
+ return if @closed
255
+ handle_open
256
+ handle(:on_error, e)
257
+ handle_close
258
+ }
259
+ end
260
+ end
261
+ end
262
+
12
263
  class RQL
13
264
  @@default_conn = nil
14
265
  def self.set_default_conn c; @@default_conn = c; end
15
- def run(c=@@default_conn, opts=nil, &b)
16
- unbound_if(@body == RQL)
17
- c, opts = @@default_conn, c if !opts && !c.kind_of?(RethinkDB::Connection)
266
+ def parse(*args, &b)
267
+ conn = nil
268
+ opts = nil
269
+ block = nil
270
+ args = args.map{|x| x.is_a?(Class) ? x.new : x}
271
+ args.each {|arg|
272
+ case arg
273
+ when RethinkDB::Connection
274
+ raise ArgumentError, "Unexpected second Connection #{arg.inspect}." if conn
275
+ conn = arg
276
+ when Hash
277
+ raise ArgumentError, "Unexpected second Hash #{arg.inspect}." if opts
278
+ opts = arg
279
+ when Proc
280
+ raise ArgumentError, "Unexpected second callback #{arg.inspect}." if block
281
+ block = arg
282
+ when Handler
283
+ raise ArgumentError, "Unexpected second callback #{arg.inspect}." if block
284
+ block = arg
285
+ else
286
+ raise ArgumentError, "Unexpected argument #{arg.inspect} " +
287
+ "(got #{args.inspect})."
288
+ end
289
+ }
290
+ conn = @@default_conn if !conn
18
291
  opts = {} if !opts
19
- opts = {opts => true} if opts.class != Hash
292
+ block = b if !block
20
293
  if (tf = opts[:time_format])
21
294
  opts[:time_format] = (tf = tf.to_s)
22
295
  if tf != 'raw' && tf != 'native'
@@ -35,11 +308,37 @@ module RethinkDB
35
308
  raise ArgumentError, "`binary_format` must be 'raw' or 'native' (got `#{bf}`)."
36
309
  end
37
310
  end
38
- if !c
311
+ if !conn
39
312
  raise ArgumentError, "No connection specified!\n" \
40
313
  "Use `query.run(conn)` or `conn.repl(); query.run`."
41
314
  end
42
- c.run(@body, opts, &b)
315
+ {conn: conn, opts: opts, block: block}
316
+ end
317
+ def run(*args, &b)
318
+ unbound_if(@body == RQL)
319
+ args = parse(*args, &b)
320
+ args[:conn].run(@body, args[:opts], args[:block])
321
+ end
322
+ def em_run(*args, &b)
323
+ if !EM.reactor_running?
324
+ raise RuntimeError, "RethinkDB::RQL::em_run can only be called inside `EM.run`"
325
+ end
326
+ unbound_if(@body == RQL)
327
+ args = parse(*args, &b)
328
+ if args[:block].is_a?(Proc)
329
+ args[:block] = CallbackHandler.new(args[:block])
330
+ end
331
+ if !args[:block].is_a?(Handler)
332
+ raise ArgumentError, "No handler specified."
333
+ end
334
+
335
+ # If the user has defined the `on_state` method, we assume they want states.
336
+ if args[:block].respond_to?(:on_state)
337
+ args[:opts] = args[:opts].merge(include_states: true)
338
+ end
339
+
340
+ EM_Guard.register(args[:conn])
341
+ args[:conn].run(@body, args[:opts], args[:block])
43
342
  end
44
343
  end
45
344
 
@@ -57,11 +356,11 @@ module RethinkDB
57
356
  preview = preview_res.pretty_inspect[0...-1]
58
357
  state = @run ? "(exhausted)" : "(enumerable)"
59
358
  extra = out_of_date ? " (Connection #{@conn.inspect} is closed.)" : ""
60
- "#<RethinkDB::Cursor:#{self.object_id} #{state}#{extra}: #{RPP.pp(@msg)}" +
359
+ "#<RethinkDB::Cursor:#{object_id} #{state}#{extra}: #{RPP.pp(@msg)}" +
61
360
  (@run ? "" : "\n#{preview}") + ">"
62
361
  end
63
362
 
64
- def initialize(results, msg, connection, opts, token, more = true) # :nodoc:
363
+ def initialize(results, msg, connection, opts, token, more) # :nodoc:
65
364
  @more = more
66
365
  @results = results
67
366
  @msg = msg
@@ -73,43 +372,64 @@ module RethinkDB
73
372
  fetch_batch
74
373
  end
75
374
 
76
- def each (&block) # :nodoc:
375
+ def each(&block) # :nodoc:
77
376
  raise RqlRuntimeError, "Can only iterate over a cursor once." if @run
78
- return self.enum_for(:each) if !block
377
+ return enum_for(:each) if !block
79
378
  @run = true
80
379
  while true
81
380
  @results.each(&block)
82
381
  return self if !@more
83
382
  raise RqlRuntimeError, "Connection is closed." if @more && out_of_date
84
- res = @conn.wait(@token)
383
+ wait_for_batch(nil)
384
+ end
385
+ end
386
+
387
+ def close
388
+ if @more
389
+ @more = false
390
+ @conn.stop(@token)
391
+ return true
392
+ end
393
+ return false
394
+ end
395
+
396
+ def wait_for_batch(timeout)
397
+ res = @conn.wait(@token, timeout)
85
398
  @results = Shim.response_to_native(res, @msg, @opts)
86
399
  if res['t'] == Response::ResponseType::SUCCESS_SEQUENCE
87
400
  @more = false
88
401
  else
89
402
  fetch_batch
90
403
  end
91
- end
92
404
  end
93
405
 
94
- def close
406
+ def fetch_batch
95
407
  if @more
96
- @more = false
97
- @conn.wait(@token) # Ignore the response - TODO: do this asynchronously
98
- q = [Query::QueryType::STOP]
99
- res = @conn.run_internal(q, @opts, @token)
100
- if ((res['t'] != Response::ResponseType::SUCCESS_SEQUENCE &&
101
- res['t'] != Response::ResponseType::SUCCESS_FEED &&
102
- res['t'] != Response::ResponseType::SUCCESS_ATOM_FEED) ||
103
- res['r'] != [])
104
- raise RqlRuntimeError, "Server sent malformed STOP response #{PP.pp(res, "")}"
105
- end
106
- return true
408
+ @conn.register_query(@token, @opts)
409
+ @conn.dispatch([Query::QueryType::CONTINUE], @token)
107
410
  end
108
411
  end
109
412
 
110
- def fetch_batch
111
- @conn.register_query(@token, @opts)
112
- @conn.dispatch([Query::QueryType::CONTINUE], @token)
413
+ def next(wait=true)
414
+ if @run
415
+ raise RqlRuntimeError, "Cannot call `next` on a cursor after calling `each`."
416
+ end
417
+ if @more && out_of_date
418
+ raise RqlRuntimeError, "Connection is closed."
419
+ end
420
+ timeout = wait
421
+ if wait == true
422
+ timeout = nil
423
+ elsif !wait
424
+ timeout = 0
425
+ end
426
+
427
+ while @results.length == 0
428
+ raise StopIteration if !@more
429
+ wait_for_batch(timeout)
430
+ end
431
+
432
+ @results.shift
113
433
  end
114
434
  end
115
435
 
@@ -127,11 +447,14 @@ module RethinkDB
127
447
  @abort_module = Faux_Abort
128
448
  end
129
449
 
130
- opts = {:host => opts} if opts.class == String
450
+ opts = Hash[opts.map{|(k,v)| [k.to_sym,v]}] if opts.is_a?(Hash)
451
+ opts = {:host => opts} if opts.is_a?(String)
131
452
  @host = opts[:host] || "localhost"
132
- @port = opts[:port] || 28015
453
+ @port = (opts[:port] || 28015).to_i
133
454
  @default_db = opts[:db]
134
455
  @auth_key = opts[:auth_key] || ""
456
+ @timeout = opts[:timeout].to_i
457
+ @timeout = 20 if @timeout <= 0
135
458
 
136
459
  @@last = self
137
460
  @default_opts = @default_db ? {:db => RQL.new.db(@default_db)} : {}
@@ -140,7 +463,7 @@ module RethinkDB
140
463
  @token_cnt = 0
141
464
  @token_cnt_mutex = Mutex.new
142
465
 
143
- self.connect()
466
+ connect()
144
467
  end
145
468
  attr_reader :host, :port, :default_db, :conn_id
146
469
 
@@ -148,12 +471,13 @@ module RethinkDB
148
471
  @token_cnt_mutex.synchronize{@token_cnt += 1}
149
472
  end
150
473
 
151
- def register_query(token, opts)
474
+ def register_query(token, opts, callback=nil)
152
475
  if !opts[:noreply]
153
- @listener_mutex.synchronize{
154
- raise RqlDriverError, "Internal driver error, token already in use." \
155
- if @waiters.has_key?(token)
156
- @waiters[token] = ConditionVariable.new
476
+ @mon.synchronize {
477
+ if @waiters.has_key?(token)
478
+ raise RqlDriverError, "Internal driver error, token already in use."
479
+ end
480
+ @waiters[token] = callback ? callback : @mon.new_cond
157
481
  @opts[token] = opts
158
482
  }
159
483
  end
@@ -161,11 +485,17 @@ module RethinkDB
161
485
  def run_internal(q, opts, token)
162
486
  register_query(token, opts)
163
487
  dispatch(q, token)
164
- opts[:noreply] ? nil : wait(token)
488
+ opts[:noreply] ? nil : wait(token, nil)
489
+ end
490
+ def stop(token)
491
+ dispatch([Query::QueryType::STOP], token)
492
+ @mon.synchronize {
493
+ !!@waiters.delete(token)
494
+ }
165
495
  end
166
- def run(msg, opts, &b)
167
- reconnect(:noreply_wait => false) if @auto_reconnect && !self.is_open()
168
- raise RqlRuntimeError, "Connection is closed." if !self.is_open()
496
+ def run(msg, opts, b)
497
+ reconnect(:noreply_wait => false) if @auto_reconnect && !is_open()
498
+ raise RqlRuntimeError, "Connection is closed." if !is_open()
169
499
 
170
500
  global_optargs = {}
171
501
  all_opts = @default_opts.merge(opts)
@@ -177,40 +507,45 @@ module RethinkDB
177
507
  q = [Query::QueryType::START,
178
508
  msg,
179
509
  Hash[all_opts.map {|k,v|
180
- [k.to_s, (v.class == RQL ? v.to_pb : RQL.new.expr(v).to_pb)]
510
+ [k.to_s, (v.is_a?(RQL) ? v.to_pb : RQL.new.expr(v).to_pb)]
181
511
  }]]
182
512
 
183
- res = run_internal(q, all_opts, token)
184
- return res if !res
185
- if res['t'] == Response::ResponseType::SUCCESS_PARTIAL ||
186
- res['t'] == Response::ResponseType::SUCCESS_FEED ||
187
- res['t'] == Response::ResponseType::SUCCESS_ATOM_FEED
188
- value = Cursor.new(Shim.response_to_native(res, msg, opts),
189
- msg, self, opts, token, true)
190
- elsif res['t'] == Response::ResponseType::SUCCESS_SEQUENCE
191
- value = Cursor.new(Shim.response_to_native(res, msg, opts),
192
- msg, self, opts, token, false)
513
+ if b.is_a? Handler
514
+ callback = QueryHandle.new(b, msg, all_opts, token, self)
515
+ register_query(token, all_opts, callback)
516
+ dispatch(q, token)
517
+ return callback
193
518
  else
194
- value = Shim.response_to_native(res, msg, opts)
195
- end
519
+ res = run_internal(q, all_opts, token)
520
+ return res if !res
521
+ if res['t'] == Response::ResponseType::SUCCESS_PARTIAL
522
+ value = Cursor.new(Shim.response_to_native(res, msg, opts),
523
+ msg, self, opts, token, true)
524
+ elsif res['t'] == Response::ResponseType::SUCCESS_SEQUENCE
525
+ value = Cursor.new(Shim.response_to_native(res, msg, opts),
526
+ msg, self, opts, token, false)
527
+ else
528
+ value = Shim.response_to_native(res, msg, opts)
529
+ end
196
530
 
197
- if res['p']
198
- real_val = {
199
- "profile" => res['p'],
200
- "value" => value
201
- }
202
- else
203
- real_val = value
204
- end
531
+ if res['p']
532
+ real_val = {
533
+ "profile" => res['p'],
534
+ "value" => value
535
+ }
536
+ else
537
+ real_val = value
538
+ end
205
539
 
206
- if b
207
- begin
208
- b.call(real_val)
209
- ensure
210
- value.close if value.class == Cursor
540
+ if b
541
+ begin
542
+ b.call(real_val)
543
+ ensure
544
+ value.close if value.is_a?(Cursor)
545
+ end
546
+ else
547
+ real_val
211
548
  end
212
- else
213
- real_val
214
549
  end
215
550
  end
216
551
 
@@ -232,21 +567,38 @@ module RethinkDB
232
567
  return token
233
568
  end
234
569
 
235
- def wait(token)
570
+ def wait(token, timeout)
236
571
  begin
237
- res = nil
238
- @listener_mutex.synchronize {
239
- raise RqlRuntimeError, "Connection is closed." if !@waiters.has_key?(token)
240
- res = @data.delete(token)
241
- if res == nil
242
- @waiters[token].wait(@listener_mutex)
572
+ @mon.synchronize {
573
+ end_time = timeout ? Time.now.to_f + timeout : nil
574
+ loop {
243
575
  res = @data.delete(token)
244
- end
245
- @waiters.delete(token)
576
+ return res if res
577
+
578
+ # Theoretically we only need to check the second property,
579
+ # but this is safer in case someone makes changes to
580
+ # `close` in the future.
581
+ if !is_open() || !@waiters.has_key?(token)
582
+ raise RqlRuntimeError, "Connection is closed."
583
+ end
584
+
585
+ if end_time
586
+ cur_time = Time.now.to_f
587
+ if cur_time >= end_time
588
+ raise Timeout::Error, "Timed out waiting for cursor response."
589
+ else
590
+ # We can't use `wait_while` because it doesn't take a
591
+ # timeout, and we can't use an external `timeout {
592
+ # ... }` block because in Ruby 1.9.1 it seems to confuse
593
+ # the synchronization in `@mon` to be timed out while
594
+ # waiting in a synchronize block.
595
+ @waiters[token].wait(end_time - cur_time)
596
+ end
597
+ else
598
+ @waiters[token].wait
599
+ end
600
+ }
246
601
  }
247
- raise RqlRuntimeError, "Connection is closed." if res.nil? && !self.is_open()
248
- raise RqlDriverError, "Internal driver error, no response found." if res.nil?
249
- return res
250
602
  rescue @abort_module::Abort => e
251
603
  print "\nAborting query and reconnecting...\n"
252
604
  reconnect(:noreply_wait => false)
@@ -263,12 +615,12 @@ module RethinkDB
263
615
  def inspect
264
616
  db = @default_opts[:db] || RQL.new.db('test')
265
617
  properties = "(#{@host}:#{@port}) (Default DB: #{db.inspect})"
266
- state = self.is_open() ? "(open)" : "(closed)"
267
- "#<RethinkDB::Connection:#{self.object_id} #{properties} #{state}>"
618
+ state = is_open() ? "(open)" : "(closed)"
619
+ "#<RethinkDB::Connection:#{object_id} #{properties} #{state}>"
268
620
  end
269
621
 
270
622
  @@last = nil
271
- @@magic_number = VersionDummy::Version::V0_3
623
+ @@magic_number = VersionDummy::Version::V0_4
272
624
  @@wire_protocol = VersionDummy::Protocol::JSON
273
625
 
274
626
  def debug_socket; @socket; end
@@ -278,14 +630,15 @@ module RethinkDB
278
630
  # enumerables on the client.
279
631
  def reconnect(opts={})
280
632
  raise ArgumentError, "Argument to reconnect must be a hash." if opts.class != Hash
281
- self.close(opts)
282
- self.connect()
633
+ close(opts)
634
+ connect()
283
635
  end
284
636
 
285
637
  def connect()
286
638
  raise RuntimeError, "Connection must be closed before calling connect." if @socket
287
639
  @socket = TCPSocket.open(@host, @port)
288
- @listener_mutex = Mutex.new
640
+ @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
641
+ @mon = Monitor.new
289
642
  @waiters = {}
290
643
  @opts = {}
291
644
  @data = {}
@@ -294,11 +647,15 @@ module RethinkDB
294
647
  self
295
648
  end
296
649
 
297
- def is_open()
650
+ def is_open
298
651
  @socket && @listener
299
652
  end
653
+ def closed?
654
+ !is_open
655
+ end
300
656
 
301
657
  def close(opts={})
658
+ EM_Guard.unregister(self)
302
659
  raise ArgumentError, "Argument to close must be a hash." if opts.class != Hash
303
660
  if !(opts.keys - [:noreply_wait]).empty?
304
661
  raise ArgumentError, "close does not understand these options: " +
@@ -306,7 +663,22 @@ module RethinkDB
306
663
  end
307
664
  opts[:noreply_wait] = true if !opts.keys.include?(:noreply_wait)
308
665
 
309
- self.noreply_wait() if opts[:noreply_wait] && self.is_open()
666
+ @mon.synchronize {
667
+ @opts.clear
668
+ @data.clear
669
+ @waiters.each {|k,v|
670
+ case v
671
+ when QueryHandle
672
+ v.handle_close
673
+ when MonitorMixin::ConditionVariable
674
+ @waiters[k] = nil
675
+ v.signal
676
+ end
677
+ }
678
+ @waiters.clear
679
+ }
680
+
681
+ noreply_wait() if opts[:noreply_wait] && is_open()
310
682
  if @listener
311
683
  @listener.terminate
312
684
  @listener.join
@@ -314,17 +686,11 @@ module RethinkDB
314
686
  @socket.close if @socket
315
687
  @listener = nil
316
688
  @socket = nil
317
- @listener_mutex.synchronize {
318
- @opts.clear
319
- @data.clear
320
- @waiters.values.each{ |w| w.signal }
321
- @waiters.clear
322
- }
323
689
  self
324
690
  end
325
691
 
326
692
  def noreply_wait
327
- raise RqlRuntimeError, "Connection is closed." if !self.is_open()
693
+ raise RqlRuntimeError, "Connection is closed." if !is_open()
328
694
  q = [Query::QueryType::NOREPLY_WAIT]
329
695
  res = run_internal(q, {noreply: false}, new_token)
330
696
  if res['t'] != Response::ResponseType::WAIT_COMPLETE
@@ -338,12 +704,31 @@ module RethinkDB
338
704
  raise RqlRuntimeError, "No last connection. Use RethinkDB::Connection.new."
339
705
  end
340
706
 
707
+ def remove_em_waiters
708
+ @mon.synchronize {
709
+ @waiters.each {|k,v|
710
+ if v.is_a? QueryHandle
711
+ v.handle_close
712
+ @waiters.delete(k)
713
+ end
714
+ }
715
+ }
716
+ end
717
+
341
718
  def note_data(token, data) # Synchronize around this!
342
- raise RqlDriverError, "Unknown token in response." if !@waiters.has_key?(token)
343
- @data[token] = data
344
719
  @opts.delete(token)
345
- w = @waiters[token]
346
- w.signal if w
720
+ w = @waiters.delete(token)
721
+ case w
722
+ when MonitorMixin::ConditionVariable
723
+ @data[token] = data
724
+ w.signal
725
+ when QueryHandle
726
+ w.callback(data)
727
+ when nil
728
+ # nothing
729
+ else
730
+ raise RqlDriverError, "Unrecognized value #{w.inspect} in `@waiters`."
731
+ end
347
732
  end
348
733
 
349
734
  def note_error(token, e) # Synchronize around this!
@@ -374,15 +759,16 @@ module RethinkDB
374
759
  @auth_key + [@@wire_protocol].pack('L<'))
375
760
  response = ""
376
761
  while response[-1..-1] != "\0"
377
- response += @socket.read_exn(1, 20)
762
+ response += @socket.read_exn(1, @timeout)
378
763
  end
379
764
  response = response[0...-1]
380
765
  if response != "SUCCESS"
381
766
  raise RqlRuntimeError, "Server dropped connection with message: \"#{response}\""
382
767
  end
383
768
 
384
- raise RqlDriverError, "Internal driver error, listener already started." \
385
- if @listener
769
+ if @listener
770
+ raise RqlDriverError, "Internal driver error, listener already started."
771
+ end
386
772
  @listener = Thread.new {
387
773
  while true
388
774
  begin
@@ -396,9 +782,9 @@ module RethinkDB
396
782
  raise RqlRuntimeError, "Bad response, server is buggy.\n" +
397
783
  "#{e.inspect}\n" + response
398
784
  end
399
- @listener_mutex.synchronize{note_data(token, data)}
785
+ @mon.synchronize{note_data(token, data)}
400
786
  rescue Exception => e
401
- @listener_mutex.synchronize {
787
+ @mon.synchronize {
402
788
  @waiters.keys.each{ |k| note_error(k, e) }
403
789
  @listener = nil
404
790
  Thread.current.terminate
data/lib/ql2.pb.rb CHANGED
@@ -7,6 +7,7 @@ module RethinkDB
7
7
  V0_1 = 1063369270
8
8
  V0_2 = 1915781601
9
9
  V0_3 = 1601562686
10
+ V0_4 = 1074539808
10
11
  end
11
12
 
12
13
  module Protocol
@@ -42,13 +43,19 @@ module RethinkDB
42
43
  SUCCESS_ATOM = 1
43
44
  SUCCESS_SEQUENCE = 2
44
45
  SUCCESS_PARTIAL = 3
45
- SUCCESS_FEED = 5
46
46
  WAIT_COMPLETE = 4
47
- SUCCESS_ATOM_FEED = 6
48
47
  CLIENT_ERROR = 16
49
48
  COMPILE_ERROR = 17
50
49
  RUNTIME_ERROR = 18
51
50
  end
51
+
52
+ module ResponseNote
53
+ SEQUENCE_FEED = 1
54
+ ATOM_FEED = 2
55
+ ORDER_BY_LIMIT_FEED = 3
56
+ UNIONED_FEED = 4
57
+ INCLUDES_STATES = 5
58
+ end
52
59
  end
53
60
 
54
61
  module Datum
@@ -103,7 +110,7 @@ module RethinkDB
103
110
  SLICE = 30
104
111
  SKIP = 70
105
112
  LIMIT = 71
106
- INDEXES_OF = 87
113
+ OFFSETS_OF = 87
107
114
  CONTAINS = 93
108
115
  GET_FIELD = 31
109
116
  KEYS = 94
@@ -113,7 +120,8 @@ module RethinkDB
113
120
  PLUCK = 33
114
121
  WITHOUT = 34
115
122
  MERGE = 35
116
- BETWEEN = 36
123
+ BETWEEN_DEPRECATED = 36
124
+ BETWEEN = 182
117
125
  REDUCE = 37
118
126
  MAP = 38
119
127
  FILTER = 39
@@ -160,8 +168,8 @@ module RethinkDB
160
168
  INDEX_RENAME = 156
161
169
  FUNCALL = 64
162
170
  BRANCH = 65
163
- ANY = 66
164
- ALL = 67
171
+ OR = 66
172
+ AND = 67
165
173
  FOR_EACH = 68
166
174
  FUNC = 69
167
175
  ASC = 73
@@ -237,6 +245,8 @@ module RethinkDB
237
245
  FILL = 167
238
246
  GET_NEAREST = 168
239
247
  POLYGON_SUB = 171
248
+ MINVAL = 180
249
+ MAXVAL = 181
240
250
  end
241
251
 
242
252
  module AssocPair
data/lib/rpp.rb CHANGED
@@ -102,18 +102,19 @@ module RethinkDB
102
102
  args[0][0] != Term::TermType::DB)
103
103
  return false
104
104
  else
105
- return !["db", "db_create", "db_drop", "json", "funcall", "args", "branch", "http",
106
- "binary", "javascript", "random", "time", "iso8601", "epoch_time", "now",
107
- "geojson", "point", "circle", "line", "polygon", "asc", "desc", "literal",
108
- "range"].include?(name)
105
+ return !["db", "db_create", "db_drop", "json", "funcall",
106
+ "args", "branch", "http", "binary", "javascript", "random",
107
+ "time", "iso8601", "epoch_time", "now", "geojson", "point",
108
+ "circle", "line", "polygon", "asc", "desc", "literal",
109
+ "range", "error"].include?(name)
109
110
  end
110
111
  end
111
112
  def self.pp_int(q, term, bt, pre_dot=false)
112
113
  q.text("\x7", 0) if bt == []
113
114
 
114
- term = term.to_pb if term.class == RQL
115
+ term = term.to_pb if term.is_a?(RQL)
115
116
  if term.class != Array
116
- if term.class == Hash
117
+ if term.is_a?(Hash)
117
118
  if not pp_pseudotype(q, term, bt)
118
119
  pp_int_optargs(q, term, bt, pre_dot)
119
120
  end
@@ -178,7 +179,7 @@ module RethinkDB
178
179
  q.text("r")
179
180
  arg_offset = 0
180
181
  end
181
- if name == "getattr"
182
+ if name == "bracket"
182
183
  argstart, argstop = "[", "]"
183
184
  else
184
185
  q.text(".")
@@ -186,7 +187,7 @@ module RethinkDB
186
187
  argstart, argstop = "(", ")"
187
188
  end
188
189
 
189
- if args[-1] && args[-1].class == Array && args[-1][0] == Term::TermType::FUNC
190
+ if args[-1] && args[-1].is_a?(Array) && args[-1][0] == Term::TermType::FUNC
190
191
  func_bt = bt_consume(bt, args.size() - 1 + arg_offset)
191
192
  func = args.pop
192
193
  end
data/lib/shim.rb CHANGED
@@ -60,8 +60,6 @@ module RethinkDB
60
60
  begin
61
61
  case r['t']
62
62
  when rt::SUCCESS_ATOM then r['r'][0]
63
- when rt::SUCCESS_FEED then r['r']
64
- when rt::SUCCESS_ATOM_FEED then r['r']
65
63
  when rt::SUCCESS_PARTIAL then r['r']
66
64
  when rt::SUCCESS_SEQUENCE then r['r']
67
65
  when rt::RUNTIME_ERROR then raise RqlRuntimeError, r['r'][0]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rethinkdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.16.0.1
4
+ version: 2.0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - RethinkDB Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-19 00:00:00.000000000 Z
11
+ date: 2015-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json