nobrainer 0.20.0 → 0.22.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/autoload.rb +0 -5
- data/lib/no_brainer/config.rb +14 -9
- data/lib/no_brainer/connection.rb +1 -3
- data/lib/no_brainer/criteria/aggregate.rb +2 -2
- data/lib/no_brainer/criteria/cache.rb +8 -3
- data/lib/no_brainer/criteria/core.rb +1 -5
- data/lib/no_brainer/criteria/delete.rb +1 -1
- data/lib/no_brainer/criteria/eager_load.rb +51 -0
- data/lib/no_brainer/criteria/find.rb +27 -0
- data/lib/no_brainer/criteria/order_by.rb +1 -1
- data/lib/no_brainer/criteria/scope.rb +3 -10
- data/lib/no_brainer/criteria/update.rb +8 -6
- data/lib/no_brainer/criteria/where.rb +50 -13
- data/lib/no_brainer/criteria.rb +3 -2
- data/lib/no_brainer/document/aliases.rb +0 -8
- data/lib/no_brainer/document/association/belongs_to.rb +6 -2
- data/lib/no_brainer/document/association/core.rb +5 -4
- data/lib/no_brainer/document/association/eager_loader.rb +7 -8
- data/lib/no_brainer/document/association/has_many.rb +22 -8
- data/lib/no_brainer/document/association/has_many_through.rb +12 -3
- data/lib/no_brainer/document/atomic_ops.rb +63 -61
- data/lib/no_brainer/document/attributes.rb +11 -3
- data/lib/no_brainer/document/core.rb +5 -2
- data/lib/no_brainer/document/criteria.rb +14 -17
- data/lib/no_brainer/document/dirty.rb +11 -16
- data/lib/no_brainer/document/index/meta_store.rb +1 -1
- data/lib/no_brainer/document/index.rb +0 -6
- data/lib/no_brainer/document/persistance.rb +13 -3
- data/lib/no_brainer/document/store_in.rb +2 -2
- data/lib/no_brainer/document/types/binary.rb +2 -6
- data/lib/no_brainer/document/types/boolean.rb +2 -3
- data/lib/no_brainer/document/types/date.rb +2 -2
- data/lib/no_brainer/document/types/float.rb +2 -2
- data/lib/no_brainer/document/types/geo.rb +1 -0
- data/lib/no_brainer/document/types/integer.rb +2 -2
- data/lib/no_brainer/document/types/set.rb +2 -2
- data/lib/no_brainer/document/types/string.rb +5 -2
- data/lib/no_brainer/document/types/symbol.rb +2 -2
- data/lib/no_brainer/document/types/text.rb +18 -0
- data/lib/no_brainer/document/types/time.rb +2 -2
- data/lib/no_brainer/document/types.rb +13 -12
- data/lib/no_brainer/document/validation/not_null.rb +15 -0
- data/lib/no_brainer/document/{uniqueness.rb → validation/uniqueness.rb} +11 -10
- data/lib/no_brainer/document/validation.rb +35 -6
- data/lib/no_brainer/document.rb +2 -2
- data/lib/no_brainer/error.rb +20 -23
- data/lib/no_brainer/locale/en.yml +1 -0
- data/lib/no_brainer/lock.rb +114 -0
- data/lib/no_brainer/query_runner/database_on_demand.rb +0 -1
- data/lib/no_brainer/query_runner/missing_index.rb +1 -1
- data/lib/no_brainer/query_runner/run_options.rb +0 -3
- data/lib/no_brainer/query_runner/table_on_demand.rb +2 -3
- data/lib/no_brainer/rql.rb +0 -4
- data/lib/nobrainer.rb +1 -1
- metadata +9 -4
- data/lib/no_brainer/criteria/preload.rb +0 -44
|
@@ -2,7 +2,7 @@ class NoBrainer::Document::Association::HasManyThrough
|
|
|
2
2
|
include NoBrainer::Document::Association::Core
|
|
3
3
|
|
|
4
4
|
class Metadata
|
|
5
|
-
VALID_OPTIONS = [:through]
|
|
5
|
+
VALID_OPTIONS = [:through, :scope]
|
|
6
6
|
include NoBrainer::Document::Association::Core::Metadata
|
|
7
7
|
|
|
8
8
|
def through_association_name
|
|
@@ -14,10 +14,18 @@ class NoBrainer::Document::Association::HasManyThrough
|
|
|
14
14
|
raise "#{through_association_name} association not found"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def eager_load(docs,
|
|
17
|
+
def eager_load(docs, additional_criteria=nil)
|
|
18
|
+
criteria = target_model.instance_exec(&options[:scope]) if options[:scope]
|
|
19
|
+
criteria = criteria ? criteria.merge(additional_criteria) : additional_criteria if additional_criteria
|
|
18
20
|
NoBrainer::Document::Association::EagerLoader.new
|
|
19
21
|
.eager_load_association(through_association.eager_load(docs), target_name, criteria)
|
|
20
22
|
end
|
|
23
|
+
|
|
24
|
+
def target_model
|
|
25
|
+
meta = through_association.target_model.association_metadata
|
|
26
|
+
association = meta[target_name.to_sym] || meta[target_name.to_s.singularize.to_sym]
|
|
27
|
+
association.target_model
|
|
28
|
+
end
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
def read
|
|
@@ -26,6 +34,7 @@ class NoBrainer::Document::Association::HasManyThrough
|
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
def write(new_children)
|
|
29
|
-
raise "You can't assign
|
|
37
|
+
raise "You can't assign `#{target_name}'. " \
|
|
38
|
+
"Instead, you must modify delete and create `#{target_model}' manually."
|
|
30
39
|
end
|
|
31
40
|
end
|
|
@@ -2,22 +2,38 @@ module NoBrainer::Document::AtomicOps
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
class PendingAtomic
|
|
5
|
+
attr_accessor :type
|
|
6
|
+
|
|
5
7
|
def self._new(instance, field, value, is_user_value)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
type = instance.class.fields[field.to_sym].try(:[], :type)
|
|
9
|
+
type ||= value.class unless value.nil?
|
|
10
|
+
|
|
11
|
+
case
|
|
12
|
+
when type == Array then PendingAtomicArray
|
|
13
|
+
when type == Set then PendingAtomicSet
|
|
9
14
|
else self
|
|
10
|
-
end.new(instance, field, value, is_user_value)
|
|
15
|
+
end.new(instance, field, value, is_user_value, type)
|
|
11
16
|
end
|
|
12
17
|
|
|
13
|
-
def initialize(instance, field, value, is_user_value)
|
|
18
|
+
def initialize(instance, field, value, is_user_value, type)
|
|
14
19
|
@instance = instance
|
|
15
20
|
@field = field.to_s
|
|
16
21
|
@value = value
|
|
17
22
|
@is_user_value = is_user_value
|
|
23
|
+
@type = type
|
|
18
24
|
@ops = []
|
|
19
25
|
end
|
|
20
26
|
|
|
27
|
+
def default_value
|
|
28
|
+
case
|
|
29
|
+
when @type == Array then []
|
|
30
|
+
when @type == Set then []
|
|
31
|
+
when @type == Integer then 0
|
|
32
|
+
when @type == Float then 0.0
|
|
33
|
+
when @type == String then ""
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
21
37
|
def initialize_copy(other)
|
|
22
38
|
super
|
|
23
39
|
@ops = @ops.dup
|
|
@@ -29,6 +45,11 @@ module NoBrainer::Document::AtomicOps
|
|
|
29
45
|
alias_method :inspect, :to_s
|
|
30
46
|
|
|
31
47
|
def method_missing(method, *a, &b)
|
|
48
|
+
if method == :<<
|
|
49
|
+
method = :append
|
|
50
|
+
modify_source!
|
|
51
|
+
end
|
|
52
|
+
|
|
32
53
|
@ops << [method, a, b]
|
|
33
54
|
self
|
|
34
55
|
end
|
|
@@ -36,11 +57,10 @@ module NoBrainer::Document::AtomicOps
|
|
|
36
57
|
def compile_rql_value(rql_doc)
|
|
37
58
|
field = @instance.class.lookup_field_alias(@field)
|
|
38
59
|
value = @is_user_value ? RethinkDB::RQL.new.expr(@value) : rql_doc[field]
|
|
60
|
+
value = value.default(default_value) if default_value
|
|
39
61
|
@ops.reduce(value) { |v, (method, a, b)| v.__send__(method, *a, &b) }
|
|
40
62
|
end
|
|
41
|
-
end
|
|
42
63
|
|
|
43
|
-
class PendingAtomicContainer < PendingAtomic
|
|
44
64
|
def modify_source!
|
|
45
65
|
unless @instance._is_attribute_touched?(@field)
|
|
46
66
|
@instance.write_attribute(@field, self)
|
|
@@ -48,56 +68,59 @@ module NoBrainer::Document::AtomicOps
|
|
|
48
68
|
end
|
|
49
69
|
end
|
|
50
70
|
|
|
51
|
-
class
|
|
52
|
-
def
|
|
53
|
-
@ops << [:
|
|
71
|
+
class PendingAtomicContainer < PendingAtomic
|
|
72
|
+
def &(value)
|
|
73
|
+
@ops << [:set_intersection, [value.to_a]]
|
|
54
74
|
self
|
|
55
75
|
end
|
|
56
|
-
|
|
76
|
+
|
|
77
|
+
def |(value)
|
|
78
|
+
@ops << [:set_union, [value.to_a]]
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add(v); self + v; end
|
|
83
|
+
def difference(v); self - v; end
|
|
84
|
+
def intersection(v); self & v; end
|
|
85
|
+
def union(v); self | v; end
|
|
57
86
|
|
|
58
87
|
def delete(value)
|
|
59
88
|
difference([value])
|
|
60
89
|
end
|
|
90
|
+
end
|
|
61
91
|
|
|
62
|
-
|
|
63
|
-
|
|
92
|
+
class PendingAtomicSet < PendingAtomicContainer
|
|
93
|
+
def <<(value)
|
|
94
|
+
@ops << [:set_union, [[value]]]
|
|
95
|
+
modify_source!
|
|
64
96
|
self
|
|
65
97
|
end
|
|
66
|
-
def add(v); self + v; end
|
|
67
98
|
|
|
68
|
-
def
|
|
69
|
-
@ops << [:
|
|
99
|
+
def +(value)
|
|
100
|
+
@ops << [:set_union, [value.to_a]]
|
|
70
101
|
self
|
|
71
102
|
end
|
|
72
|
-
def intersection(v); self & v; end
|
|
73
103
|
|
|
74
|
-
def
|
|
75
|
-
@ops << [:
|
|
104
|
+
def -(value)
|
|
105
|
+
@ops << [:set_difference, [value.to_a]]
|
|
76
106
|
self
|
|
77
107
|
end
|
|
78
|
-
|
|
108
|
+
end
|
|
79
109
|
|
|
110
|
+
class PendingAtomicArray < PendingAtomicContainer
|
|
80
111
|
def <<(value)
|
|
81
112
|
@ops << [:append, [value]]
|
|
82
113
|
modify_source!
|
|
83
114
|
self
|
|
84
115
|
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
class PendingAtomicSet < PendingAtomicContainer
|
|
88
|
-
def -(value)
|
|
89
|
-
@ops << [:set_difference, [value.to_a]]
|
|
90
|
-
self
|
|
91
|
-
end
|
|
92
116
|
|
|
93
117
|
def +(value)
|
|
94
|
-
@ops << [
|
|
118
|
+
@ops << [:+, [value.to_a]]
|
|
95
119
|
self
|
|
96
120
|
end
|
|
97
121
|
|
|
98
|
-
def
|
|
99
|
-
@ops << [:
|
|
100
|
-
modify_source!
|
|
122
|
+
def -(value)
|
|
123
|
+
@ops << [:difference, [value.to_a]]
|
|
101
124
|
self
|
|
102
125
|
end
|
|
103
126
|
end
|
|
@@ -108,6 +131,9 @@ module NoBrainer::Document::AtomicOps
|
|
|
108
131
|
end
|
|
109
132
|
|
|
110
133
|
def _touch_attribute(name)
|
|
134
|
+
# The difference with dirty tracking and this is that dirty tracking does
|
|
135
|
+
# not take into account fields that are set with their old value, whereas the
|
|
136
|
+
# touched attribute does.
|
|
111
137
|
@_touched_attributes << name.to_s
|
|
112
138
|
end
|
|
113
139
|
|
|
@@ -142,12 +168,12 @@ module NoBrainer::Document::AtomicOps
|
|
|
142
168
|
def _read_attribute(name)
|
|
143
169
|
ensure_exclusive_atomic!
|
|
144
170
|
value = super
|
|
171
|
+
return value unless in_atomic?
|
|
145
172
|
|
|
146
|
-
case
|
|
147
|
-
when
|
|
148
|
-
when
|
|
149
|
-
|
|
150
|
-
when [false, false] then value
|
|
173
|
+
case value
|
|
174
|
+
when PendingAtomicContainer then value
|
|
175
|
+
when PendingAtomic then value.dup
|
|
176
|
+
else PendingAtomic._new(self, name, value, _is_attribute_touched?(name))
|
|
151
177
|
end
|
|
152
178
|
end
|
|
153
179
|
|
|
@@ -174,39 +200,15 @@ module NoBrainer::Document::AtomicOps
|
|
|
174
200
|
if saved
|
|
175
201
|
@_attributes.each do |attr, value|
|
|
176
202
|
next unless value.is_a?(PendingAtomic)
|
|
177
|
-
@_attributes[attr] = value.class.new(self, attr, nil, false)
|
|
203
|
+
@_attributes[attr] = value.class.new(self, attr, nil, false, value.type)
|
|
178
204
|
end
|
|
179
205
|
end
|
|
180
206
|
end
|
|
181
207
|
end
|
|
182
208
|
|
|
183
|
-
def read_attribute_for_change(attr)
|
|
184
|
-
super
|
|
185
|
-
rescue NoBrainer::Error::CannotReadAtomic => e
|
|
186
|
-
e.value
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def read_attribute_for_validation(attr)
|
|
190
|
-
super
|
|
191
|
-
rescue NoBrainer::Error::CannotReadAtomic => e
|
|
192
|
-
e.value
|
|
193
|
-
end
|
|
194
|
-
|
|
195
209
|
module ClassMethods
|
|
196
210
|
def persistable_value(k, v, options={})
|
|
197
211
|
v.is_a?(PendingAtomic) ? v.compile_rql_value(options[:rql_doc]) : super
|
|
198
212
|
end
|
|
199
213
|
end
|
|
200
214
|
end
|
|
201
|
-
|
|
202
|
-
class ActiveModel::EachValidator
|
|
203
|
-
# XXX Monkey Patching :(
|
|
204
|
-
def validate(record)
|
|
205
|
-
attributes.each do |attribute|
|
|
206
|
-
value = record.read_attribute_for_validation(attribute)
|
|
207
|
-
next if value.is_a?(NoBrainer::Document::AtomicOps::PendingAtomic) # <--- This is the added line
|
|
208
|
-
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
|
209
|
-
validate_each(record, attribute, value)
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
module NoBrainer::Document::Attributes
|
|
2
2
|
VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key, :lazy_fetch, :store_as,
|
|
3
|
-
:validates, :required, :unique, :uniq, :format, :in]
|
|
3
|
+
:validates, :required, :unique, :uniq, :format, :in, :length, :min_length, :max_length]
|
|
4
4
|
RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] \
|
|
5
5
|
+ NoBrainer::Criteria::Where::OPERATORS
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
|
+
include ActiveModel::ForbiddenAttributesProtection
|
|
7
8
|
|
|
8
9
|
included do
|
|
9
10
|
singleton_class.send(:attr_accessor, :fields)
|
|
@@ -23,6 +24,10 @@ module NoBrainer::Document::Attributes
|
|
|
23
24
|
Hash[readable_attributes.map { |k| [k, read_attribute(k)] }].with_indifferent_access.freeze
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
def raw_attributes
|
|
28
|
+
@_attributes
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
def _read_attribute(name)
|
|
27
32
|
@_attributes[name]
|
|
28
33
|
end
|
|
@@ -54,12 +59,14 @@ module NoBrainer::Document::Attributes
|
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
default_value = field_options[:default]
|
|
57
|
-
default_value = default_value
|
|
62
|
+
default_value = instance_exec(&default_value) if default_value.is_a?(Proc)
|
|
58
63
|
self.write_attribute(name, default_value)
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
66
|
|
|
62
67
|
def assign_attributes(attrs, options={})
|
|
68
|
+
raise ArgumentError, "To assign attributes, please pass a hash instead of `#{attrs.class}'" unless attrs.is_a?(Hash)
|
|
69
|
+
|
|
63
70
|
if options[:pristine]
|
|
64
71
|
if options[:keep_ivars] && options[:missing_attributes].try(:[], :pluck)
|
|
65
72
|
options[:missing_attributes][:pluck].keys.each { |k| @_attributes.delete(k) }
|
|
@@ -74,6 +81,7 @@ module NoBrainer::Document::Attributes
|
|
|
74
81
|
clear_dirtiness(options)
|
|
75
82
|
else
|
|
76
83
|
clear_dirtiness(options) if options[:pristine]
|
|
84
|
+
attrs = sanitize_for_mass_assignment(attrs)
|
|
77
85
|
attrs.each { |k,v| self.write_attribute(k,v) }
|
|
78
86
|
end
|
|
79
87
|
assign_defaults(options) if options[:pristine]
|
|
@@ -82,7 +90,7 @@ module NoBrainer::Document::Attributes
|
|
|
82
90
|
|
|
83
91
|
def inspectable_attributes
|
|
84
92
|
# TODO test that thing
|
|
85
|
-
Hash[@_attributes.sort_by { |k,v| self.class.fields.keys.index(k.to_sym) || 2**10 }]
|
|
93
|
+
Hash[@_attributes.sort_by { |k,v| self.class.fields.keys.index(k.to_sym) || 2**10 }].with_indifferent_access.freeze
|
|
86
94
|
end
|
|
87
95
|
|
|
88
96
|
def to_s
|
|
@@ -2,7 +2,7 @@ module NoBrainer::Document::Core
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
singleton_class.class_eval do
|
|
5
|
-
attr_accessor :_all
|
|
5
|
+
attr_accessor :_all, :_all_nobrainer
|
|
6
6
|
|
|
7
7
|
def all
|
|
8
8
|
Rails.application.eager_load! if defined?(Rails.application.eager_load!)
|
|
@@ -10,10 +10,12 @@ module NoBrainer::Document::Core
|
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
self._all = []
|
|
13
|
+
self._all_nobrainer = []
|
|
13
14
|
|
|
14
15
|
include ActiveModel::Conversion
|
|
15
16
|
|
|
16
17
|
def to_key
|
|
18
|
+
# ActiveModel::Conversion stuff
|
|
17
19
|
[pk_value]
|
|
18
20
|
end
|
|
19
21
|
|
|
@@ -22,6 +24,7 @@ module NoBrainer::Document::Core
|
|
|
22
24
|
extend ActiveModel::Naming
|
|
23
25
|
extend ActiveModel::Translation
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
list_name = self.name =~ /^NoBrainer::/ ? :_all_nobrainer : :_all
|
|
28
|
+
NoBrainer::Document::Core.__send__(list_name) << self
|
|
26
29
|
end
|
|
27
30
|
end
|
|
@@ -2,12 +2,14 @@ module NoBrainer::Document::Criteria
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
def selector
|
|
5
|
-
|
|
5
|
+
# Used for writes
|
|
6
|
+
self.class.rql_table.get(pk_value)
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
included do
|
|
9
|
-
cattr_accessor :default_scope_proc, :instance_accessor => false
|
|
10
10
|
cattr_accessor :perf_warnings_disabled, :instance_accessor => false
|
|
11
|
+
singleton_class.send(:attr_accessor, :default_scopes)
|
|
12
|
+
self.default_scopes = []
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
module ClassMethods
|
|
@@ -21,12 +23,13 @@ module NoBrainer::Document::Criteria
|
|
|
21
23
|
:with_cache, :without_cache, # Cache
|
|
22
24
|
:count, :empty?, :any?, # Count
|
|
23
25
|
:delete_all, :destroy_all, # Delete
|
|
24
|
-
:
|
|
26
|
+
:preload, :eager_load, # EagerLoad
|
|
25
27
|
:each, :to_a, # Enumerable
|
|
26
28
|
:first, :last, :first!, :last!, :sample, # First
|
|
27
29
|
:min, :max, :sum, :avg, # Aggregate
|
|
28
30
|
:update_all, :replace_all, # Update
|
|
29
31
|
:pluck, :without, :lazy_fetch, :without_plucking, # Pluck
|
|
32
|
+
:find_by?, :find_by, :find_by!, :find?, :find, :find!, # Find
|
|
30
33
|
:to => :all
|
|
31
34
|
|
|
32
35
|
def all
|
|
@@ -42,24 +45,18 @@ module NoBrainer::Document::Criteria
|
|
|
42
45
|
end
|
|
43
46
|
|
|
44
47
|
def default_scope(criteria=nil, &block)
|
|
45
|
-
criteria
|
|
46
|
-
raise "
|
|
47
|
-
self.default_scope_proc = criteria.is_a?(Proc) ? criteria : proc { criteria }
|
|
48
|
-
end
|
|
48
|
+
criteria_proc = block || (criteria.is_a?(Proc) ? criteria : proc { criteria })
|
|
49
|
+
raise "default_scope only accepts a criteria or a proc that returns criteria" unless criteria_proc.is_a?(Proc)
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def find?(pk)
|
|
55
|
-
attrs = NoBrainer.run { selector_for(pk) }
|
|
56
|
-
new_from_db(attrs).tap { |doc| doc.run_callbacks(:find) } if attrs
|
|
51
|
+
([self] + self.descendants).each do |model|
|
|
52
|
+
model.default_scopes << criteria_proc
|
|
53
|
+
end
|
|
57
54
|
end
|
|
58
55
|
|
|
59
|
-
def
|
|
60
|
-
|
|
56
|
+
def inherited(subclass)
|
|
57
|
+
subclass.default_scopes = self.default_scopes.dup
|
|
58
|
+
super
|
|
61
59
|
end
|
|
62
|
-
alias_method :find!, :find
|
|
63
60
|
|
|
64
61
|
def disable_perf_warnings
|
|
65
62
|
self.perf_warnings_disabled = true
|
|
@@ -31,14 +31,10 @@ module NoBrainer::Document::Dirty
|
|
|
31
31
|
changes.keys
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def read_attribute_for_change(attr)
|
|
35
|
-
read_attribute(attr)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
34
|
def changes
|
|
39
35
|
result = {}.with_indifferent_access
|
|
40
36
|
@_old_attributes.each do |attr, old_value|
|
|
41
|
-
current_value =
|
|
37
|
+
current_value = read_attribute(attr)
|
|
42
38
|
if current_value != old_value || !@_old_attributes_keys.include?(attr)
|
|
43
39
|
result[attr] = [old_value, current_value]
|
|
44
40
|
end
|
|
@@ -46,16 +42,15 @@ module NoBrainer::Document::Dirty
|
|
|
46
42
|
result
|
|
47
43
|
end
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
current_value
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
class None; end
|
|
46
|
+
def attribute_may_change(attr, current_value = None)
|
|
47
|
+
if current_value == None
|
|
48
|
+
current_value = begin
|
|
49
|
+
assert_access_field(attr)
|
|
50
|
+
read_attribute(attr)
|
|
51
|
+
rescue NoBrainer::Error::MissingAttribute => e
|
|
52
|
+
e
|
|
56
53
|
end
|
|
57
|
-
rescue NoBrainer::Error::MissingAttribute => e
|
|
58
|
-
e
|
|
59
54
|
end
|
|
60
55
|
|
|
61
56
|
unless @_old_attributes.has_key?(attr)
|
|
@@ -84,7 +79,7 @@ module NoBrainer::Document::Dirty
|
|
|
84
79
|
inject_in_layer :dirty_tracking do
|
|
85
80
|
define_method("#{attr}_change") do
|
|
86
81
|
if @_old_attributes.has_key?(attr)
|
|
87
|
-
result = [@_old_attributes[attr],
|
|
82
|
+
result = [@_old_attributes[attr], read_attribute(attr)]
|
|
88
83
|
result if result.first != result.last || !@_old_attributes_keys.include?(attr)
|
|
89
84
|
end
|
|
90
85
|
end
|
|
@@ -94,7 +89,7 @@ module NoBrainer::Document::Dirty
|
|
|
94
89
|
end
|
|
95
90
|
|
|
96
91
|
define_method("#{attr}_was") do
|
|
97
|
-
@_old_attributes.has_key?(attr) ? @_old_attributes[attr] :
|
|
92
|
+
@_old_attributes.has_key?(attr) ? @_old_attributes[attr] : read_attribute(attr)
|
|
98
93
|
end
|
|
99
94
|
end
|
|
100
95
|
end
|
|
@@ -24,7 +24,7 @@ class NoBrainer::Document::Index::MetaStore
|
|
|
24
24
|
def self.on(db_name, &block)
|
|
25
25
|
old_db_name = Thread.current[:nobrainer_meta_store_db]
|
|
26
26
|
Thread.current[:nobrainer_meta_store_db] = db_name
|
|
27
|
-
|
|
27
|
+
block.call
|
|
28
28
|
ensure
|
|
29
29
|
Thread.current[:nobrainer_meta_store_db] = old_db_name
|
|
30
30
|
end
|
|
@@ -14,12 +14,6 @@ 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
|
-
|
|
23
17
|
options.assert_valid_keys(*VALID_INDEX_OPTIONS)
|
|
24
18
|
|
|
25
19
|
raise "Too many arguments: #{args}" if args.size > 1
|
|
@@ -29,7 +29,7 @@ module NoBrainer::Document::Persistance
|
|
|
29
29
|
|
|
30
30
|
def _reload(options={})
|
|
31
31
|
attrs = NoBrainer.run { _reload_selector(options) }
|
|
32
|
-
raise NoBrainer::Error::DocumentNotFound, "#{self.class}
|
|
32
|
+
raise NoBrainer::Error::DocumentNotFound, "#{self.class} :#{self.class.pk_name}=>\"#{pk_value}\" not found" unless attrs
|
|
33
33
|
|
|
34
34
|
options = options.merge(:pristine => true, :from_db => true)
|
|
35
35
|
|
|
@@ -113,13 +113,11 @@ module NoBrainer::Document::Persistance
|
|
|
113
113
|
assign_attributes(attrs, options)
|
|
114
114
|
save?(options)
|
|
115
115
|
end
|
|
116
|
-
alias_method :update_attributes?, :update?
|
|
117
116
|
|
|
118
117
|
def update(*args)
|
|
119
118
|
update?(*args) or raise NoBrainer::Error::DocumentInvalid, self
|
|
120
119
|
nil
|
|
121
120
|
end
|
|
122
|
-
alias_method :update_attributes, :update
|
|
123
121
|
|
|
124
122
|
def update!(*args)
|
|
125
123
|
update(*args)
|
|
@@ -127,6 +125,18 @@ module NoBrainer::Document::Persistance
|
|
|
127
125
|
end
|
|
128
126
|
alias_method :update_attributes!, :update!
|
|
129
127
|
|
|
128
|
+
def update_attributes?(*args)
|
|
129
|
+
update?(*args).tap { STDERR.puts "[NoBrainer] update_attributes?() is deprecated. Please use update?() instead" }
|
|
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" }
|
|
138
|
+
end
|
|
139
|
+
|
|
130
140
|
def delete
|
|
131
141
|
unless @destroyed
|
|
132
142
|
NoBrainer.run { selector.delete }
|
|
@@ -16,13 +16,13 @@ module NoBrainer::Document::StoreIn
|
|
|
16
16
|
|
|
17
17
|
def database_name
|
|
18
18
|
db = self.store_in_options[:database]
|
|
19
|
-
db.is_a?(Proc) ? db.call : db
|
|
19
|
+
(db.is_a?(Proc) ? db.call : db).try(:to_s)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def table_name
|
|
23
23
|
table = store_in_options[:table]
|
|
24
24
|
table_name = table.is_a?(Proc) ? table.call : table
|
|
25
|
-
table_name || root_class.name.tableize.gsub('/', '__')
|
|
25
|
+
table_name.try(:to_s) || root_class.name.tableize.gsub('/', '__')
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def rql_table
|
|
@@ -4,7 +4,7 @@ class NoBrainer::Binary
|
|
|
4
4
|
def self.to_s; inspect; end
|
|
5
5
|
def self.name; inspect; end
|
|
6
6
|
|
|
7
|
-
module
|
|
7
|
+
module NoBrainerExtensions
|
|
8
8
|
InvalidType = NoBrainer::Error::InvalidType
|
|
9
9
|
|
|
10
10
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -13,10 +13,6 @@ class NoBrainer::Binary
|
|
|
13
13
|
else raise InvalidType
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
|
-
|
|
17
|
-
def nobrainer_cast_db_to_model(value)
|
|
18
|
-
value.is_a?(String) ? RethinkDB::Binary.new(value) : value
|
|
19
|
-
end
|
|
20
16
|
end
|
|
21
|
-
extend
|
|
17
|
+
extend NoBrainerExtensions
|
|
22
18
|
end
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
# We namespace our fake Boolean class to avoid polluting the global namespace
|
|
2
1
|
class NoBrainer::Boolean
|
|
3
2
|
def initialize; raise; end
|
|
4
3
|
def self.inspect; 'Boolean'; end
|
|
5
4
|
def self.to_s; inspect; end
|
|
6
5
|
def self.name; inspect; end
|
|
7
6
|
|
|
8
|
-
module
|
|
7
|
+
module NoBrainerExtensions
|
|
9
8
|
InvalidType = NoBrainer::Error::InvalidType
|
|
10
9
|
|
|
11
10
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -21,5 +20,5 @@ class NoBrainer::Boolean
|
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
end
|
|
24
|
-
extend
|
|
23
|
+
extend NoBrainerExtensions
|
|
25
24
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class Date
|
|
2
|
-
module
|
|
2
|
+
module NoBrainerExtensions
|
|
3
3
|
InvalidType = NoBrainer::Error::InvalidType
|
|
4
4
|
|
|
5
5
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -22,5 +22,5 @@ class Date
|
|
|
22
22
|
value.is_a?(Date) ? Time.utc(value.year, value.month, value.day) : value
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
|
-
extend
|
|
25
|
+
extend NoBrainerExtensions
|
|
26
26
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class Float
|
|
2
|
-
module
|
|
2
|
+
module NoBrainerExtensions
|
|
3
3
|
InvalidType = NoBrainer::Error::InvalidType
|
|
4
4
|
|
|
5
5
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -16,5 +16,5 @@ class Float
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
|
-
extend
|
|
19
|
+
extend NoBrainerExtensions
|
|
20
20
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Look in lib/no_brainer/geo.rb instead
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class Integer
|
|
2
|
-
module
|
|
2
|
+
module NoBrainerExtensions
|
|
3
3
|
InvalidType = NoBrainer::Error::InvalidType
|
|
4
4
|
|
|
5
5
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -14,5 +14,5 @@ class Integer
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
|
-
extend
|
|
17
|
+
extend NoBrainerExtensions
|
|
18
18
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class Set
|
|
2
|
-
module
|
|
2
|
+
module NoBrainerExtensions
|
|
3
3
|
InvalidType = NoBrainer::Error::InvalidType
|
|
4
4
|
|
|
5
5
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -19,5 +19,5 @@ class Set
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
extend
|
|
22
|
+
extend NoBrainerExtensions
|
|
23
23
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class String
|
|
2
|
-
module
|
|
2
|
+
module NoBrainerExtensions
|
|
3
3
|
InvalidType = NoBrainer::Error::InvalidType
|
|
4
4
|
|
|
5
5
|
def nobrainer_cast_user_to_model(value)
|
|
@@ -7,8 +7,11 @@ class String
|
|
|
7
7
|
when String then value
|
|
8
8
|
when Symbol then value.to_s
|
|
9
9
|
else raise InvalidType
|
|
10
|
+
end.tap do |str|
|
|
11
|
+
max_length = NoBrainer::Config.max_string_length
|
|
12
|
+
raise InvalidType.new(:error => { :message => :too_long, :count => max_length }) if str.size > max_length
|
|
10
13
|
end
|
|
11
14
|
end
|
|
12
15
|
end
|
|
13
|
-
extend
|
|
16
|
+
extend NoBrainerExtensions
|
|
14
17
|
end
|