nobrainer 0.18.1 → 0.19.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.
- checksums.yaml +4 -4
- data/lib/no_brainer/connection.rb +1 -1
- data/lib/no_brainer/criteria/core.rb +1 -1
- data/lib/no_brainer/criteria/delete.rb +1 -1
- data/lib/no_brainer/criteria/order_by.rb +4 -2
- data/lib/no_brainer/criteria/where.rb +166 -109
- data/lib/no_brainer/document/aliases.rb +10 -2
- data/lib/no_brainer/document/association/belongs_to.rb +3 -3
- data/lib/no_brainer/document/association/core.rb +1 -1
- data/lib/no_brainer/document/association/eager_loader.rb +1 -1
- data/lib/no_brainer/document/association/has_many.rb +2 -2
- data/lib/no_brainer/document/atomic_ops.rb +29 -30
- data/lib/no_brainer/document/attributes.rb +15 -19
- data/lib/no_brainer/document/callbacks.rb +1 -1
- data/lib/no_brainer/document/id.rb +7 -3
- data/lib/no_brainer/document/index.rb +20 -10
- data/lib/no_brainer/document/persistance.rb +11 -10
- data/lib/no_brainer/document/timestamps.rb +4 -2
- data/lib/no_brainer/document/types.rb +0 -9
- data/lib/no_brainer/document/types/binary.rb +0 -1
- data/lib/no_brainer/document/types/boolean.rb +0 -1
- data/lib/no_brainer/document/uniqueness.rb +0 -1
- data/lib/no_brainer/document/validation.rb +5 -5
- data/lib/no_brainer/error.rb +1 -0
- data/lib/no_brainer/query_runner/connection_lock.rb +1 -1
- data/lib/no_brainer/query_runner/reconnect.rb +9 -11
- data/lib/no_brainer/query_runner/table_on_demand.rb +1 -1
- data/lib/no_brainer/railtie/database.rake +2 -2
- data/lib/no_brainer/rql.rb +1 -1
- data/lib/nobrainer.rb +1 -6
- data/lib/rails/generators/nobrainer.rb +1 -1
- metadata +18 -5
- data/lib/no_brainer/decorated_symbol.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5dc33c00dc3aa418ae3bd36b0ccb2618a8c0e47f
|
4
|
+
data.tar.gz: 3a4c171e4c41341d14a3c4b6103b463be1b12701
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70af56ece8a93103f310a729c338631e0bc56b52d4725ea2a304dfdb389e04fb530fda2b3d8039ec36e4f175ae9d510f3eb765fdf2d7a6f155ea37b822f0f464
|
7
|
+
data.tar.gz: 58371614f47e835c11b183fd2b004271b7c0031f27de8b17a9f8b0d3d2c4ee185a4e32efb5e495f5f6cf0b53fa9f64e4510150cfff3305d237431ec195733b13
|
@@ -50,7 +50,7 @@ class NoBrainer::Connection
|
|
50
50
|
|
51
51
|
# Note that truncating each table (purge) is much faster than dropping the
|
52
52
|
# database (drop)
|
53
|
-
def purge!
|
53
|
+
def purge!
|
54
54
|
table_list.each do |table_name|
|
55
55
|
next if table_name =~ /^nobrainer_/
|
56
56
|
NoBrainer.run { |r| r.table(table_name).delete }
|
@@ -65,7 +65,9 @@ module NoBrainer::Criteria::OrderBy
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def first_key_indexable?
|
68
|
-
|
68
|
+
return false unless first_key.is_a?(Symbol) || first_key.is_a?(String)
|
69
|
+
return false unless index = criteria.model.indexes[first_key.to_sym]
|
70
|
+
return !index.multi && !index.geo
|
69
71
|
end
|
70
72
|
|
71
73
|
def find_index
|
@@ -87,7 +89,7 @@ module NoBrainer::Criteria::OrderBy
|
|
87
89
|
|
88
90
|
def order_by_index_finder
|
89
91
|
return finalized_criteria.__send__(:order_by_index_finder) unless finalized?
|
90
|
-
@order_by_index_finder ||= IndexFinder.new(self).tap
|
92
|
+
@order_by_index_finder ||= IndexFinder.new(self).tap(&:find_index)
|
91
93
|
end
|
92
94
|
|
93
95
|
def compile_rql_pass1
|
@@ -1,4 +1,12 @@
|
|
1
1
|
module NoBrainer::Criteria::Where
|
2
|
+
NON_CHAINABLE_OPERATORS = %w(in nin eq ne not gt ge gte lt le lte defined).map(&:to_sym)
|
3
|
+
CHAINABLE_OPERATORS = %w(any all).map(&:to_sym)
|
4
|
+
OPERATORS = CHAINABLE_OPERATORS + NON_CHAINABLE_OPERATORS
|
5
|
+
|
6
|
+
require 'symbol_decoration'
|
7
|
+
Symbol::Decoration.register(*NON_CHAINABLE_OPERATORS)
|
8
|
+
Symbol::Decoration.register(*CHAINABLE_OPERATORS, :chainable => true)
|
9
|
+
|
2
10
|
extend ActiveSupport::Concern
|
3
11
|
|
4
12
|
included do
|
@@ -18,15 +26,16 @@ module NoBrainer::Criteria::Where
|
|
18
26
|
end
|
19
27
|
|
20
28
|
def where_indexed?
|
21
|
-
|
29
|
+
where_index_name.present?
|
22
30
|
end
|
23
31
|
|
24
32
|
def where_index_name
|
25
|
-
where_index_finder.
|
33
|
+
index = where_index_finder.strategy.try(:index)
|
34
|
+
index.is_a?(Array) ? index.map(&:name) : index.try(:name)
|
26
35
|
end
|
27
36
|
|
28
37
|
def where_index_type
|
29
|
-
where_index_finder.
|
38
|
+
where_index_finder.strategy.try(:rql_op)
|
30
39
|
end
|
31
40
|
|
32
41
|
private
|
@@ -34,7 +43,7 @@ module NoBrainer::Criteria::Where
|
|
34
43
|
class MultiOperator < Struct.new(:op, :clauses)
|
35
44
|
def simplify
|
36
45
|
clauses = self.clauses.map(&:simplify)
|
37
|
-
if
|
46
|
+
if clauses.size == 1 && clauses.first.is_a?(self.class)
|
38
47
|
return clauses.first
|
39
48
|
end
|
40
49
|
|
@@ -42,19 +51,19 @@ module NoBrainer::Criteria::Where
|
|
42
51
|
v.is_a?(self.class) && (v.clauses.size == 1 || v.op == self.op)
|
43
52
|
end
|
44
53
|
simplified_clauses = other_clauses + same_op_clauses.map(&:clauses).flatten(1)
|
45
|
-
simplified_clauses =
|
54
|
+
simplified_clauses = BinaryOperator.simplify_clauses(op, simplified_clauses.uniq)
|
46
55
|
self.class.new(op, simplified_clauses)
|
47
56
|
end
|
48
57
|
|
49
58
|
def to_rql(doc)
|
50
59
|
case op
|
51
|
-
when :and then clauses.map { |c| c.to_rql(doc) }.reduce
|
52
|
-
when :or then clauses.map { |c| c.to_rql(doc) }.reduce
|
60
|
+
when :and then clauses.map { |c| c.to_rql(doc) }.reduce(:&)
|
61
|
+
when :or then clauses.map { |c| c.to_rql(doc) }.reduce(:|)
|
53
62
|
end
|
54
63
|
end
|
55
64
|
end
|
56
65
|
|
57
|
-
class BinaryOperator < Struct.new(:key, :op, :value, :model, :casted_values)
|
66
|
+
class BinaryOperator < Struct.new(:key, :key_modifier, :op, :value, :model, :casted_values)
|
58
67
|
def self.get_candidate_clauses(clauses, *types)
|
59
68
|
clauses.select { |c| c.is_a?(self) && types.include?(c.op) }
|
60
69
|
end
|
@@ -63,14 +72,14 @@ module NoBrainer::Criteria::Where
|
|
63
72
|
# This code assumes that simplfy() has already been called on all clauses.
|
64
73
|
if op == :or
|
65
74
|
eq_clauses = get_candidate_clauses(ast_clauses, :in, :eq)
|
66
|
-
new_clauses = eq_clauses.group_by
|
67
|
-
|
68
|
-
when 1 then clauses.first
|
69
|
-
else
|
75
|
+
new_clauses = eq_clauses.group_by { |c| [c.key, c.key_modifier] }.map do |(key, key_modifier), clauses|
|
76
|
+
if key_modifier.in?([:scalar, :any]) && clauses.size > 1
|
70
77
|
values = clauses.map { |c| c.op == :in ? c.value : [c.value] }.flatten(1).uniq
|
71
|
-
BinaryOperator.new(key, :in, values, clauses.first.model, true)
|
78
|
+
[BinaryOperator.new(key, key_modifier, :in, values, clauses.first.model, true)]
|
79
|
+
else
|
80
|
+
clauses
|
72
81
|
end
|
73
|
-
end
|
82
|
+
end.flatten(1)
|
74
83
|
|
75
84
|
if new_clauses.size != eq_clauses.size
|
76
85
|
ast_clauses = ast_clauses - eq_clauses + new_clauses
|
@@ -81,29 +90,49 @@ module NoBrainer::Criteria::Where
|
|
81
90
|
end
|
82
91
|
|
83
92
|
def simplify
|
84
|
-
|
85
|
-
case op
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
93
|
+
new_key = cast_key(key)
|
94
|
+
new_op, new_value = case op
|
95
|
+
when :in then
|
96
|
+
case value
|
97
|
+
when Range then [:between, (cast_value(value.min)..cast_value(value.max))]
|
98
|
+
when Array then [:in, value.map(&method(:cast_value)).uniq]
|
99
|
+
else raise ArgumentError.new ":in takes an array/range, not #{value}"
|
100
|
+
end
|
101
|
+
when :between then [op, (cast_value(value.min)..cast_value(value.max))]
|
102
|
+
when :defined
|
103
|
+
raise "Incorrect use of `#{op}' and `#{key_modifier}'" if key_modifier != :scalar
|
104
|
+
[op, cast_value(value)]
|
105
|
+
else [op, cast_value(value)]
|
94
106
|
end
|
107
|
+
BinaryOperator.new(new_key, key_modifier, new_op, new_value, model, true)
|
95
108
|
end
|
96
109
|
|
97
110
|
def to_rql(doc)
|
98
111
|
key = model.lookup_field_alias(self.key)
|
112
|
+
|
113
|
+
case key_modifier
|
114
|
+
when :scalar then
|
115
|
+
case op
|
116
|
+
when :defined then value ? doc.has_fields(key) : doc.has_fields(key).not
|
117
|
+
else to_rql_scalar(doc[key])
|
118
|
+
end
|
119
|
+
when :any then doc[key].map { |lvalue| to_rql_scalar(lvalue) }.contains(true)
|
120
|
+
when :all then doc[key].map { |lvalue| to_rql_scalar(lvalue) }.contains(false).not
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_rql_scalar(lvalue)
|
99
125
|
case op
|
100
|
-
when :
|
101
|
-
when :
|
102
|
-
|
103
|
-
else doc[key].__send__(op, value)
|
126
|
+
when :between then (lvalue >= value.min) & (lvalue <= value.max)
|
127
|
+
when :in then RethinkDB::RQL.new.expr(value).contains(lvalue)
|
128
|
+
else lvalue.__send__(op, value)
|
104
129
|
end
|
105
130
|
end
|
106
131
|
|
132
|
+
def compatible_with_index?(index)
|
133
|
+
[key_modifier, index.multi].in?([[:any, true], [:scalar, false]])
|
134
|
+
end
|
135
|
+
|
107
136
|
private
|
108
137
|
|
109
138
|
def association
|
@@ -120,11 +149,14 @@ module NoBrainer::Criteria::Where
|
|
120
149
|
case association
|
121
150
|
when NoBrainer::Document::Association::BelongsTo::Metadata
|
122
151
|
target_model = association.target_model
|
123
|
-
opts = { :attr_name => key, :value => value, :type => target_model}
|
152
|
+
opts = { :attr_name => key, :value => value, :type => target_model }
|
124
153
|
raise NoBrainer::Error::InvalidType.new(opts) unless value.is_a?(target_model)
|
125
154
|
value.pk_value
|
126
155
|
else
|
127
|
-
|
156
|
+
case key_modifier
|
157
|
+
when :scalar then model.cast_user_to_db_for(key, value)
|
158
|
+
when :any, :all then model.cast_user_to_db_for(key, [value]).first
|
159
|
+
end
|
128
160
|
end
|
129
161
|
end
|
130
162
|
|
@@ -133,9 +165,14 @@ module NoBrainer::Criteria::Where
|
|
133
165
|
|
134
166
|
case association
|
135
167
|
when NoBrainer::Document::Association::BelongsTo::Metadata then association.foreign_key
|
136
|
-
else key
|
168
|
+
else ensure_valid_key!(key); key
|
137
169
|
end
|
138
170
|
end
|
171
|
+
|
172
|
+
def ensure_valid_key!(key)
|
173
|
+
return if model.has_field?(key) || model.has_index?(key) || model < NoBrainer::Document::DynamicAttributes
|
174
|
+
raise NoBrainer::Error::UnknownAttribute, "`#{key}' is not a declared attribute of #{model}"
|
175
|
+
end
|
139
176
|
end
|
140
177
|
|
141
178
|
class UnaryOperator < Struct.new(:op, :value)
|
@@ -165,7 +202,7 @@ module NoBrainer::Criteria::Where
|
|
165
202
|
when Array then MultiOperator.new(:and, clause.map { |c| parse_clause(c) })
|
166
203
|
when Hash then MultiOperator.new(:and, clause.map { |k,v| parse_clause_stub(k,v) })
|
167
204
|
when Proc then Lambda.new(clause)
|
168
|
-
when
|
205
|
+
when Symbol::Decoration
|
169
206
|
case clause.args.size
|
170
207
|
when 1 then parse_clause_stub(clause, clause.args.first)
|
171
208
|
else raise "Invalid argument: #{clause}"
|
@@ -180,12 +217,15 @@ module NoBrainer::Criteria::Where
|
|
180
217
|
when :or then MultiOperator.new(:or, value.map { |v| parse_clause(v) })
|
181
218
|
when :not then UnaryOperator.new(:not, parse_clause(value))
|
182
219
|
when String, Symbol then parse_clause_stub_eq(key, value)
|
183
|
-
when
|
184
|
-
case key.
|
220
|
+
when Symbol::Decoration then
|
221
|
+
case key.decorator
|
222
|
+
when :any, :all then parse_clause_stub_eq(key, value)
|
223
|
+
when :not, :ne then parse_clause(:not => { key.symbol.eq => value })
|
185
224
|
when :nin then parse_clause(:not => { key.symbol.in => value })
|
186
|
-
when :
|
225
|
+
when :gte then parse_clause(key.symbol.ge => value)
|
226
|
+
when :lte then parse_clause(key.symbol.le => value)
|
187
227
|
when :eq then parse_clause_stub_eq(key.symbol, value)
|
188
|
-
else
|
228
|
+
else instantiate_binary_op(key.symbol, key.decorator, value)
|
189
229
|
end
|
190
230
|
else raise "Invalid key: #{key}"
|
191
231
|
end
|
@@ -193,137 +233,154 @@ module NoBrainer::Criteria::Where
|
|
193
233
|
|
194
234
|
def parse_clause_stub_eq(key, value)
|
195
235
|
case value
|
196
|
-
when Range then
|
197
|
-
when Regexp then
|
198
|
-
else
|
236
|
+
when Range then instantiate_binary_op(key, :between, value)
|
237
|
+
when Regexp then instantiate_binary_op(key, :match, value.inspect[1..-2])
|
238
|
+
else instantiate_binary_op(key, :eq, value)
|
199
239
|
end
|
200
240
|
end
|
201
241
|
|
202
|
-
|
203
|
-
|
204
|
-
|
242
|
+
def instantiate_binary_op(key, op, value)
|
243
|
+
case key
|
244
|
+
when Symbol::Decoration then BinaryOperator.new(key.symbol, key.decorator, op, value, self.model)
|
245
|
+
else BinaryOperator.new(key, :scalar, op, value, self.model)
|
205
246
|
end
|
247
|
+
end
|
206
248
|
|
207
|
-
|
208
|
-
|
249
|
+
class IndexFinder < Struct.new(:criteria, :ast, :strategy)
|
250
|
+
class Strategy < Struct.new(:rql_op, :index, :ast, :rql_proc); end
|
251
|
+
class IndexStrategy < Struct.new(:criteria_ast, :optimized_clauses, :index, :rql_op, :rql_args, :rql_options)
|
252
|
+
def ast
|
253
|
+
MultiOperator.new(criteria_ast.op, criteria_ast.clauses - optimized_clauses)
|
254
|
+
end
|
255
|
+
|
256
|
+
def rql_proc
|
257
|
+
lambda do |rql|
|
258
|
+
opt = (rql_options || {}).merge(:index => index.aliased_name)
|
259
|
+
r = rql.__send__(rql_op, *rql_args, opt)
|
260
|
+
# TODO distinct: waiting for issue #3345
|
261
|
+
# TODO coerce_to: waiting for issue #3346
|
262
|
+
index.multi ? r.coerce_to('array').distinct : r
|
263
|
+
end
|
264
|
+
end
|
209
265
|
end
|
210
266
|
|
211
267
|
def get_candidate_clauses(*types)
|
212
268
|
BinaryOperator.get_candidate_clauses(ast.clauses, *types)
|
213
269
|
end
|
214
270
|
|
215
|
-
def get_usable_indexes(
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
indexes = indexes.select { |i|
|
220
|
-
if criteria.options[:use_index] && criteria.options[:use_index] != true
|
221
|
-
indexes = indexes.select { |i| i.name == criteria.options[:use_index].to_sym }
|
222
|
-
end
|
223
|
-
indexes
|
271
|
+
def get_usable_indexes(options={})
|
272
|
+
indexes = criteria.model.indexes.values
|
273
|
+
options.each { |k,v| indexes = indexes.select { |i| v == i.__send__(k) } }
|
274
|
+
if criteria.options[:use_index] && criteria.options[:use_index] != true
|
275
|
+
indexes = indexes.select { |i| i.name == criteria.options[:use_index].to_sym }
|
224
276
|
end
|
277
|
+
indexes
|
225
278
|
end
|
226
279
|
|
227
|
-
def
|
228
|
-
new_ast = MultiOperator.new(ast.op, ast.clauses - clauses)
|
229
|
-
return new_ast if new_ast.clauses.present?
|
230
|
-
end
|
231
|
-
|
232
|
-
def find_index_canonical
|
280
|
+
def find_strategy_canonical
|
233
281
|
clauses = Hash[get_candidate_clauses(:eq, :in, :between).map { |c| [c.key, c] }]
|
234
|
-
return unless clauses.present?
|
282
|
+
return nil unless clauses.present?
|
235
283
|
|
236
|
-
|
284
|
+
get_usable_indexes.each do |index|
|
237
285
|
clause = clauses[index.name]
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
when :
|
243
|
-
when :
|
244
|
-
|
245
|
-
:left_bound => :closed, :right_bound => :closed) }
|
286
|
+
next unless clause.try(:compatible_with_index?, index)
|
287
|
+
|
288
|
+
args = case clause.op
|
289
|
+
when :eq then [:get_all, [clause.value]]
|
290
|
+
when :in then [:get_all, clause.value]
|
291
|
+
when :between then [:between, [clause.value.min, clause.value.max],
|
292
|
+
:left_bound => :closed, :right_bound => :closed]
|
246
293
|
end
|
294
|
+
return IndexStrategy.new(ast, [clause], index, *args)
|
247
295
|
end
|
296
|
+
return nil
|
248
297
|
end
|
249
298
|
|
250
|
-
def
|
299
|
+
def find_strategy_compound
|
251
300
|
clauses = Hash[get_candidate_clauses(:eq).map { |c| [c.key, c] }]
|
252
|
-
return unless clauses.present?
|
301
|
+
return nil unless clauses.present?
|
253
302
|
|
254
|
-
|
303
|
+
get_usable_indexes(:kind => :compound, :multi => false).each do |index|
|
255
304
|
indexed_clauses = index.what.map { |field| clauses[field] }
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
self.rql_proc = ->(rql){ rql.get_all(indexed_clauses.map { |c| c.value }, :index => index.aliased_name) }
|
305
|
+
next unless indexed_clauses.all? { |c| c.try(:compatible_with_index?, index) }
|
306
|
+
|
307
|
+
return IndexStrategy.new(ast, indexed_clauses, index, :get_all, [indexed_clauses.map(&:value)])
|
260
308
|
end
|
309
|
+
return nil
|
261
310
|
end
|
262
311
|
|
263
|
-
def
|
312
|
+
def find_strategy_hidden_between
|
264
313
|
clauses = get_candidate_clauses(:gt, :ge, :lt, :le).group_by(&:key)
|
265
|
-
return unless clauses.present?
|
314
|
+
return nil unless clauses.present?
|
315
|
+
|
316
|
+
get_usable_indexes.each do |index|
|
317
|
+
matched_clauses = clauses[index.name].try(:select) { |c| c.compatible_with_index?(index) }
|
318
|
+
next unless matched_clauses.present?
|
266
319
|
|
267
|
-
|
268
|
-
|
269
|
-
left_bound = op_clauses[:gt] || op_clauses[:ge]
|
320
|
+
op_clauses = Hash[matched_clauses.map { |c| [c.op, c] }]
|
321
|
+
left_bound = op_clauses[:gt] || op_clauses[:ge]
|
270
322
|
right_bound = op_clauses[:lt] || op_clauses[:le]
|
271
323
|
|
272
|
-
|
273
|
-
|
324
|
+
# XXX we must keep only one bound when using `any', otherwise we get different semantics.
|
325
|
+
right_bound = nil if index.multi && left_bound && right_bound
|
274
326
|
|
275
327
|
options = {}
|
276
|
-
options[:
|
277
|
-
options[:left_bound] = {:gt => :open, :ge => :closed}[left_bound.op] if left_bound
|
328
|
+
options[:left_bound] = {:gt => :open, :ge => :closed}[left_bound.op] if left_bound
|
278
329
|
options[:right_bound] = {:lt => :open, :le => :closed}[right_bound.op] if right_bound
|
279
|
-
|
280
|
-
|
330
|
+
|
331
|
+
return IndexStrategy.new(ast, [left_bound, right_bound].compact, index, :between,
|
332
|
+
[left_bound.try(:value), right_bound.try(:value)], options)
|
281
333
|
end
|
334
|
+
return nil
|
282
335
|
end
|
283
336
|
|
284
|
-
def
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
index_finder = index_finder.dup
|
290
|
-
break unless index_finder.find_index_canonical
|
291
|
-
# TODO To use a compound index, we'd have to add all permutations in the query
|
292
|
-
indexes << index_finder
|
293
|
-
break unless index_finder.ast
|
337
|
+
def find_strategy_union
|
338
|
+
strategies = ast.clauses.map do |inner_ast|
|
339
|
+
inner_ast = MultiOperator.new(:and, [inner_ast]) unless inner_ast.is_a?(MultiOperator)
|
340
|
+
raise 'fatal' unless inner_ast.op == :and
|
341
|
+
self.class.new(criteria, inner_ast).find_strategy
|
294
342
|
end
|
295
343
|
|
296
|
-
if
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
344
|
+
return nil if strategies.any?(&:nil?)
|
345
|
+
|
346
|
+
rql_proc = lambda do |base_rql|
|
347
|
+
strategies.map do |strategy|
|
348
|
+
rql = strategy.rql_proc.call(base_rql)
|
349
|
+
rql = rql.filter { |doc| strategy.ast.to_rql(doc) } if strategy.ast.try(:clauses).present?
|
350
|
+
rql
|
351
|
+
end.reduce(:union).distinct
|
301
352
|
end
|
353
|
+
|
354
|
+
Strategy.new(:union, strategies.map(&:index), nil, rql_proc)
|
302
355
|
end
|
303
356
|
|
304
|
-
def
|
305
|
-
return
|
357
|
+
def find_strategy
|
358
|
+
return nil unless ast.try(:clauses).present? && !criteria.without_index?
|
306
359
|
case ast.op
|
307
|
-
when :and then
|
308
|
-
when :or then
|
360
|
+
when :and then find_strategy_compound || find_strategy_canonical || find_strategy_hidden_between
|
361
|
+
when :or then find_strategy_union
|
309
362
|
end
|
310
363
|
end
|
364
|
+
|
365
|
+
def find_strategy!
|
366
|
+
self.strategy = find_strategy
|
367
|
+
end
|
311
368
|
end
|
312
369
|
|
313
370
|
def where_index_finder
|
314
371
|
return finalized_criteria.__send__(:where_index_finder) unless finalized?
|
315
|
-
@where_index_finder ||= IndexFinder.new(self, @options[:where_ast]).tap
|
372
|
+
@where_index_finder ||= IndexFinder.new(self, @options[:where_ast]).tap(&:find_strategy!)
|
316
373
|
end
|
317
374
|
|
318
375
|
def compile_rql_pass1
|
319
376
|
rql = super
|
320
|
-
rql = where_index_finder.rql_proc.call(rql) if
|
377
|
+
rql = where_index_finder.strategy.rql_proc.call(rql) if where_indexed?
|
321
378
|
rql
|
322
379
|
end
|
323
380
|
|
324
381
|
def compile_rql_pass2
|
325
382
|
rql = super
|
326
|
-
ast =
|
383
|
+
ast = where_indexed? ? where_index_finder.strategy.ast : @options[:where_ast]
|
327
384
|
rql = rql.filter { |doc| ast.to_rql(doc) } if ast.try(:clauses).present?
|
328
385
|
rql
|
329
386
|
end
|
@@ -12,9 +12,17 @@ module NoBrainer::Document::Aliases
|
|
12
12
|
|
13
13
|
module ClassMethods
|
14
14
|
def _field(attr, options={})
|
15
|
+
if options[:store_as]
|
16
|
+
self.alias_map[attr.to_s] = options[:store_as].to_s
|
17
|
+
self.alias_reverse_map[options[:store_as].to_s] = attr.to_s
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def field(attr, options={})
|
15
23
|
if options[:as]
|
16
|
-
|
17
|
-
|
24
|
+
STDERR.puts "[NoBrainer] `:as' is deprecated and will be removed. Please use `:store_as' instead (from the #{self} model)"
|
25
|
+
options[:store_as] = options.delete(:as)
|
18
26
|
end
|
19
27
|
super
|
20
28
|
end
|
@@ -2,7 +2,7 @@ class NoBrainer::Document::Association::BelongsTo
|
|
2
2
|
include NoBrainer::Document::Association::Core
|
3
3
|
|
4
4
|
class Metadata
|
5
|
-
VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :
|
5
|
+
VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :foreign_key_store_as, :index, :validates, :required]
|
6
6
|
include NoBrainer::Document::Association::Core::Metadata
|
7
7
|
extend NoBrainer::Document::Association::EagerLoader::Generic
|
8
8
|
|
@@ -29,8 +29,8 @@ class NoBrainer::Document::Association::BelongsTo
|
|
29
29
|
# This would have the effect of loading all the models because they
|
30
30
|
# are likely to be related to each other. So we don't know the type
|
31
31
|
# of the primary key of the target.
|
32
|
-
owner_model.field(foreign_key, :
|
33
|
-
owner_model.validates(target_name,
|
32
|
+
owner_model.field(foreign_key, :store_as => options[:foreign_key_store_as], :index => options[:index])
|
33
|
+
owner_model.validates(target_name, :presence => options[:required]) if options[:required]
|
34
34
|
owner_model.validates(target_name, options[:validates]) if options[:validates]
|
35
35
|
|
36
36
|
delegate("#{foreign_key}=", :assign_foreign_key, :call_super => true)
|
@@ -37,7 +37,7 @@ module NoBrainer::Document::Association::Core
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def add_callback_for(what)
|
40
|
-
instance_eval <<-RUBY, __FILE__, __LINE__+1
|
40
|
+
instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
41
41
|
if !@added_#{what}
|
42
42
|
metadata = self
|
43
43
|
owner_model.#{what} { associations[metadata].#{what}_callback }
|
@@ -17,7 +17,7 @@ class NoBrainer::Document::Association::EagerLoader
|
|
17
17
|
if owner_keys.present?
|
18
18
|
targets = criteria.where(target_key.in => owner_keys)
|
19
19
|
.map { |target| [target.read_attribute(target_key), target] }
|
20
|
-
.
|
20
|
+
.each_with_object(Hash.new { |k,v| k[v] = [] }) { |(k,v),h| h[k] << v }
|
21
21
|
|
22
22
|
unloaded_docs.each do |doc|
|
23
23
|
doc_targets = targets[doc.read_attribute(owner_key)]
|
@@ -53,7 +53,7 @@ class NoBrainer::Document::Association::HasMany
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def write(new_children)
|
56
|
-
raise "You can't assign #{target_name}. "
|
56
|
+
raise "You can't assign #{target_name}. " \
|
57
57
|
"Instead, you must modify delete and create #{target_model} manually."
|
58
58
|
end
|
59
59
|
|
@@ -80,7 +80,7 @@ class NoBrainer::Document::Association::HasMany
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def set_inverse_proc
|
83
|
-
|
83
|
+
->(target){ set_inverses_of([target]) if target.is_a?(NoBrainer::Document) }
|
84
84
|
end
|
85
85
|
|
86
86
|
def before_destroy_callback
|
@@ -2,54 +2,53 @@ module NoBrainer::Document::AtomicOps
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
class PendingAtomic
|
5
|
-
def self._new(instance, field, value, is_user_value
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
model.new(instance, field, value, is_user_value, options)
|
5
|
+
def self._new(instance, field, value, is_user_value)
|
6
|
+
case value
|
7
|
+
when Array then PendingAtomicArray
|
8
|
+
when Set then PendingAtomicSet
|
9
|
+
else self
|
10
|
+
end.new(instance, field, value, is_user_value)
|
12
11
|
end
|
13
12
|
|
14
|
-
def initialize(instance, field, value, is_user_value
|
13
|
+
def initialize(instance, field, value, is_user_value)
|
15
14
|
@instance = instance
|
16
15
|
@field = field.to_s
|
17
16
|
@value = value
|
18
17
|
@is_user_value = is_user_value
|
19
|
-
@options = options.dup
|
20
18
|
@ops = []
|
21
19
|
end
|
22
20
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def ensure_writeable!
|
28
|
-
unless write_access?
|
29
|
-
@options[:write_access] = true
|
30
|
-
@instance.write_attribute(@field, self)
|
31
|
-
end
|
21
|
+
def initialize_copy(other)
|
22
|
+
super
|
23
|
+
@ops = @ops.dup
|
32
24
|
end
|
33
25
|
|
34
26
|
def to_s
|
35
|
-
"
|
27
|
+
"<`#{@field}' with #{@ops.size} pending atomic operations>"
|
36
28
|
end
|
37
|
-
|
29
|
+
alias_method :inspect, :to_s
|
38
30
|
|
39
|
-
def method_missing(
|
40
|
-
@ops << [
|
31
|
+
def method_missing(method, *a, &b)
|
32
|
+
@ops << [method, a, b]
|
41
33
|
self
|
42
34
|
end
|
43
35
|
|
44
36
|
def compile_rql_value(rql_doc)
|
45
37
|
field = @instance.class.lookup_field_alias(@field)
|
46
38
|
value = @is_user_value ? RethinkDB::RQL.new.expr(@value) : rql_doc[field]
|
47
|
-
@ops.
|
48
|
-
|
39
|
+
@ops.reduce(value) { |v, (method, a, b)| v.__send__(method, *a, &b) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class PendingAtomicContainer < PendingAtomic
|
44
|
+
def modify_source!
|
45
|
+
unless @instance._is_attribute_touched?(@field)
|
46
|
+
@instance.write_attribute(@field, self)
|
47
|
+
end
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
|
-
class PendingAtomicArray <
|
51
|
+
class PendingAtomicArray < PendingAtomicContainer
|
53
52
|
def -(value)
|
54
53
|
@ops << [:difference, [value.to_a]]
|
55
54
|
self
|
@@ -80,12 +79,12 @@ module NoBrainer::Document::AtomicOps
|
|
80
79
|
|
81
80
|
def <<(value)
|
82
81
|
@ops << [:append, [value]]
|
83
|
-
|
82
|
+
modify_source!
|
84
83
|
self
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
|
-
class PendingAtomicSet <
|
87
|
+
class PendingAtomicSet < PendingAtomicContainer
|
89
88
|
def -(value)
|
90
89
|
@ops << [:set_difference, [value.to_a]]
|
91
90
|
self
|
@@ -98,7 +97,7 @@ module NoBrainer::Document::AtomicOps
|
|
98
97
|
|
99
98
|
def <<(value)
|
100
99
|
@ops << [:set_union, [[value]]]
|
101
|
-
|
100
|
+
modify_source!
|
102
101
|
self
|
103
102
|
end
|
104
103
|
end
|
@@ -145,9 +144,9 @@ module NoBrainer::Document::AtomicOps
|
|
145
144
|
value = super
|
146
145
|
|
147
146
|
case [in_atomic?, value.is_a?(PendingAtomic)]
|
148
|
-
when [true, true] then value
|
149
147
|
when [true, false] then PendingAtomic._new(self, name, value, _is_attribute_touched?(name))
|
150
148
|
when [false, true] then raise NoBrainer::Error::CannotReadAtomic.new(self, name, value)
|
149
|
+
when [true, true] then value.is_a?(PendingAtomicContainer) ? value : value.dup
|
151
150
|
when [false, false] then value
|
152
151
|
end
|
153
152
|
end
|
@@ -156,9 +155,9 @@ module NoBrainer::Document::AtomicOps
|
|
156
155
|
ensure_exclusive_atomic!
|
157
156
|
|
158
157
|
case [in_atomic?, value.is_a?(PendingAtomic)]
|
159
|
-
when [true, true] then super
|
160
158
|
when [true, false] then raise NoBrainer::Error::AtomicBlock.new('Avoid the use of atomic blocks for non atomic operations')
|
161
159
|
when [false, true] then raise NoBrainer::Error::AtomicBlock.new('Use atomic blocks for atomic operations')
|
160
|
+
when [true, true] then super.tap { _touch_attribute(name) }
|
162
161
|
when [false, false] then super.tap { _touch_attribute(name) }
|
163
162
|
end
|
164
163
|
end
|
@@ -1,14 +1,11 @@
|
|
1
1
|
module NoBrainer::Document::Attributes
|
2
|
-
VALID_FIELD_OPTIONS = [:index, :default, :type, :
|
3
|
-
:validates, :required, :unique, :uniq, :format, :in
|
4
|
-
:readonly, :primary_key, :as, :lazy_fetch]
|
2
|
+
VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key, :lazy_fetch, :store_as,
|
3
|
+
:validates, :required, :unique, :uniq, :format, :in]
|
5
4
|
RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] \
|
6
|
-
+ NoBrainer::
|
5
|
+
+ NoBrainer::Criteria::Where::OPERATORS
|
7
6
|
extend ActiveSupport::Concern
|
8
7
|
|
9
8
|
included do
|
10
|
-
# Not using class_attribute because we want to
|
11
|
-
# use our custom logic
|
12
9
|
singleton_class.send(:attr_accessor, :fields)
|
13
10
|
self.fields = {}
|
14
11
|
end
|
@@ -46,20 +43,19 @@ module NoBrainer::Document::Attributes
|
|
46
43
|
|
47
44
|
def assign_defaults(options)
|
48
45
|
self.class.fields.each do |name, field_options|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
default_value = field_options[:default]
|
60
|
-
default_value = default_value.call if default_value.is_a?(Proc)
|
61
|
-
self.write_attribute(name, default_value)
|
46
|
+
next unless field_options.has_key?(:default) &&
|
47
|
+
!@_attributes.has_key?(name)
|
48
|
+
|
49
|
+
if opt = options[:missing_attributes]
|
50
|
+
if (opt[:pluck] && !opt[:pluck][name]) ||
|
51
|
+
(opt[:without] && opt[:without][name])
|
52
|
+
next
|
53
|
+
end
|
62
54
|
end
|
55
|
+
|
56
|
+
default_value = field_options[:default]
|
57
|
+
default_value = default_value.call if default_value.is_a?(Proc)
|
58
|
+
self.write_attribute(name, default_value)
|
63
59
|
end
|
64
60
|
end
|
65
61
|
|
@@ -51,14 +51,13 @@ module NoBrainer::Document::Id
|
|
51
51
|
# 3 bytes inc
|
52
52
|
oid += [get_inc].pack("N")[1, 3]
|
53
53
|
|
54
|
-
oid.unpack("C12").map {|e| v=e.to_s(16); v.size == 1 ? "0#{v}" : v }.join
|
54
|
+
oid.unpack("C12").map { |e| v = e.to_s(16); v.size == 1 ? "0#{v}" : v }.join
|
55
55
|
end
|
56
56
|
|
57
57
|
module ClassMethods
|
58
58
|
def define_default_pk
|
59
59
|
class_variable_set(:@@pk_name, nil)
|
60
|
-
field NoBrainer::Document::Id::DEFAULT_PK_NAME, :primary_key => :default
|
61
|
-
:type => String, :default => ->{ NoBrainer::Document::Id.generate }
|
60
|
+
field NoBrainer::Document::Id::DEFAULT_PK_NAME, :primary_key => :default
|
62
61
|
end
|
63
62
|
|
64
63
|
def define_pk(attr)
|
@@ -81,6 +80,11 @@ module NoBrainer::Document::Id
|
|
81
80
|
if options[:primary_key]
|
82
81
|
options = options.merge(:readonly => true) if options[:readonly].nil?
|
83
82
|
options = options.merge(:index => true)
|
83
|
+
|
84
|
+
if options[:type].in?([String, nil]) && options[:default].nil?
|
85
|
+
options[:type] = String
|
86
|
+
options[:default] = ->{ NoBrainer::Document::Id.generate }
|
87
|
+
end
|
84
88
|
end
|
85
89
|
super
|
86
90
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module NoBrainer::Document::Index
|
2
|
-
VALID_INDEX_OPTIONS = [:external, :geo, :multi, :
|
2
|
+
VALID_INDEX_OPTIONS = [:external, :geo, :multi, :store_as]
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
extend NoBrainer::Autoload
|
5
5
|
|
@@ -14,6 +14,12 @@ module NoBrainer::Document::Index
|
|
14
14
|
def index(name, *args)
|
15
15
|
name = name.to_sym
|
16
16
|
options = args.extract_options!
|
17
|
+
|
18
|
+
if options[:as]
|
19
|
+
STDERR.puts "[NoBrainer] `:as' is deprecated and will be removed. Please use `:store_as' instead (from the #{self} model)"
|
20
|
+
options[:store_as] = options.delete(:as)
|
21
|
+
end
|
22
|
+
|
17
23
|
options.assert_valid_keys(*VALID_INDEX_OPTIONS)
|
18
24
|
|
19
25
|
raise "Too many arguments: #{args}" if args.size > 1
|
@@ -33,12 +39,16 @@ module NoBrainer::Document::Index
|
|
33
39
|
raise "Cannot reuse field name #{name}"
|
34
40
|
end
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
if kind == :compound && what.size < 2
|
43
|
+
raise "Compound indexes only make sense with 2 or more fields"
|
44
|
+
end
|
45
|
+
|
46
|
+
store_as = options.delete(:store_as)
|
47
|
+
store_as ||= fields[name][:store_as] if has_field?(name)
|
48
|
+
store_as ||= name
|
49
|
+
store_as = store_as.to_sym
|
40
50
|
|
41
|
-
indexes[name] = NoBrainer::Document::Index::Index.new(self.root_class, name,
|
51
|
+
indexes[name] = NoBrainer::Document::Index::Index.new(self.root_class, name, store_as,
|
42
52
|
kind, what, options[:external], options[:geo], options[:multi], nil)
|
43
53
|
end
|
44
54
|
|
@@ -57,12 +67,12 @@ module NoBrainer::Document::Index
|
|
57
67
|
|
58
68
|
super
|
59
69
|
|
60
|
-
|
70
|
+
store_as = {:store_as => options[:store_as]}
|
61
71
|
case options[:index]
|
62
72
|
when nil then
|
63
|
-
when Hash then index(attr,
|
64
|
-
when Symbol then index(attr,
|
65
|
-
when true then index(attr,
|
73
|
+
when Hash then index(attr, store_as.merge(options[:index]))
|
74
|
+
when Symbol then index(attr, store_as.merge(options[:index] => true))
|
75
|
+
when true then index(attr, store_as)
|
66
76
|
when false then remove_index(attr)
|
67
77
|
end
|
68
78
|
end
|
@@ -45,16 +45,16 @@ module NoBrainer::Document::Persistance
|
|
45
45
|
|
46
46
|
def reload(options={})
|
47
47
|
[:without, :pluck].each do |type|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
48
|
+
next unless v = options.delete(type)
|
49
|
+
|
50
|
+
v = Hash[v.flatten.map { |k| [k, true] }] if v.is_a?(Array)
|
51
|
+
v = {v => true} unless v.is_a?(Hash)
|
52
|
+
v = v.select { |k,_v| _v }
|
53
|
+
v = v.with_indifferent_access
|
54
|
+
next unless v.present?
|
55
|
+
|
56
|
+
options[:missing_attributes] ||= {}
|
57
|
+
options[:missing_attributes][type] = v
|
58
58
|
end
|
59
59
|
_reload(options)
|
60
60
|
end
|
@@ -144,6 +144,7 @@ module NoBrainer::Document::Persistance
|
|
144
144
|
def create(attrs={}, options={})
|
145
145
|
new(attrs, options).tap { |doc| doc.save(options) }
|
146
146
|
end
|
147
|
+
alias_method :create!, :create
|
147
148
|
|
148
149
|
def insert_all(*args)
|
149
150
|
docs = args.shift
|
@@ -7,12 +7,14 @@ module NoBrainer::Document::Timestamps
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def _create(options={})
|
10
|
-
|
10
|
+
now = Time.now
|
11
|
+
self.created_at = now unless created_at_changed?
|
12
|
+
self.updated_at = now unless updated_at_changed?
|
11
13
|
super
|
12
14
|
end
|
13
15
|
|
14
16
|
def _update(attrs)
|
15
|
-
self.updated_at = Time.now
|
17
|
+
self.updated_at = Time.now unless updated_at_changed?
|
16
18
|
super(attrs.merge('updated_at' => @_attributes['updated_at']))
|
17
19
|
end
|
18
20
|
end
|
@@ -88,15 +88,6 @@ module NoBrainer::Document::Types
|
|
88
88
|
remove_method("#{attr}?") if method_defined?("#{attr}?")
|
89
89
|
end
|
90
90
|
end
|
91
|
-
|
92
|
-
def field(attr, options={})
|
93
|
-
options[:real_type] = options[:type]
|
94
|
-
if options[:type] == Array || options[:type] == Hash
|
95
|
-
# XXX For the moment, NoBrainer does not support these complex types
|
96
|
-
options.delete(:type)
|
97
|
-
end
|
98
|
-
super
|
99
|
-
end
|
100
91
|
end
|
101
92
|
|
102
93
|
require File.join(File.dirname(__FILE__), 'types', 'binary')
|
@@ -23,11 +23,11 @@ module NoBrainer::Document::Validation
|
|
23
23
|
module ClassMethods
|
24
24
|
def _field(attr, options={})
|
25
25
|
super
|
26
|
-
validates(attr,
|
27
|
-
validates(attr,
|
28
|
-
validates(attr,
|
29
|
-
validates(attr,
|
30
|
-
validates(attr,
|
26
|
+
validates(attr, :format => { :with => options[:format] }) if options.has_key?(:format)
|
27
|
+
validates(attr, :presence => options[:required]) if options.has_key?(:required)
|
28
|
+
validates(attr, :uniqueness => options[:unique]) if options.has_key?(:unique)
|
29
|
+
validates(attr, :uniqueness => options[:uniq]) if options.has_key?(:uniq)
|
30
|
+
validates(attr, :inclusion => {:in => options[:in]}) if options.has_key?(:in)
|
31
31
|
validates(attr, options[:validates]) if options[:validates]
|
32
32
|
end
|
33
33
|
end
|
data/lib/no_brainer/error.rb
CHANGED
@@ -9,6 +9,7 @@ module NoBrainer::Error
|
|
9
9
|
class AssociationNotPersisted < RuntimeError; end
|
10
10
|
class ReadonlyField < RuntimeError; end
|
11
11
|
class MissingAttribute < RuntimeError; end
|
12
|
+
class UnknownAttribute < RuntimeError; end
|
12
13
|
class AtomicBlock < RuntimeError; end
|
13
14
|
|
14
15
|
class CannotReadAtomic < RuntimeError
|
@@ -18,18 +18,16 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def reconnect(e, context)
|
21
|
-
|
22
|
-
|
23
|
-
context[:retries] -= 1
|
21
|
+
return false if context[:retries].zero?
|
22
|
+
context[:retries] -= 1
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
24
|
+
warn_reconnect(e)
|
25
|
+
sleep 1
|
26
|
+
NoBrainer.connection.reconnect(:noreply_wait => false)
|
27
|
+
true
|
28
|
+
rescue StandardError => e
|
29
|
+
retry if is_connection_error_exception?(e)
|
30
|
+
raise
|
33
31
|
end
|
34
32
|
|
35
33
|
def is_connection_error_exception?(e)
|
@@ -30,7 +30,7 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
|
|
30
30
|
env[:last_auto_create_table] = [database_name, table_name]
|
31
31
|
|
32
32
|
NoBrainer.with_database(database_name) do
|
33
|
-
NoBrainer.table_create(table_name, :primary_key => model.pk_name)
|
33
|
+
NoBrainer.table_create(table_name, :primary_key => model.lookup_field_alias(model.pk_name))
|
34
34
|
end
|
35
35
|
rescue RuntimeError => e
|
36
36
|
# We might have raced with another table create
|
@@ -19,10 +19,10 @@ namespace :nobrainer do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
desc 'Equivalent to :sync_indexes_quiet + :seed'
|
22
|
-
task :setup => [
|
22
|
+
task :setup => [:sync_indexes_quiet, :seed]
|
23
23
|
|
24
24
|
desc 'Equivalent to :drop + :setup'
|
25
|
-
task :reset => [
|
25
|
+
task :reset => [:drop, :setup]
|
26
26
|
|
27
27
|
task :create => :environment do
|
28
28
|
# noop
|
data/lib/no_brainer/rql.rb
CHANGED
@@ -19,7 +19,7 @@ module NoBrainer::RQL
|
|
19
19
|
case rql_query.body.first
|
20
20
|
when UPDATE, DELETE, REPLACE, INSERT
|
21
21
|
:write
|
22
|
-
when DB_CREATE,DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST, SYNC,
|
22
|
+
when DB_CREATE, DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST, SYNC,
|
23
23
|
INDEX_CREATE, INDEX_DROP, INDEX_LIST, INDEX_STATUS, INDEX_WAIT
|
24
24
|
:management
|
25
25
|
else
|
data/lib/nobrainer.rb
CHANGED
@@ -12,7 +12,7 @@ module NoBrainer
|
|
12
12
|
|
13
13
|
# We eager load things that could be loaded when handling the first web request.
|
14
14
|
# Code that is loaded through the DSL of NoBrainer should not be eager loaded.
|
15
|
-
autoload :Document, :IndexManager, :Loader, :Fork
|
15
|
+
autoload :Document, :IndexManager, :Loader, :Fork
|
16
16
|
eager_autoload :Config, :Connection, :ConnectionManager, :Error,
|
17
17
|
:QueryRunner, :Criteria, :RQL
|
18
18
|
|
@@ -31,13 +31,8 @@ module NoBrainer
|
|
31
31
|
def jruby?
|
32
32
|
RUBY_PLATFORM == 'java'
|
33
33
|
end
|
34
|
-
|
35
|
-
def user_caller
|
36
|
-
caller.reject { |s| s =~ /\/no_brainer\// }.first
|
37
|
-
end
|
38
34
|
end
|
39
35
|
|
40
|
-
DecoratedSymbol.hook
|
41
36
|
Fork.hook unless jruby?
|
42
37
|
end
|
43
38
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nobrainer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolas Viennot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rethinkdb
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.15.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.15.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.1.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: symbol_decoration
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.1'
|
69
83
|
description: ORM for RethinkDB
|
70
84
|
email:
|
71
85
|
- nicolas@viennot.biz
|
@@ -98,7 +112,6 @@ files:
|
|
98
112
|
- lib/no_brainer/criteria/scope.rb
|
99
113
|
- lib/no_brainer/criteria/update.rb
|
100
114
|
- lib/no_brainer/criteria/where.rb
|
101
|
-
- lib/no_brainer/decorated_symbol.rb
|
102
115
|
- lib/no_brainer/document.rb
|
103
116
|
- lib/no_brainer/document/aliases.rb
|
104
117
|
- lib/no_brainer/document/association.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
class NoBrainer::DecoratedSymbol < Struct.new(:symbol, :modifier, :args)
|
2
|
-
MODIFIERS = { :in => :in, :nin => :nin,
|
3
|
-
:eq => :eq, :ne => :ne, :not => :ne,
|
4
|
-
:gt => :gt, :ge => :ge, :gte => :ge,
|
5
|
-
:lt => :lt, :le => :le, :lte => :le,
|
6
|
-
:defined => :defined }
|
7
|
-
|
8
|
-
def self.hook
|
9
|
-
Symbol.class_eval do
|
10
|
-
MODIFIERS.each do |modifier_name, modifier|
|
11
|
-
define_method modifier_name do |*args|
|
12
|
-
NoBrainer::DecoratedSymbol.new(self, modifier, args)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|