nobrainer 0.18.1 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/no_brainer/connection.rb +1 -1
  3. data/lib/no_brainer/criteria/core.rb +1 -1
  4. data/lib/no_brainer/criteria/delete.rb +1 -1
  5. data/lib/no_brainer/criteria/order_by.rb +4 -2
  6. data/lib/no_brainer/criteria/where.rb +166 -109
  7. data/lib/no_brainer/document/aliases.rb +10 -2
  8. data/lib/no_brainer/document/association/belongs_to.rb +3 -3
  9. data/lib/no_brainer/document/association/core.rb +1 -1
  10. data/lib/no_brainer/document/association/eager_loader.rb +1 -1
  11. data/lib/no_brainer/document/association/has_many.rb +2 -2
  12. data/lib/no_brainer/document/atomic_ops.rb +29 -30
  13. data/lib/no_brainer/document/attributes.rb +15 -19
  14. data/lib/no_brainer/document/callbacks.rb +1 -1
  15. data/lib/no_brainer/document/id.rb +7 -3
  16. data/lib/no_brainer/document/index.rb +20 -10
  17. data/lib/no_brainer/document/persistance.rb +11 -10
  18. data/lib/no_brainer/document/timestamps.rb +4 -2
  19. data/lib/no_brainer/document/types.rb +0 -9
  20. data/lib/no_brainer/document/types/binary.rb +0 -1
  21. data/lib/no_brainer/document/types/boolean.rb +0 -1
  22. data/lib/no_brainer/document/uniqueness.rb +0 -1
  23. data/lib/no_brainer/document/validation.rb +5 -5
  24. data/lib/no_brainer/error.rb +1 -0
  25. data/lib/no_brainer/query_runner/connection_lock.rb +1 -1
  26. data/lib/no_brainer/query_runner/reconnect.rb +9 -11
  27. data/lib/no_brainer/query_runner/table_on_demand.rb +1 -1
  28. data/lib/no_brainer/railtie/database.rake +2 -2
  29. data/lib/no_brainer/rql.rb +1 -1
  30. data/lib/nobrainer.rb +1 -6
  31. data/lib/rails/generators/nobrainer.rb +1 -1
  32. metadata +18 -5
  33. data/lib/no_brainer/decorated_symbol.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d6a500e008eff372e800b011e917d121c07e00a
4
- data.tar.gz: e43be89ac4963422c26559727c9a68011ee25f8a
3
+ metadata.gz: 5dc33c00dc3aa418ae3bd36b0ccb2618a8c0e47f
4
+ data.tar.gz: 3a4c171e4c41341d14a3c4b6103b463be1b12701
5
5
  SHA512:
6
- metadata.gz: 56015e819aba3537ab60ea5f039d4b391ec803cc61b5bf246e44da642787e62606e9c02339b17f05976d5428ec3e4fa0e335cb51694b840bd2ba7821b6d66b20
7
- data.tar.gz: d4b78185fed14f5baa28165462a4088096da640d93c0dfb3f20c851e968deceb1f48f13602850075a7eea2408fa23e52c2694d140bea1c9a937d7d49d5084f53
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!(options={})
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 }
@@ -57,7 +57,7 @@ module NoBrainer::Criteria::Core
57
57
 
58
58
  private
59
59
 
60
- def chain(options={}, merge_options={}, &block)
60
+ def chain(options={}, merge_options={})
61
61
  merge(self.class.new(options), merge_options)
62
62
  end
63
63
 
@@ -6,6 +6,6 @@ module NoBrainer::Criteria::Delete
6
6
  end
7
7
 
8
8
  def destroy_all
9
- to_a.each { |doc| doc.destroy }
9
+ to_a.each(&:destroy)
10
10
  end
11
11
  end
@@ -65,7 +65,9 @@ module NoBrainer::Criteria::OrderBy
65
65
  end
66
66
 
67
67
  def first_key_indexable?
68
- (first_key.is_a?(Symbol) || first_key.is_a?(String)) && criteria.model.has_index?(first_key)
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 { |index_finder| index_finder.find_index }
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
- !!where_index_name
29
+ where_index_name.present?
22
30
  end
23
31
 
24
32
  def where_index_name
25
- where_index_finder.index_name
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.index_type
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 self.clauses.size == 1 && self.clauses.first.is_a?(self.class)
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 = BinaryOperator.simplify_clauses(op, simplified_clauses.uniq)
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 { |a,b| a & b }
52
- when :or then clauses.map { |c| c.to_rql(doc) }.reduce { |a,b| a | b }
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(&:key).map do |key, clauses|
67
- case clauses.size
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
- key = cast_key(self.key)
85
- case op
86
- when :in then
87
- case value
88
- when Range then BinaryOperator.new(key, :between, (cast_value(value.min)..cast_value(value.max)), model, true)
89
- when Array then BinaryOperator.new(key, :in, value.map(&method(:cast_value)).uniq, model, true)
90
- else raise ArgumentError.new ":in takes an array/range, not #{value}"
91
- end
92
- when :between then BinaryOperator.new(key, :between, (cast_value(value.min)..cast_value(value.max)), model, true)
93
- else BinaryOperator.new(key, op, cast_value(value), model, true)
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 :defined then value ? doc.has_fields(key) : doc.has_fields(key).not
101
- when :between then (doc[key] >= value.min) & (doc[key] <= value.max)
102
- when :in then RethinkDB::RQL.new.expr(value).contains(doc[key])
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
- model.cast_user_to_db_for(key, value)
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 NoBrainer::DecoratedSymbol
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 NoBrainer::DecoratedSymbol then
184
- case key.modifier
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 :ne then parse_clause(:not => { key.symbol.eq => value })
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 BinaryOperator.new(key.symbol, key.modifier, value, self.model)
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 BinaryOperator.new(key, :between, value, self.model)
197
- when Regexp then BinaryOperator.new(key, :match, value.inspect[1..-2], self.model)
198
- else BinaryOperator.new(key, :eq, value, self.model)
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
- class IndexFinder < Struct.new(:criteria, :ast, :index_name, :index_type, :rql_proc)
203
- def initialize(*args)
204
- super
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
- def could_find_index?
208
- !!self.index_name
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(*types)
216
- @usable_indexes = {}
217
- @usable_indexes[types] ||= begin
218
- indexes = criteria.model.indexes.values
219
- indexes = indexes.select { |i| types.include?(i.kind) } if types.present?
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 remove_from_ast(clauses)
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
- if index = get_usable_indexes.select { |i| clauses[i.name] }.first
284
+ get_usable_indexes.each do |index|
237
285
  clause = clauses[index.name]
238
- self.index_name = index.name
239
- self.ast = remove_from_ast([clause])
240
- self.index_type = clause.op == :between ? :between : :get_all
241
- self.rql_proc = case clause.op
242
- when :eq then ->(rql){ rql.get_all(clause.value, :index => index.aliased_name) }
243
- when :in then ->(rql){ rql.get_all(*clause.value, :index => index.aliased_name) }
244
- when :between then ->(rql){ rql.between(clause.value.min, clause.value.max, :index => index.aliased_name,
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 find_index_compound
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
- if index = get_usable_indexes(:compound).select { |i| i.what & clauses.keys == i.what }.first
303
+ get_usable_indexes(:kind => :compound, :multi => false).each do |index|
255
304
  indexed_clauses = index.what.map { |field| clauses[field] }
256
- self.index_name = index.name
257
- self.ast = remove_from_ast(indexed_clauses)
258
- self.index_type = :get_all
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 find_index_hidden_between
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
- if index = get_usable_indexes.select { |i| clauses[i.name] }.first
268
- op_clauses = Hash[clauses[index.name].map { |c| [c.op, c] }]
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
- self.index_name = index.name
273
- self.ast = remove_from_ast([left_bound, right_bound].compact)
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[:index] = index.aliased_name
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
- self.index_type = :between
280
- self.rql_proc = ->(rql){ rql.between(left_bound.try(:value), right_bound.try(:value), options) }
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 find_union_index
285
- indexes = []
286
- index_finder = self
287
-
288
- loop do
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 indexes.present? && !index_finder.ast
297
- self.ast = nil
298
- self.index_name = indexes.map(&:index_name)
299
- self.index_type = indexes.map(&:index_type)
300
- self.rql_proc = ->(rql){ indexes.map { |index| index.rql_proc.call(rql) }.reduce { |a,b| a.union(b) } }
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 find_index
305
- return if ast.nil? || criteria.without_index?
357
+ def find_strategy
358
+ return nil unless ast.try(:clauses).present? && !criteria.without_index?
306
359
  case ast.op
307
- when :and then find_index_compound || find_index_canonical || find_index_hidden_between
308
- when :or then find_union_index
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 { |index_finder| index_finder.find_index }
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 where_index_finder.could_find_index?
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 = where_index_finder.could_find_index? ? where_index_finder.ast : @options[:where_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
- self.alias_map[attr.to_s] = options[:as].to_s
17
- self.alias_reverse_map[options[:as].to_s] = attr.to_s
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, :foreign_key_as, :index, :validates, :required]
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, :as => options[:foreign_key_as], :index => options[:index])
33
- owner_model.validates(target_name, { :presence => true }) if options[:required]
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
- .reduce(Hash.new { |k,v| k[v] = [] }) { |h,(k,v)| h[k] << v; h }
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
- lambda { |target| set_inverses_of([target]) if target.is_a?(NoBrainer::Document) }
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, options={})
6
- model = case value
7
- when Array then PendingAtomicArray
8
- when Set then PendingAtomicSet
9
- else self
10
- end
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, options={})
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 write_access?
24
- !!@options[:write_access]
25
- end
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
- "<#{@field} with pending atomic operations>"
27
+ "<`#{@field}' with #{@ops.size} pending atomic operations>"
36
28
  end
37
- alias inspect to_s
29
+ alias_method :inspect, :to_s
38
30
 
39
- def method_missing(method_name, *a, &b)
40
- @ops << [method_name, a, b]
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.each { |method_name, a, b| value = value.__send__(method_name, *a, &b) }
48
- value
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 < PendingAtomic
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
- ensure_writeable!
82
+ modify_source!
84
83
  self
85
84
  end
86
85
  end
87
86
 
88
- class PendingAtomicSet < PendingAtomicArray
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
- ensure_writeable!
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, :real_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::DecoratedSymbol::MODIFIERS.keys
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
- if field_options.has_key?(:default) &&
50
- !@_attributes.has_key?(name)
51
-
52
- if opt = options[:missing_attributes]
53
- if (opt[:pluck] && !opt[:pluck][name]) ||
54
- (opt[:without] && opt[:without][name])
55
- next
56
- end
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
 
@@ -9,7 +9,7 @@ module NoBrainer::Document::Callbacks
9
9
  end
10
10
 
11
11
  def initialize(*args, &block)
12
- run_callbacks(:initialize) { _initialize(*args); true }
12
+ run_callbacks(:initialize) { _initialize(*args, &block); true }
13
13
  end
14
14
 
15
15
  def _create(*args, &block)
@@ -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, :as]
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
- as = options.delete(:as)
37
- as ||= fields[name][:as] if has_field?(name)
38
- as ||= name
39
- as = as.to_sym
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, as,
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
- as = {:as => options[:as]}
70
+ store_as = {:store_as => options[:store_as]}
61
71
  case options[:index]
62
72
  when nil then
63
- when Hash then index(attr, as.merge(options[:index]))
64
- when Symbol then index(attr, as.merge(options[:index] => true))
65
- when true then index(attr, as)
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
- if v = options.delete(type)
49
- v = Hash[v.flatten.map { |k| [k, true] }] if v.is_a?(Array)
50
- v = {v => true} if !v.is_a?(Hash)
51
- v = v.select { |k,_v| _v }
52
- v = v.with_indifferent_access
53
- next unless v.present?
54
-
55
- options[:missing_attributes] ||= {}
56
- options[:missing_attributes][type] = v
57
- end
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
- self.created_at = self.updated_at = Time.now
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')
@@ -20,4 +20,3 @@ class NoBrainer::Binary
20
20
  end
21
21
  extend NoBrainerExtentions
22
22
  end
23
-
@@ -23,4 +23,3 @@ class NoBrainer::Boolean
23
23
  end
24
24
  extend NoBrainerExtentions
25
25
  end
26
-
@@ -58,7 +58,6 @@ module NoBrainer::Document::Uniqueness
58
58
  subclass.unique_validators = self.unique_validators.dup
59
59
  super
60
60
  end
61
-
62
61
  end
63
62
 
64
63
  class UniquenessValidator < ActiveModel::EachValidator
@@ -23,11 +23,11 @@ module NoBrainer::Document::Validation
23
23
  module ClassMethods
24
24
  def _field(attr, options={})
25
25
  super
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)
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
@@ -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
@@ -3,7 +3,7 @@ class NoBrainer::QueryRunner::ConnectionLock < NoBrainer::QueryRunner::Middlewar
3
3
 
4
4
  def call(env)
5
5
  if NoBrainer::Config.per_thread_connection
6
- @runner.call(env)
6
+ @runner.call(env)
7
7
  else
8
8
  @@lock.synchronize { @runner.call(env) }
9
9
  end
@@ -18,18 +18,16 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
18
18
  private
19
19
 
20
20
  def reconnect(e, context)
21
- begin
22
- return false if context[:retries].zero?
23
- context[:retries] -= 1
21
+ return false if context[:retries].zero?
22
+ context[:retries] -= 1
24
23
 
25
- warn_reconnect(e)
26
- sleep 1
27
- NoBrainer.connection.reconnect(:noreply_wait => false)
28
- return true
29
- rescue StandardError => e
30
- retry if is_connection_error_exception?(e)
31
- raise
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 => [ :sync_indexes_quiet, :seed ]
22
+ task :setup => [:sync_indexes_quiet, :seed]
23
23
 
24
24
  desc 'Equivalent to :drop + :setup'
25
- task :reset => [ :drop, :setup ]
25
+ task :reset => [:drop, :setup]
26
26
 
27
27
  task :create => :environment do
28
28
  # noop
@@ -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, :DecoratedSymbol
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
 
@@ -12,7 +12,7 @@ module NoBrainer::Generators
12
12
  end
13
13
 
14
14
  def self.namespace
15
- super.gsub(/no_brainer/,'nobrainer')
15
+ super.gsub(/no_brainer/, 'nobrainer')
16
16
  end
17
17
  end
18
18
  end
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.18.1
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-08 00:00:00.000000000 Z
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.14.0.0
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.14.0.0
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