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