nobrainer 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/no_brainer/criteria/core.rb +5 -5
- data/lib/no_brainer/criteria/enumerable.rb +1 -1
- data/lib/no_brainer/criteria/first.rb +1 -1
- data/lib/no_brainer/criteria/order_by.rb +2 -0
- data/lib/no_brainer/criteria/where.rb +57 -49
- data/lib/no_brainer/document.rb +1 -1
- data/lib/no_brainer/document/association/belongs_to.rb +8 -4
- data/lib/no_brainer/document/association/core.rb +11 -0
- data/lib/no_brainer/document/association/has_many.rb +2 -2
- data/lib/no_brainer/document/association/has_one.rb +1 -1
- data/lib/no_brainer/document/attributes.rb +5 -0
- data/lib/no_brainer/document/callbacks.rb +1 -1
- data/lib/no_brainer/document/dynamic_attributes.rb +6 -0
- data/lib/no_brainer/document/persistance.rb +14 -30
- data/lib/no_brainer/document/validation.rb +1 -58
- data/lib/no_brainer/document/validation/core.rb +63 -0
- data/lib/no_brainer/document/validation/uniqueness.rb +2 -9
- data/lib/no_brainer/geo/base.rb +0 -1
- data/lib/nobrainer.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56337a0aa87999793ca7bc6ed3e1de79ef76b427
|
4
|
+
data.tar.gz: 6740efd7304818a5a71638a639b48301d16eb602
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69a4a851eb9e3a497a2bff26c65933437e3155b23f6573e1a9d7fcaa04980f59964b7d6f59383e3c7b9bb27014ab95eda52ca8be38d4b4f82144d2c389e211b6
|
7
|
+
data.tar.gz: 03968de9d8391cd208938420fa19d1aa1f2c76191bf938132f5c04dce33f673e725f5336390c010bb70cf3ff660a79a0b7dd72cc27bca994ba6411445f797948
|
@@ -32,11 +32,6 @@ module NoBrainer::Criteria::Core
|
|
32
32
|
to_rql.inspect rescue super
|
33
33
|
end
|
34
34
|
|
35
|
-
def run(&block)
|
36
|
-
block ||= proc { to_rql }
|
37
|
-
NoBrainer.run(:criteria => self, &block)
|
38
|
-
end
|
39
|
-
|
40
35
|
def merge!(criteria, options={})
|
41
36
|
criteria.options.each do |k,v|
|
42
37
|
merge_proc = self.class.options_definitions[k]
|
@@ -61,6 +56,11 @@ module NoBrainer::Criteria::Core
|
|
61
56
|
merge(self.class.new(options), merge_options)
|
62
57
|
end
|
63
58
|
|
59
|
+
def run(&block)
|
60
|
+
block ||= proc { to_rql }
|
61
|
+
NoBrainer.run(:criteria => self, &block)
|
62
|
+
end
|
63
|
+
|
64
64
|
def compile_rql_pass1
|
65
65
|
# This method is overriden by other modules.
|
66
66
|
raise "Criteria not bound to a model" unless model
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module NoBrainer::Criteria::Where
|
2
|
-
NON_CHAINABLE_OPERATORS = %w(in
|
3
|
-
CHAINABLE_OPERATORS = %w(any all).map(&:to_sym)
|
2
|
+
NON_CHAINABLE_OPERATORS = %w(in eq gt ge gte lt le lte defined near intersects).map(&:to_sym)
|
3
|
+
CHAINABLE_OPERATORS = %w(not any all).map(&:to_sym)
|
4
4
|
OPERATORS = CHAINABLE_OPERATORS + NON_CHAINABLE_OPERATORS
|
5
5
|
|
6
6
|
require 'symbol_decoration'
|
@@ -134,12 +134,9 @@ module NoBrainer::Criteria::Where
|
|
134
134
|
when :in then RethinkDB::RQL.new.expr(value).contains(lvalue)
|
135
135
|
when :intersects then lvalue.intersects(value.to_rql)
|
136
136
|
when :near
|
137
|
-
options
|
138
|
-
|
139
|
-
|
140
|
-
# XXX max_results is not used, seems to be a workaround of rethinkdb index implemetnation.
|
141
|
-
_ = options.delete(:max_results)
|
142
|
-
RethinkDB::RQL.new.distance(lvalue, point.to_rql, options) <= max_dist
|
137
|
+
# XXX options[:max_results] is not used, seems to be a workaround of rethinkdb index implementation.
|
138
|
+
circle = value[:circle]
|
139
|
+
RethinkDB::RQL.new.distance(lvalue, circle.center.to_rql, circle.options) <= circle.radius
|
143
140
|
else lvalue.__send__(op, value)
|
144
141
|
end
|
145
142
|
end
|
@@ -176,18 +173,23 @@ module NoBrainer::Criteria::Where
|
|
176
173
|
raise "Use a geo object with `intersects`" unless value.is_a?(NoBrainer::Geo::Base)
|
177
174
|
value
|
178
175
|
when :near
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
176
|
+
case value
|
177
|
+
when Hash
|
178
|
+
options = NoBrainer::Geo::Base.normalize_geo_options(value)
|
179
|
+
|
180
|
+
options[:radius] = options.delete(:max_distance) if options[:max_distance]
|
181
|
+
options[:radius] = options.delete(:max_dist) if options[:max_dist]
|
182
|
+
options[:center] = options.delete(:point) if options[:point]
|
183
|
+
|
184
|
+
unless options[:circle]
|
185
|
+
unless options[:center] && options[:radius]
|
186
|
+
raise "`near' takes something like {:center => P, :radius => d}"
|
187
|
+
end
|
188
|
+
{ :circle => NoBrainer::Geo::Circle.new(options), :max_results => options[:max_results] }
|
189
|
+
end
|
190
|
+
when NoBrainer::Geo::Circle then { :circle => value }
|
191
|
+
else raise "Incorrect use of `near': rvalue must be a hash or a circle"
|
188
192
|
end
|
189
|
-
|
190
|
-
options
|
191
193
|
else
|
192
194
|
case key_modifier
|
193
195
|
when :scalar then model.cast_user_to_db_for(key, value)
|
@@ -202,24 +204,28 @@ module NoBrainer::Criteria::Where
|
|
202
204
|
|
203
205
|
case association
|
204
206
|
when NoBrainer::Document::Association::BelongsTo::Metadata then association.foreign_key
|
205
|
-
else ensure_valid_key!(key); key
|
207
|
+
else model.ensure_valid_key!(key); key
|
206
208
|
end
|
207
209
|
end
|
208
|
-
|
209
|
-
def ensure_valid_key!(key)
|
210
|
-
return if model.has_field?(key) || model.has_index?(key) || model < NoBrainer::Document::DynamicAttributes
|
211
|
-
raise NoBrainer::Error::UnknownAttribute, "`#{key}' is not a declared attribute of #{model}"
|
212
|
-
end
|
213
210
|
end
|
214
211
|
|
215
|
-
class UnaryOperator < Struct.new(:op, :
|
212
|
+
class UnaryOperator < Struct.new(:op, :clause)
|
216
213
|
def simplify
|
217
|
-
|
214
|
+
simplified_clause = self.clause.simplify
|
215
|
+
|
216
|
+
case simplified_clause
|
217
|
+
when UnaryOperator then
|
218
|
+
case [self.op, simplified_clause.op]
|
219
|
+
when [:not, :not] then simplified_clause.clause
|
220
|
+
else self.class.new(op, simplified_clause)
|
221
|
+
end
|
222
|
+
else self.class.new(op, simplified_clause)
|
223
|
+
end
|
218
224
|
end
|
219
225
|
|
220
226
|
def to_rql(doc)
|
221
227
|
case op
|
222
|
-
when :not then
|
228
|
+
when :not then clause.to_rql(doc).not
|
223
229
|
end
|
224
230
|
end
|
225
231
|
end
|
@@ -256,15 +262,12 @@ module NoBrainer::Criteria::Where
|
|
256
262
|
when :_and then parse_multi_value(:and, value, :safe => true)
|
257
263
|
when :_or then parse_multi_value(:or, value, :safe => true)
|
258
264
|
when :not then UnaryOperator.new(:not, parse_clause(value))
|
259
|
-
when String, Symbol then
|
265
|
+
when String, Symbol then instantiate_binary_op(key, :eq, value)
|
260
266
|
when Symbol::Decoration then
|
261
267
|
case key.decorator
|
262
|
-
when :any, :all then
|
263
|
-
when :
|
264
|
-
when :
|
265
|
-
when :gte then parse_clause(key.symbol.ge => value)
|
266
|
-
when :lte then parse_clause(key.symbol.le => value)
|
267
|
-
when :eq then parse_clause_stub_eq(key.symbol, value)
|
268
|
+
when :any, :all, :not then instantiate_binary_op(key, :eq, value)
|
269
|
+
when :gte then instantiate_binary_op(key.symbol, :ge, value)
|
270
|
+
when :lte then instantiate_binary_op(key.symbol, :le, value)
|
268
271
|
else instantiate_binary_op(key.symbol, key.decorator, value)
|
269
272
|
end
|
270
273
|
else raise "Invalid key: #{key}"
|
@@ -282,14 +285,6 @@ module NoBrainer::Criteria::Where
|
|
282
285
|
MultiOperator.new(op, value.map { |v| parse_clause(v) })
|
283
286
|
end
|
284
287
|
|
285
|
-
def parse_clause_stub_eq(key, value)
|
286
|
-
case value
|
287
|
-
when Range then instantiate_binary_op(key, :between, value)
|
288
|
-
when Regexp then instantiate_binary_op(key, :match, translate_regexp_to_re2_syntax(value))
|
289
|
-
else instantiate_binary_op(key, :eq, value)
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
288
|
def translate_regexp_to_re2_syntax(value)
|
294
289
|
# Ruby always uses what RE2 calls "multiline mode" (the "m" flag),
|
295
290
|
# meaning that "foo\nbar" matches /^bar$/.
|
@@ -304,8 +299,19 @@ module NoBrainer::Criteria::Where
|
|
304
299
|
end
|
305
300
|
|
306
301
|
def instantiate_binary_op(key, op, value)
|
302
|
+
op, value = case value
|
303
|
+
when Range then [:between, value]
|
304
|
+
when Regexp then [:match, translate_regexp_to_re2_syntax(value)]
|
305
|
+
else [:eq, value]
|
306
|
+
end if op == :eq
|
307
|
+
|
307
308
|
case key
|
308
|
-
when Symbol::Decoration
|
309
|
+
when Symbol::Decoration
|
310
|
+
raise "Use only one .not, .all or .any modifiers in the query" if key.symbol.is_a?(Symbol::Decoration)
|
311
|
+
case key.decorator
|
312
|
+
when :any, :all then BinaryOperator.new(key.symbol, key.decorator, op, value, self.model)
|
313
|
+
when :not then UnaryOperator.new(:not, BinaryOperator.new(key.symbol, :scalar, op, value, self.model))
|
314
|
+
end
|
309
315
|
else BinaryOperator.new(key, :scalar, op, value, self.model)
|
310
316
|
end
|
311
317
|
end
|
@@ -319,12 +325,12 @@ module NoBrainer::Criteria::Where
|
|
319
325
|
|
320
326
|
def rql_proc
|
321
327
|
lambda do |rql|
|
328
|
+
return RethinkDB::RQL.new.expr([]) if rql_op == :get_all && rql_args.empty?
|
329
|
+
|
322
330
|
opt = (rql_options || {}).merge(:index => index.aliased_name)
|
323
331
|
r = rql.__send__(rql_op, *rql_args, opt)
|
324
332
|
r = r.map { |i| i['doc'] } if rql_op == :get_nearest
|
325
|
-
|
326
|
-
# TODO coerce_to: waiting for issue #3346
|
327
|
-
r = r.coerce_to('array').distinct if index.multi && !index_finder.criteria.options[:without_distinct]
|
333
|
+
r = r.distinct if index.multi && !index_finder.criteria.options[:without_distinct]
|
328
334
|
r
|
329
335
|
end
|
330
336
|
end
|
@@ -357,8 +363,10 @@ module NoBrainer::Criteria::Where
|
|
357
363
|
when :intersects then [:get_intersecting, clause.value.to_rql]
|
358
364
|
when :near
|
359
365
|
options = clause.value.dup
|
360
|
-
|
361
|
-
|
366
|
+
circle = options.delete(:circle)
|
367
|
+
options.delete(:max_results) if options[:max_results].nil?
|
368
|
+
options[:max_dist] = circle.radius
|
369
|
+
[:get_nearest, circle.center.to_rql, circle.options.merge(options)]
|
362
370
|
when :eq then [:get_all, [clause.value]]
|
363
371
|
when :in then [:get_all, clause.value]
|
364
372
|
when :between then [:between, [clause.value.min, clause.value.max],
|
data/lib/no_brainer/document.rb
CHANGED
@@ -5,7 +5,7 @@ module NoBrainer::Document
|
|
5
5
|
extend NoBrainer::Autoload
|
6
6
|
|
7
7
|
autoload_and_include :Core, :StoreIn, :InjectionLayer, :Attributes, :Readonly,
|
8
|
-
:Persistance, :
|
8
|
+
:Persistance, :Callbacks, :Validation, :Types, :Dirty, :PrimaryKey,
|
9
9
|
:Association, :Serialization, :Criteria, :Polymorphic, :Index, :Aliases,
|
10
10
|
:MissingAttributes, :LazyFetch, :AtomicOps
|
11
11
|
|
@@ -15,7 +15,7 @@ class NoBrainer::Document::Association::BelongsTo
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def target_model
|
18
|
-
(options[:class_name] || target_name.to_s.camelize)
|
18
|
+
get_model_by_name(options[:class_name] || target_name.to_s.camelize)
|
19
19
|
end
|
20
20
|
|
21
21
|
def base_criteria
|
@@ -24,14 +24,18 @@ class NoBrainer::Document::Association::BelongsTo
|
|
24
24
|
|
25
25
|
def hook
|
26
26
|
super
|
27
|
-
# XXX We are loading the target_model unless the primary_key is
|
28
|
-
#
|
29
|
-
# Oh well.
|
27
|
+
# XXX We are loading the target_model unless the primary_key is specified.
|
28
|
+
# This may eager load a part of the application Oh well.
|
30
29
|
|
31
30
|
# TODO if the primary key of the target_model changes, we need to revisit
|
32
31
|
# our default foreign_key/primary_key value
|
33
32
|
|
34
33
|
# TODO set the type of the foreign key to be the same as the target's primary key
|
34
|
+
if owner_model.association_metadata.values.any? { |assoc|
|
35
|
+
assoc.is_a?(self.class) && assoc != self && assoc.foreign_key == foreign_key }
|
36
|
+
raise "Cannot declare `#{target_name}' in #{owner_model}: the foreign_key `#{foreign_key}' is already used"
|
37
|
+
end
|
38
|
+
|
35
39
|
owner_model.field(foreign_key, :store_as => options[:foreign_key_store_as], :index => options[:index])
|
36
40
|
|
37
41
|
unless options[:validates] == false
|
@@ -46,6 +46,17 @@ module NoBrainer::Document::Association::Core
|
|
46
46
|
end
|
47
47
|
RUBY
|
48
48
|
end
|
49
|
+
|
50
|
+
def get_model_by_name(model_name)
|
51
|
+
return model_name if model_name.is_a?(Module)
|
52
|
+
|
53
|
+
model_name = model_name.to_s
|
54
|
+
current_module = @owner_model.parent
|
55
|
+
return model_name.constantize if current_module == Object
|
56
|
+
return model_name.constantize if model_name =~ /^::/
|
57
|
+
return model_name.constantize if !current_module.const_defined?(model_name)
|
58
|
+
current_module.const_get(model_name)
|
59
|
+
end
|
49
60
|
end
|
50
61
|
|
51
62
|
included { attr_accessor :metadata, :owner }
|
@@ -7,7 +7,7 @@ class NoBrainer::Document::Association::HasMany
|
|
7
7
|
extend NoBrainer::Document::Association::EagerLoader::Generic
|
8
8
|
|
9
9
|
def foreign_key
|
10
|
-
options[:foreign_key].try(:to_sym) || :"#{owner_model.name.underscore}_#{primary_key}"
|
10
|
+
options[:foreign_key].try(:to_sym) || :"#{owner_model.name.split('::').last.underscore}_#{primary_key}"
|
11
11
|
end
|
12
12
|
|
13
13
|
def primary_key
|
@@ -15,7 +15,7 @@ class NoBrainer::Document::Association::HasMany
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def target_model
|
18
|
-
(options[:class_name] || target_name.to_s.singularize.camelize)
|
18
|
+
get_model_by_name(options[:class_name] || target_name.to_s.singularize.camelize)
|
19
19
|
end
|
20
20
|
|
21
21
|
def base_criteria
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class NoBrainer::Document::Association::HasOne < NoBrainer::Document::Association::HasMany
|
2
2
|
class Metadata < NoBrainer::Document::Association::HasMany::Metadata
|
3
3
|
def target_model
|
4
|
-
(options[:class_name] || target_name.to_s.camelize)
|
4
|
+
get_model_by_name(options[:class_name] || target_name.to_s.camelize)
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
@@ -157,5 +157,10 @@ module NoBrainer::Document::Attributes
|
|
157
157
|
def has_field?(attr)
|
158
158
|
!!fields[attr.to_sym]
|
159
159
|
end
|
160
|
+
|
161
|
+
def ensure_valid_key!(key)
|
162
|
+
return if has_field?(key) || has_index?(key)
|
163
|
+
raise NoBrainer::Error::UnknownAttribute, "`#{key}' is not a declared attribute of #{self}"
|
164
|
+
end
|
160
165
|
end
|
161
166
|
end
|
@@ -60,12 +60,11 @@ module NoBrainer::Document::Persistance
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def _create(options={})
|
63
|
-
return false if options[:validate] != false && !valid?(nil, :clear_errors => false)
|
64
|
-
|
65
63
|
attrs = self.class.persistable_attributes(@_attributes, :instance => self)
|
66
64
|
result = NoBrainer.run(self.class.rql_table.insert(attrs))
|
67
65
|
self.pk_value ||= result['generated_keys'].to_a.first
|
68
66
|
@new_record = false
|
67
|
+
unlock_unique_fields # just an optimization for the uniquness validation
|
69
68
|
true
|
70
69
|
end
|
71
70
|
|
@@ -75,8 +74,6 @@ module NoBrainer::Document::Persistance
|
|
75
74
|
end
|
76
75
|
|
77
76
|
def _update_only_changed_attrs(options={})
|
78
|
-
return false if options[:validate] != false && !valid?(nil, :clear_errors => false)
|
79
|
-
|
80
77
|
# We won't be using the `changes` values, because they went through
|
81
78
|
# read_attribute(), and we want the raw values.
|
82
79
|
attrs = Hash[self.changed.map do |k|
|
@@ -87,26 +84,24 @@ module NoBrainer::Document::Persistance
|
|
87
84
|
[k, attr]
|
88
85
|
end]
|
89
86
|
_update(attrs) if attrs.present?
|
87
|
+
unlock_unique_fields # just an optimization for the uniquness validation
|
90
88
|
true
|
91
89
|
end
|
92
90
|
|
93
|
-
def _save?(options)
|
91
|
+
def _save?(options={})
|
94
92
|
new_record? ? _create(options) : _update_only_changed_attrs(options)
|
95
93
|
end
|
96
94
|
|
97
95
|
def save?(options={})
|
98
|
-
errors.clear
|
99
96
|
_save?(options)
|
100
97
|
end
|
101
98
|
|
102
|
-
def save(*args)
|
99
|
+
def save!(*args)
|
103
100
|
save?(*args) or raise NoBrainer::Error::DocumentInvalid, self
|
104
|
-
nil
|
105
101
|
end
|
106
102
|
|
107
|
-
def save
|
108
|
-
save(*args)
|
109
|
-
:you_should_be_using_the_non_bang_version_of_save
|
103
|
+
def save(*args)
|
104
|
+
save?(*args)
|
110
105
|
end
|
111
106
|
|
112
107
|
def update?(attrs, options={})
|
@@ -114,27 +109,13 @@ module NoBrainer::Document::Persistance
|
|
114
109
|
save?(options)
|
115
110
|
end
|
116
111
|
|
117
|
-
def update(*args)
|
118
|
-
update?(*args) or raise NoBrainer::Error::DocumentInvalid, self
|
119
|
-
nil
|
120
|
-
end
|
121
|
-
|
122
112
|
def update!(*args)
|
123
|
-
update(*args)
|
124
|
-
:you_should_be_using_the_non_bang_version_of_update
|
113
|
+
update?(*args) or raise NoBrainer::Error::DocumentInvalid, self
|
125
114
|
end
|
126
115
|
alias_method :update_attributes!, :update!
|
127
116
|
|
128
|
-
def
|
129
|
-
update?(*args)
|
130
|
-
end
|
131
|
-
|
132
|
-
def update_attributes(*args)
|
133
|
-
update(*args).tap { STDERR.puts "[NoBrainer] update_attributes() is deprecated. Please use update() instead" }
|
134
|
-
end
|
135
|
-
|
136
|
-
def update_attributes!(*args)
|
137
|
-
update!(*args).tap { STDERR.puts "[NoBrainer] update_attributes!() is deprecated. Please use update() instead" }
|
117
|
+
def update(*args)
|
118
|
+
update?(*args)
|
138
119
|
end
|
139
120
|
|
140
121
|
def delete
|
@@ -152,9 +133,12 @@ module NoBrainer::Document::Persistance
|
|
152
133
|
|
153
134
|
module ClassMethods
|
154
135
|
def create(attrs={}, options={})
|
155
|
-
new(attrs, options).tap { |doc| doc.save(options) }
|
136
|
+
new(attrs, options).tap { |doc| doc.save?(options) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def create!(attrs={}, options={})
|
140
|
+
new(attrs, options).tap { |doc| doc.save!(options) }
|
156
141
|
end
|
157
|
-
alias_method :create!, :create
|
158
142
|
|
159
143
|
def insert_all(*args)
|
160
144
|
docs = args.shift
|
@@ -1,63 +1,6 @@
|
|
1
1
|
module NoBrainer::Document::Validation
|
2
2
|
extend NoBrainer::Autoload
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
include ActiveModel::Validations
|
5
|
-
include ActiveModel::Validations::Callbacks
|
6
4
|
|
7
|
-
autoload_and_include :Uniqueness, :NotNull
|
8
|
-
|
9
|
-
included do
|
10
|
-
# We don't want before_validation returning false to halt the chain.
|
11
|
-
define_callbacks :validation, :skip_after_callbacks_if_terminated => true,
|
12
|
-
:scope => [:kind, :name], :terminator => proc { false }
|
13
|
-
end
|
14
|
-
|
15
|
-
def valid?(context=nil, options={})
|
16
|
-
context ||= new_record? ? :create : :update
|
17
|
-
|
18
|
-
# XXX Monkey Patching, because we need to have control on errors.clear
|
19
|
-
current_context, self.validation_context = validation_context, context
|
20
|
-
errors.clear unless options[:clear_errors] == false
|
21
|
-
run_validations!
|
22
|
-
ensure
|
23
|
-
self.validation_context = current_context
|
24
|
-
end
|
25
|
-
|
26
|
-
SHORTHANDS = { :format => :format, :length => :length, :required => :presence,
|
27
|
-
:uniq => :uniqueness, :unique => :uniqueness, :in => :inclusion }
|
28
|
-
|
29
|
-
module ClassMethods
|
30
|
-
def _field(attr, options={})
|
31
|
-
super
|
32
|
-
|
33
|
-
shorthands = SHORTHANDS
|
34
|
-
shorthands = shorthands.merge(:required => :not_null) if options[:type] == NoBrainer::Boolean
|
35
|
-
shorthands.each { |k,v| validates(attr, v => options[k]) if options.has_key?(k) }
|
36
|
-
|
37
|
-
validates(attr, options[:validates]) if options[:validates]
|
38
|
-
validates(attr, :length => { :minimum => options[:min_length] }) if options[:min_length]
|
39
|
-
validates(attr, :length => { :maximum => options[:max_length] }) if options[:max_length]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class ActiveModel::EachValidator
|
45
|
-
def should_validate_field?(record, attribute)
|
46
|
-
return true unless record.is_a?(NoBrainer::Document)
|
47
|
-
return true if record.new_record?
|
48
|
-
|
49
|
-
attr_changed = "#{attribute}_changed?"
|
50
|
-
return record.respond_to?(attr_changed) ? record.__send__(attr_changed) : true
|
51
|
-
end
|
52
|
-
|
53
|
-
# XXX Monkey Patching :(
|
54
|
-
def validate(record)
|
55
|
-
attributes.each do |attribute|
|
56
|
-
next unless should_validate_field?(record, attribute) # <--- Added
|
57
|
-
value = record.read_attribute_for_validation(attribute)
|
58
|
-
next if value.is_a?(NoBrainer::Document::AtomicOps::PendingAtomic) # <--- Added
|
59
|
-
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
60
|
-
validate_each(record, attribute, value)
|
61
|
-
end
|
62
|
-
end
|
5
|
+
autoload_and_include :Core, :Uniqueness, :NotNull
|
63
6
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module NoBrainer::Document::Validation::Core
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
include ActiveModel::Validations
|
4
|
+
include ActiveModel::Validations::Callbacks
|
5
|
+
|
6
|
+
included do
|
7
|
+
# We don't want before_validation returning false to halt the chain.
|
8
|
+
define_callbacks :validation, :skip_after_callbacks_if_terminated => true,
|
9
|
+
:scope => [:kind, :name], :terminator => proc { false }
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid?(context=nil, options={})
|
13
|
+
super(context || (new_record? ? :create : :update))
|
14
|
+
end
|
15
|
+
|
16
|
+
def save?(options={})
|
17
|
+
options = { :validate => true }.merge(options)
|
18
|
+
|
19
|
+
if options[:validate]
|
20
|
+
valid? ? super : false
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
SHORTHANDS = { :format => :format, :length => :length, :required => :presence,
|
27
|
+
:uniq => :uniqueness, :unique => :uniqueness, :in => :inclusion }
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def _field(attr, options={})
|
31
|
+
super
|
32
|
+
|
33
|
+
shorthands = SHORTHANDS
|
34
|
+
shorthands = shorthands.merge(:required => :not_null) if options[:type] == NoBrainer::Boolean
|
35
|
+
shorthands.each { |k,v| validates(attr, v => options[k]) if options.has_key?(k) }
|
36
|
+
|
37
|
+
validates(attr, options[:validates]) if options[:validates]
|
38
|
+
validates(attr, :length => { :minimum => options[:min_length] }) if options[:min_length]
|
39
|
+
validates(attr, :length => { :maximum => options[:max_length] }) if options[:max_length]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class ActiveModel::EachValidator
|
45
|
+
def should_validate_field?(record, attribute)
|
46
|
+
return true unless record.is_a?(NoBrainer::Document)
|
47
|
+
return true if record.new_record?
|
48
|
+
|
49
|
+
attr_changed = "#{attribute}_changed?"
|
50
|
+
return record.respond_to?(attr_changed) ? record.__send__(attr_changed) : true
|
51
|
+
end
|
52
|
+
|
53
|
+
# XXX Monkey Patching :(
|
54
|
+
def validate(record)
|
55
|
+
attributes.each do |attribute|
|
56
|
+
next unless should_validate_field?(record, attribute) # <--- Added
|
57
|
+
value = record.read_attribute_for_validation(attribute)
|
58
|
+
next if value.is_a?(NoBrainer::Document::AtomicOps::PendingAtomic) # <--- Added
|
59
|
+
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
60
|
+
validate_each(record, attribute, value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,14 +1,7 @@
|
|
1
1
|
module NoBrainer::Document::Validation::Uniqueness
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
def
|
5
|
-
lock_unique_fields
|
6
|
-
super
|
7
|
-
ensure
|
8
|
-
unlock_unique_fields
|
9
|
-
end
|
10
|
-
|
11
|
-
def _update_only_changed_attrs(options={})
|
4
|
+
def save?(options={})
|
12
5
|
lock_unique_fields
|
13
6
|
super
|
14
7
|
ensure
|
@@ -93,7 +86,7 @@ module NoBrainer::Document::Validation::Uniqueness
|
|
93
86
|
end
|
94
87
|
|
95
88
|
def exclude_doc(criteria, doc)
|
96
|
-
criteria.where(doc.class.pk_name.
|
89
|
+
criteria.where(doc.class.pk_name.not => doc.pk_value)
|
97
90
|
end
|
98
91
|
end
|
99
92
|
end
|
data/lib/no_brainer/geo/base.rb
CHANGED
data/lib/nobrainer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nobrainer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.24.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: 2015-
|
11
|
+
date: 2015-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rethinkdb
|
@@ -158,6 +158,7 @@ files:
|
|
158
158
|
- lib/no_brainer/document/types/text.rb
|
159
159
|
- lib/no_brainer/document/types/time.rb
|
160
160
|
- lib/no_brainer/document/validation.rb
|
161
|
+
- lib/no_brainer/document/validation/core.rb
|
161
162
|
- lib/no_brainer/document/validation/not_null.rb
|
162
163
|
- lib/no_brainer/document/validation/uniqueness.rb
|
163
164
|
- lib/no_brainer/error.rb
|