rethinkdb 1.12.0.2 → 1.13.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +4 -4
  2. data/lib/func.rb +19 -19
  3. data/lib/net.rb +96 -87
  4. data/lib/ql2.pb.rb +219 -323
  5. data/lib/rethinkdb.rb +3 -43
  6. data/lib/rpp.rb +108 -73
  7. data/lib/shim.rb +75 -189
  8. metadata +4 -32
@@ -1,7 +1,6 @@
1
1
  # Copyright 2010-2012 RethinkDB, all rights reserved.
2
2
  require 'rubygems'
3
3
  require 'ql2.pb.rb'
4
-
5
4
  require 'socket'
6
5
  require 'pp'
7
6
 
@@ -11,45 +10,6 @@ load 'shim.rb'
11
10
  load 'func.rb'
12
11
  load 'rpp.rb'
13
12
 
14
- class Term::AssocPair
15
- def deep_dup
16
- dup.tap { |pair| pair.val = pair.val.deep_dup }
17
- end
18
- end
19
-
20
- class Term
21
- attr_accessor :is_error
22
-
23
- def deep_dup
24
- dup.tap {|term|
25
- term.args.map!(&:deep_dup)
26
- term.optargs.map!(&:deep_dup)
27
- }
28
- end
29
-
30
- def bt_tag bt
31
- @is_error = true
32
- begin
33
- return if bt == []
34
- frame, sub_bt = bt[0], bt [1..-1]
35
- if frame.class == String
36
- optargs.each {|optarg|
37
- if optarg.key == frame
38
- @is_error = false
39
- optarg.val.bt_tag(sub_bt)
40
- end
41
- }
42
- else
43
- @is_error = false
44
- args[frame].bt_tag(sub_bt)
45
- end
46
- rescue StandardError => e
47
- @is_error = true
48
- $stderr.puts "ERROR: Failed to parse backtrace (we'll take our best guess)."
49
- end
50
- end
51
- end
52
-
53
13
  module RethinkDB
54
14
  module Shortcuts
55
15
  def r(*args)
@@ -74,17 +34,17 @@ module RethinkDB
74
34
 
75
35
  attr_accessor :body, :bitop
76
36
 
77
- def initialize(body = nil, bitop = nil)
37
+ def initialize(body = RQL, bitop = nil)
78
38
  @body = body
79
39
  @bitop = bitop
80
40
  end
81
41
 
82
42
  def pp
83
- unbound_if !@body
43
+ unbound_if(@body == RQL)
84
44
  RethinkDB::RPP.pp(@body)
85
45
  end
86
46
  def inspect
87
- @body ? pp : super
47
+ (@body != RQL) ? pp : super
88
48
  end
89
49
  end
90
50
  end
data/lib/rpp.rb CHANGED
@@ -3,30 +3,33 @@ require 'prettyprint'
3
3
 
4
4
  module RethinkDB
5
5
  module RPP
6
- @@termtype_to_str = Hash[
7
- Term::TermType.constants.map{|x| [Term::TermType.const_get(x), x.to_s]}
8
- ]
6
+ @@termtype_to_str =
7
+ Hash[Term::TermType.constants.map{|x| [Term::TermType.const_get(x), x.to_s]}]
9
8
  @@regex = if __FILE__ =~ /^(.*\/)[^\/]+.rb$/ then /^#{$1}/ else nil end
10
9
 
11
- def self.pp_int_optargs(q, optargs, pre_dot = false)
10
+ def self.bt_consume(bt, el)
11
+ (bt && bt[0] == el) ? bt[1..-1] : nil
12
+ end
13
+
14
+ def self.pp_int_optargs(q, optargs, bt, pre_dot = false)
12
15
  q.text("r(") if pre_dot
13
16
  q.group(1, "{", "}") {
14
- optargs.each_with_index {|optarg, i|
17
+ optargs.to_a.each_with_index {|optarg, i|
15
18
  if i != 0
16
19
  q.text(",")
17
20
  q.breakable
18
21
  end
19
- q.text(":#{optarg.key} =>")
22
+ q.text("#{optarg[0].inspect} =>")
20
23
  q.nest(2) {
21
24
  q.breakable
22
- pp_int(q, optarg.val)
25
+ pp_int(q, optarg[1], bt_consume(bt, optarg[0]))
23
26
  }
24
27
  }
25
28
  }
26
29
  q.text(")") if pre_dot
27
30
  end
28
31
 
29
- def self.pp_int_args(q, args, pre_dot = false)
32
+ def self.pp_int_args(q, args, bt, pre_dot = false)
30
33
  q.text("r(") if pre_dot
31
34
  q.group(1, "[", "]") {
32
35
  args.each_with_index {|arg, i|
@@ -34,7 +37,7 @@ module RethinkDB
34
37
  q.text(",")
35
38
  q.breakable
36
39
  end
37
- pp_int(q, arg)
40
+ pp_int(q, arg, bt_consume(bt, i))
38
41
  }
39
42
  }
40
43
  q.text(")") if pre_dot
@@ -42,70 +45,104 @@ module RethinkDB
42
45
 
43
46
  def self.pp_int_datum(q, dat, pre_dot)
44
47
  q.text("r(") if pre_dot
45
- q.text(Shim.datum_to_native(dat, :time_format => 'raw').inspect)
48
+ q.text(dat.inspect)
46
49
  q.text(")") if pre_dot
47
50
  end
48
51
 
49
- def self.pp_int_func(q, func)
50
- func_args = func.args[0].args.map{|x| x.datum.r_num}
51
- func_body = func.args[1]
52
- q.text(" ")
53
- q.group(0, "{", "}") {
54
- if func_args != []
55
- q.text(func_args.map{|x| :"var_#{x}"}.inspect.gsub(/\[|\]/,"|").gsub(":",""))
56
- end
57
- q.nest(2) {
58
- q.breakable
59
- pp_int(q, func_body)
52
+ def self.pp_int_func(q, func, bt)
53
+ # PP.pp [:func, func.to_json, bt]
54
+ begin
55
+ # PP.pp func
56
+ func_args = func[1][0][1].map{|x| x.to_pb}
57
+ # PP.pp JSON.parse(func_args.to_json)
58
+ func_body = func[1][1]
59
+ q.text(" ")
60
+ q.group(0, "{", "}") {
61
+ if func_args != []
62
+ q.text(func_args.map{|x| :"var_#{x}"}.inspect.gsub(/\[|\]/,"|").gsub(":",""))
63
+ end
64
+ q.nest(2) {
65
+ q.breakable
66
+ pp_int(q, func_body, bt_consume(bt, 1))
67
+ }
68
+ q.breakable('')
60
69
  }
61
- q.breakable('')
62
- }
70
+ rescue StandardError => e
71
+ q.text(" {#<unprintable function:`#{func}`>}")
72
+ end
63
73
  end
64
74
 
65
75
  def self.can_prefix (name, args)
66
- return false if name == "db" || name == "funcall"
67
- return false if args.size == 1 && args[0].type == Term::TermType::DATUM
68
- return true
76
+ return !["db", "table", "funcall", "args", "branch"].include?(name)
69
77
  end
70
- def self.pp_int(q, term, pre_dot=false)
71
- q.text("\x7", 0) if term.is_error
78
+ def self.pp_int(q, term, bt, pre_dot=false)
79
+ q.text("\x7", 0) if bt == []
72
80
 
73
- if term.type == Term::TermType::DATUM
74
- res = pp_int_datum(q, term.datum, pre_dot)
75
- q.text("\x7", 0) if term.is_error
76
- return res
81
+ term = term.to_pb if term.class == RQL
82
+ # PP.pp [:pp_int, term.to_json, bt]
83
+ if term.class != Array
84
+ if term.class == Hash
85
+ pp_int_optargs(q, term, bt, pre_dot)
86
+ else
87
+ pp_int_datum(q, term, pre_dot)
88
+ end
89
+ q.text("\x7", 0) if bt == []
90
+ return
77
91
  end
78
92
 
79
- if term.type == Term::TermType::VAR
93
+ type = term[0]
94
+ args = (term[1] || []).dup
95
+ optargs = (term[2] || {}).dup
96
+ if type == Term::TermType::VAR
80
97
  q.text("var_")
81
- res = pp_int_datum(q, term.args[0].datum, false)
82
- q.text("\x7", 0) if term.is_error
83
- return res
84
- elsif term.type == Term::TermType::FUNC
98
+ res = pp_int_datum(q, args[0], false)
99
+ q.text("\x7", 0) if bt == []
100
+ return
101
+ elsif type == Term::TermType::FUNC
85
102
  q.text("r(") if pre_dot
86
103
  q.text("lambda")
87
- res = pp_int_func(q, term)
104
+ pp_int_func(q, term, bt)
88
105
  q.text(")") if pre_dot
89
- q.text("\x7", 0) if term.is_error
90
- return res
91
- elsif term.type == Term::TermType::MAKE_OBJ
92
- res = pp_int_optargs(q, term.optargs, pre_dot)
93
- q.text("\x7", 0) if term.is_error
94
- return res
95
- elsif term.type == Term::TermType::MAKE_ARRAY
96
- res = pp_int_args(q, term.args, pre_dot)
97
- q.text("\x7", 0) if term.is_error
98
- return res
106
+ q.text("\x7", 0) if bt == []
107
+ return
108
+ elsif type == Term::TermType::MAKE_ARRAY
109
+ pp_int_args(q, args, bt, pre_dot)
110
+ q.text("\x7", 0) if bt == []
111
+ return
112
+ elsif type == Term::TermType::FUNCALL
113
+ func = (args[0][0] == Term::TermType::FUNC) ? args[0] : nil
114
+ if args.size == 2
115
+ pp_int(q, args[1], bt_consume(bt, 1), pre_dot)
116
+ q.text(".do")
117
+ if !func
118
+ q.text("(")
119
+ pp_int(q, args[0], bt_consume(bt, 0)) if !func
120
+ q.text(")")
121
+ end
122
+ else
123
+ q.text("r.do(")
124
+ (1...args.size).each {|i|
125
+ q.text(", ") if i != 1
126
+ pp_int(q, args[i], bt_consume(bt, i))
127
+ }
128
+ if !func
129
+ q.text(", ")
130
+ pp_int(q, args[0], bt_consume(bt, 0))
131
+ end
132
+ q.text(")")
133
+ end
134
+ pp_int_func(q, args[0], bt_consume(bt, 0)) if func
135
+ return
99
136
  end
100
137
 
101
- name = @@termtype_to_str[term.type].downcase
102
- args = term.args.dup
103
- optargs = term.optargs.dup
138
+ name = @@termtype_to_str[type].downcase
104
139
 
105
140
  if can_prefix(name, args) && first_arg = args.shift
106
- pp_int(q, first_arg, true)
141
+ pp_int(q, first_arg, bt_consume(bt, 0), true)
142
+ arg_offset = 1
107
143
  else
108
144
  q.text("r")
145
+ arg_offset = 0
109
146
  end
110
147
  if name == "getattr"
111
148
  argstart, argstop = "[", "]"
@@ -115,13 +152,17 @@ module RethinkDB
115
152
  argstart, argstop = "(", ")"
116
153
  end
117
154
 
118
- func = args[-1] && args[-1].type == Term::TermType::FUNC && args.pop
155
+ if args[-1] && args[-1].class == Array && args[-1][0] == Term::TermType::FUNC
156
+ func_bt = bt_consume(bt, args.size() - 1 + arg_offset)
157
+ func = args.pop
158
+ # PP.pp [:func_bt, bt, arg_offset, (args.size() - 1) + arg_offset, func_bt]
159
+ end
119
160
 
120
- if args != [] || optargs != []
161
+ if args != [] || optargs != {}
121
162
  q.group(0, argstart, argstop) {
122
163
  pushed = nil
123
164
  q.nest(2) {
124
- args.each {|arg|
165
+ args.each_with_index {|arg, index|
125
166
  if !pushed
126
167
  pushed = true
127
168
  q.breakable('')
@@ -129,35 +170,30 @@ module RethinkDB
129
170
  q.text(",")
130
171
  q.breakable
131
172
  end
132
- pp_int(q, arg)
173
+ # PP.pp [:int, arg.to_json, bt_consume(bt, index)]
174
+ pp_int(q, arg, bt_consume(bt, index + arg_offset))
133
175
  }
134
- if optargs != []
176
+ if optargs != {}
135
177
  if pushed
136
178
  q.text(",")
137
179
  q.breakable
138
180
  end
139
- pp_int_optargs(q, optargs)
140
- end
141
- if func && name == "grouped_map_reduce"
142
- q.text(",")
143
- q.breakable
144
- q.text("lambda")
145
- pp_int_func(q, func)
146
- func = nil
181
+ pp_int_optargs(q, optargs, bt)
147
182
  end
148
183
  }
149
184
  q.breakable('')
150
185
  }
151
186
  end
152
187
 
153
- pp_int_func(q, func) if func
154
- q.text("\x7", 0) if term.is_error
188
+ pp_int_func(q, func, func_bt) if func
189
+ q.text("\x7", 0) if bt == []
155
190
  end
156
191
 
157
- def self.pp term
192
+ def self.pp(term, bt=nil)
193
+ # PP.pp bt
158
194
  begin
159
195
  q = PrettyPrint.new
160
- pp_int(q, term, true)
196
+ pp_int(q, term, bt, true)
161
197
  q.flush
162
198
 
163
199
  in_bt = false
@@ -165,16 +201,15 @@ module RethinkDB
165
201
  line = line.gsub(/^ */) {|x| x+"\x7"} if in_bt
166
202
  arr = line.split("\x7")
167
203
  if arr[1]
168
- in_bt = !(arr[2] || (line[-1] == 0x7))
204
+ in_bt = !(arr[2] || (line[-1] == "\x7"))
169
205
  [arr.join(""), " "*arr[0].size + "^"*arr[1].size]
170
206
  else
171
207
  line
172
208
  end
173
209
  }.flatten.join("\n")
174
- rescue Exception => e
175
- raise e
210
+ rescue Exception => e
176
211
  "AN ERROR OCCURED DURING PRETTY-PRINTING:\n#{e.inspect}\n" +
177
- "FALLING BACK TO PROTOBUF PRETTY-PRINTER.\n#{@term.inspect}"
212
+ "FALLING BACK TO GENERIC PRINTER.\n#{term.inspect}"
178
213
  end
179
214
  end
180
215
  end
@@ -1,225 +1,111 @@
1
- require 'json'
2
- require 'time'
3
-
4
1
  module RethinkDB
2
+ require 'json'
3
+ require 'time'
5
4
  module Shim
6
- def self.is_reql_time(obj)
7
- obj.is_a? Hash and obj["$reql_type$"] == "TIME"
8
- end
9
-
10
- def self.convert_time(obj)
11
- t = Time.at(obj['epoch_time'])
12
- tz = obj['timezone']
13
- (tz && tz != "" && tz != "Z") ? t.getlocal(tz) : t.utc
14
- end
15
-
16
- def self.is_grouped_data(obj)
17
- obj.is_a? Hash and obj["$reql_type$"] == "GROUPED_DATA"
18
- end
19
-
20
- def self.convert_grouped_data(obj, opts)
21
- convert_reql_types!(obj['data'], opts)
22
- Hash[obj["data"]]
23
- end
24
-
25
- def self.maybe_convert_type(obj, opts)
26
- if opts[:time_format] != 'raw' && is_reql_time(obj)
27
- convert_time(obj)
28
- elsif opts[:group_format] != 'raw' && is_grouped_data(obj)
29
- convert_grouped_data(obj, opts)
30
- else
31
- nil
32
- end
33
- end
34
-
35
- def self.convert_reql_types!(result, opts)
36
- case result
5
+ def self.recursive_munge(x, parse_time, parse_group)
6
+ case x
37
7
  when Hash
38
- result.each {|k,v|
39
- if (new_res = maybe_convert_type(v, opts))
40
- result[k] = new_res
41
- else
42
- convert_reql_types!(v, opts)
43
- end
44
- }
8
+ if parse_time && x['$reql_type$'] == 'TIME'
9
+ t = Time.at(x['epoch_time'])
10
+ tz = x['timezone']
11
+ return (tz && tz != "" && tz != "Z") ? t.getlocal(tz) : t.utc
12
+ elsif parse_group && x['$reql_type$'] == 'GROUPED_DATA'
13
+ return Hash[x['data']]
14
+ else
15
+ x.each {|k, v|
16
+ v2 = recursive_munge(v, parse_time, parse_group)
17
+ x[k] = v2 if v.object_id != v2.object_id
18
+ }
19
+ end
45
20
  when Array
46
- result.each_index {|i|
47
- if (new_res = maybe_convert_type(result[i], opts))
48
- result[i] = new_res;
49
- else
50
- convert_reql_types!(result[i], opts)
51
- end
21
+ x.each_with_index {|v, i|
22
+ v2 = recursive_munge(v, parse_time, parse_group)
23
+ x[i] = v2 if v.object_id != v2.object_id
52
24
  }
53
25
  end
54
- nil
26
+ return x
55
27
  end
56
28
 
57
- def self.postprocess!(result, opts)
58
- maybe_convert_type(result, opts) \
59
- || (convert_reql_types!(result, opts); result)
29
+ def self.load_json(target, opts=nil)
30
+ recursive_munge(JSON.parse(target, max_nesting: false),
31
+ opts && opts[:time_format] != 'raw',
32
+ opts && opts[:group_format] != 'raw')
60
33
  end
61
34
 
62
- def self.datum_to_native(d, opts)
63
- raise RqlRuntimeError, "SHENANIGANS" if d.class != Datum
64
- dt = Datum::DatumType
65
- case d.type
66
- when dt::R_NUM then d.r_num == d.r_num.to_i ? d.r_num.to_i : d.r_num
67
- when dt::R_STR then d.r_str
68
- when dt::R_BOOL then d.r_bool
69
- when dt::R_NULL then nil
70
- when dt::R_ARRAY then d.r_array.map{|d2| datum_to_native(d2, opts)}
71
- when dt::R_OBJECT then
72
- obj = Hash[d.r_object.map{|x| [x.key, datum_to_native(x.val, opts)]}]
73
- postprocess!(obj, opts)
74
- when dt::R_JSON then
75
- postprocess!(JSON.parse("[" + d.r_str + "]")[0], opts)
76
- else raise RqlRuntimeError, "#{dt} Unimplemented."
77
- end
35
+ def self.dump_json(x)
36
+ JSON.generate(x, max_nesting: false)
78
37
  end
79
38
 
80
39
  def self.response_to_native(r, orig_term, opts)
81
40
  rt = Response::ResponseType
82
- if r.backtrace
83
- bt = r.backtrace.frames.map {|x|
84
- x.type == Frame::FrameType::POS ? x.pos : x.opt
85
- }
86
- else
87
- bt = []
88
- end
89
-
90
41
  begin
91
- case r.type
92
- when rt::SUCCESS_ATOM then datum_to_native(r.response[0], opts)
93
- when rt::SUCCESS_PARTIAL then r.response.map{|d| datum_to_native(d, opts)}
94
- when rt::SUCCESS_SEQUENCE then r.response.map{|d| datum_to_native(d, opts)}
95
- when rt::RUNTIME_ERROR then
96
- raise RqlRuntimeError, "#{r.response[0].r_str}"
97
- when rt::COMPILE_ERROR then # TODO: remove?
98
- raise RqlCompileError, "#{r.response[0].r_str}"
99
- when rt::CLIENT_ERROR then
100
- raise RqlDriverError, "#{r.response[0].r_str}"
42
+ case r['t']
43
+ when rt::SUCCESS_ATOM then r['r'][0]
44
+ when rt::SUCCESS_FEED then r['r']
45
+ when rt::SUCCESS_PARTIAL then r['r']
46
+ when rt::SUCCESS_SEQUENCE then r['r']
47
+ when rt::RUNTIME_ERROR then raise RqlRuntimeError, r['r'][0]
48
+ when rt::COMPILE_ERROR then raise RqlCompileError, r['r'][0]
49
+ when rt::CLIENT_ERROR then raise RqlDriverError, r['r'][0]
101
50
  else raise RqlRuntimeError, "Unexpected response: #{r.inspect}"
102
51
  end
103
52
  rescue RqlError => e
104
- term = orig_term.deep_dup
105
- term.bt_tag(bt)
106
- raise e.class, "#{e.message}\nBacktrace:\n#{RPP.pp(term)}"
53
+ raise e.class, "#{e.message}\nBacktrace:\n#{RPP.pp(orig_term, r['b'])}"
107
54
  end
108
55
  end
109
-
110
- def self.native_to_datum_term x
111
- dt = Datum::DatumType
112
- d = Datum.new
113
- case x
114
- when Fixnum then d.type = dt::R_NUM; d.r_num = x
115
- when Float then d.type = dt::R_NUM; d.r_num = x
116
- when Bignum then d.type = dt::R_NUM; d.r_num = x
117
- when String then d.type = dt::R_STR; d.r_str = x
118
- when Symbol then d.type = dt::R_STR; d.r_str = x.to_s
119
- when TrueClass then d.type = dt::R_BOOL; d.r_bool = x
120
- when FalseClass then d.type = dt::R_BOOL; d.r_bool = x
121
- when NilClass then d.type = dt::R_NULL
122
- else raise RqlRuntimeError, "UNREACHABLE"
123
- end
124
- t = Term.new
125
- t.type = Term::TermType::DATUM
126
- t.datum = d
127
- return t
128
- end
129
56
  end
130
57
 
131
58
  class RQL
132
- def to_pb; @body; end
133
-
134
- @@datum_types = [Fixnum, Float, Bignum, String, Symbol,
135
- TrueClass, FalseClass, NilClass]
136
-
137
- def any_to_pb(x)
138
- return x.to_pb if x.class == RQL
139
- t = Term.new
140
- t.type = Term::TermType::JSON
141
- t.args = [Shim.native_to_datum_term(x.to_json(:max_nesting => 500))]
142
- return t
143
- end
144
-
145
- def timezone_from_offset(offset)
146
- raw_offset = offset.abs
147
- raw_hours = raw_offset / 3600
148
- raw_minutes = (raw_offset / 60) - (raw_hours * 60)
149
- return (offset < 0 ? "-" : "+") + sprintf("%02d:%02d", raw_hours, raw_minutes);
59
+ def to_json(*a, &b)
60
+ @body.to_json(*a, &b)
150
61
  end
62
+ def to_pb; @body; end
151
63
 
152
- def fast_expr(x, allow_json)
153
- return x if x.class == RQL
154
- if @@datum_types.include?(x.class)
155
- return x if allow_json
156
- return RQL.new(Shim.native_to_datum_term(x), nil)
157
- end
158
-
64
+ def self.safe_to_s(x)
159
65
  case x
160
- when Array
161
- args = x.map{|y| fast_expr(y, allow_json)}
162
- return x if allow_json && args.all?{|y| y.class != RQL}
163
- t = Term.new
164
- t.type = Term::TermType::MAKE_ARRAY
165
- t.args = args.map{|y| any_to_pb(y)}
166
- return RQL.new(t, nil)
167
- when Hash
168
- kvs = x.map{|k,v| [k, fast_expr(v, allow_json)]}
169
- return x if allow_json && kvs.all? {|k,v|
170
- (k.class == String || k.class == Symbol) && v.class != RQL
171
- }
172
- t = Term.new
173
- t.type = Term::TermType::MAKE_OBJ
174
- t.optargs = kvs.map{|k,v|
175
- ap = Term::AssocPair.new;
176
- if k.class == Symbol || k.class == String
177
- ap.key = k.to_s
178
- else
179
- raise RqlDriverError, "Object keys must be strings or symbols." +
180
- " (Got object `#{k.inspect}` of class `#{k.class}`.)"
181
- end
182
- ap.val = any_to_pb(v)
183
- ap
184
- }
185
- return RQL.new(t, nil)
186
- when Proc
187
- t = RQL.new(nil, nil).new_func(&x).to_pb
188
- return RQL.new(t, nil)
189
- else raise RqlDriverError, "r.expr can't handle #{x.inspect} of type #{x.class}"
66
+ when String then x
67
+ when Symbol then x.to_s
68
+ else raise RqlDriverError, 'Object keys must be strings or symbols. '+
69
+ "(Got object `#{x.inspect}` of class `#{x.class}`.)"
190
70
  end
191
71
  end
192
72
 
193
- def check_depth depth
194
- raise RqlRuntimeError, "Maximum expression depth of 20 exceeded." if depth > 20
195
- end
196
-
197
- def reql_typify(tree, depth=0)
198
- check_depth(depth)
199
- case tree
200
- when Array
201
- return tree.map{|x| reql_typify(x, depth+1)}
202
- when Hash
203
- return Hash[tree.map{|k,v| [k, reql_typify(v, depth+1)]}]
204
- when Time
205
- return {
206
- '$reql_type$' => 'TIME',
207
- 'epoch_time' => tree.to_f,
208
- 'timezone' => timezone_from_offset(tree.utc_offset)
209
- }
210
- else
211
- return tree
73
+ def self.fast_expr(x, max_depth)
74
+ if max_depth == 0
75
+ raise RqlDriverError, "Maximum expression depth exceeded " +
76
+ "(you can override this with `r.expr(X, MAX_DEPTH)`)."
77
+ end
78
+ case x
79
+ when RQL then x
80
+ when Array then RQL.new([Term::TermType::MAKE_ARRAY,
81
+ x.map{|y| fast_expr(y, max_depth-1)}])
82
+ when Hash then RQL.new(Hash[x.map{|k,v| [safe_to_s(k),
83
+ fast_expr(v, max_depth-1)]}])
84
+ when Proc then RQL.new.new_func(&x)
85
+ when String then RQL.new(x)
86
+ when Symbol then RQL.new(x)
87
+ when Numeric then RQL.new(x)
88
+ when FalseClass then RQL.new(x)
89
+ when TrueClass then RQL.new(x)
90
+ when NilClass then RQL.new(x)
91
+ when Time then
92
+ epoch_time = x.to_f
93
+ offset = x.utc_offset
94
+ raw_offset = offset.abs
95
+ raw_hours = raw_offset / 3600
96
+ raw_minutes = (raw_offset / 60) - (raw_hours * 60)
97
+ tz = (offset < 0 ? "-" : "+") + sprintf("%02d:%02d", raw_hours, raw_minutes);
98
+ RQL.new({ '$reql_type$' => 'TIME',
99
+ 'epoch_time' => epoch_time,
100
+ 'timezone' => tz })
101
+ else raise RqlDriverError, "r.expr can't handle #{x.inspect} of class #{x.class}."
212
102
  end
213
103
  end
214
104
 
215
- def expr(x, opts={})
216
- allow_json = opts[:allow_json]
217
- unbound_if @body
218
- res = fast_expr(reql_typify(x), allow_json)
219
- return res if res.class == RQL
220
- return RQL.new(any_to_pb(res), nil)
105
+ def expr(x, max_depth=20)
106
+ unbound_if(@body != RQL)
107
+ RQL.fast_expr(x, max_depth)
221
108
  end
222
-
223
109
  def coerce(other)
224
110
  [RQL.new.expr(other), self]
225
111
  end