rethinkdb 1.12.0.2 → 1.13.0.0

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.
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