rethinkdb 1.16.0.1 → 2.0.0.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.
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