nobrainer 0.23.0 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/no_brainer/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
|