nobrainer 0.18.1 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|