active_data 0.3.0 → 1.0.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/.gitignore +1 -1
- data/.rspec +0 -1
- data/.rvmrc +1 -1
- data/.travis.yml +13 -6
- data/Appraisals +7 -0
- data/Gemfile +1 -5
- data/Guardfile +68 -15
- data/README.md +144 -2
- data/active_data.gemspec +19 -11
- data/gemfiles/rails.4.0.gemfile +14 -0
- data/gemfiles/rails.4.1.gemfile +14 -0
- data/gemfiles/rails.4.2.gemfile +14 -0
- data/gemfiles/rails.5.0.gemfile +14 -0
- data/lib/active_data.rb +120 -3
- data/lib/active_data/active_record/associations.rb +50 -0
- data/lib/active_data/active_record/nested_attributes.rb +24 -0
- data/lib/active_data/config.rb +40 -0
- data/lib/active_data/errors.rb +93 -0
- data/lib/active_data/extensions.rb +33 -0
- data/lib/active_data/model.rb +16 -74
- data/lib/active_data/model/associations.rb +84 -15
- data/lib/active_data/model/associations/base.rb +79 -0
- data/lib/active_data/model/associations/collection/embedded.rb +12 -0
- data/lib/active_data/model/associations/collection/proxy.rb +32 -0
- data/lib/active_data/model/associations/collection/referenced.rb +26 -0
- data/lib/active_data/model/associations/embeds_many.rb +124 -18
- data/lib/active_data/model/associations/embeds_one.rb +90 -15
- data/lib/active_data/model/associations/nested_attributes.rb +180 -0
- data/lib/active_data/model/associations/references_many.rb +96 -0
- data/lib/active_data/model/associations/references_one.rb +83 -0
- data/lib/active_data/model/associations/reflections/base.rb +100 -0
- data/lib/active_data/model/associations/reflections/embeds_many.rb +25 -0
- data/lib/active_data/model/associations/reflections/embeds_one.rb +49 -0
- data/lib/active_data/model/associations/reflections/reference_reflection.rb +45 -0
- data/lib/active_data/model/associations/reflections/references_many.rb +28 -0
- data/lib/active_data/model/associations/reflections/references_one.rb +28 -0
- data/lib/active_data/model/associations/validations.rb +63 -0
- data/lib/active_data/model/attributes.rb +247 -0
- data/lib/active_data/model/attributes/attribute.rb +73 -0
- data/lib/active_data/model/attributes/base.rb +116 -0
- data/lib/active_data/model/attributes/collection.rb +17 -0
- data/lib/active_data/model/attributes/dictionary.rb +26 -0
- data/lib/active_data/model/attributes/localized.rb +42 -0
- data/lib/active_data/model/attributes/reference_many.rb +21 -0
- data/lib/active_data/model/attributes/reference_one.rb +42 -0
- data/lib/active_data/model/attributes/reflections/attribute.rb +55 -0
- data/lib/active_data/model/attributes/reflections/base.rb +62 -0
- data/lib/active_data/model/attributes/reflections/collection.rb +10 -0
- data/lib/active_data/model/attributes/reflections/dictionary.rb +13 -0
- data/lib/active_data/model/attributes/reflections/localized.rb +43 -0
- data/lib/active_data/model/attributes/reflections/reference_many.rb +10 -0
- data/lib/active_data/model/attributes/reflections/reference_one.rb +58 -0
- data/lib/active_data/model/attributes/reflections/represents.rb +55 -0
- data/lib/active_data/model/attributes/represents.rb +64 -0
- data/lib/active_data/model/callbacks.rb +71 -0
- data/lib/active_data/model/conventions.rb +35 -0
- data/lib/active_data/model/dirty.rb +77 -0
- data/lib/active_data/model/lifecycle.rb +307 -0
- data/lib/active_data/model/localization.rb +21 -0
- data/lib/active_data/model/persistence.rb +57 -0
- data/lib/active_data/model/primary.rb +51 -0
- data/lib/active_data/model/scopes.rb +77 -0
- data/lib/active_data/model/validations.rb +27 -0
- data/lib/active_data/model/validations/associated.rb +19 -0
- data/lib/active_data/model/validations/nested.rb +39 -0
- data/lib/active_data/railtie.rb +7 -0
- data/lib/active_data/version.rb +1 -1
- data/spec/lib/active_data/active_record/associations_spec.rb +149 -0
- data/spec/lib/active_data/active_record/nested_attributes_spec.rb +16 -0
- data/spec/lib/active_data/config_spec.rb +44 -0
- data/spec/lib/active_data/model/associations/embeds_many_spec.rb +362 -52
- data/spec/lib/active_data/model/associations/embeds_one_spec.rb +250 -31
- data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +23 -0
- data/spec/lib/active_data/model/associations/references_many_spec.rb +196 -0
- data/spec/lib/active_data/model/associations/references_one_spec.rb +134 -0
- data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +144 -0
- data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +116 -0
- data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +255 -0
- data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +208 -0
- data/spec/lib/active_data/model/associations/validations_spec.rb +153 -0
- data/spec/lib/active_data/model/associations_spec.rb +189 -0
- data/spec/lib/active_data/model/attributes/attribute_spec.rb +144 -0
- data/spec/lib/active_data/model/attributes/base_spec.rb +82 -0
- data/spec/lib/active_data/model/attributes/collection_spec.rb +73 -0
- data/spec/lib/active_data/model/attributes/dictionary_spec.rb +93 -0
- data/spec/lib/active_data/model/attributes/localized_spec.rb +88 -33
- data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +72 -0
- data/spec/lib/active_data/model/attributes/reflections/base_spec.rb +56 -0
- data/spec/lib/active_data/model/attributes/reflections/collection_spec.rb +37 -0
- data/spec/lib/active_data/model/attributes/reflections/dictionary_spec.rb +43 -0
- data/spec/lib/active_data/model/attributes/reflections/localized_spec.rb +37 -0
- data/spec/lib/active_data/model/attributes/reflections/represents_spec.rb +70 -0
- data/spec/lib/active_data/model/attributes/represents_spec.rb +153 -0
- data/spec/lib/active_data/model/attributes_spec.rb +243 -0
- data/spec/lib/active_data/model/callbacks_spec.rb +338 -0
- data/spec/lib/active_data/model/conventions_spec.rb +12 -0
- data/spec/lib/active_data/model/dirty_spec.rb +75 -0
- data/spec/lib/active_data/model/lifecycle_spec.rb +330 -0
- data/spec/lib/active_data/model/nested_attributes.rb +202 -0
- data/spec/lib/active_data/model/persistence_spec.rb +47 -0
- data/spec/lib/active_data/model/primary_spec.rb +84 -0
- data/spec/lib/active_data/model/scopes_spec.rb +88 -0
- data/spec/lib/active_data/model/typecasting_spec.rb +192 -0
- data/spec/lib/active_data/model/validations/associated_spec.rb +94 -0
- data/spec/lib/active_data/model/validations/nested_spec.rb +93 -0
- data/spec/lib/active_data/model/validations_spec.rb +31 -0
- data/spec/lib/active_data/model_spec.rb +1 -32
- data/spec/lib/active_data_spec.rb +12 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/model_helpers.rb +10 -0
- metadata +246 -54
- data/gemfiles/Gemfile.rails-3 +0 -14
- data/lib/active_data/attributes/base.rb +0 -69
- data/lib/active_data/attributes/localized.rb +0 -42
- data/lib/active_data/model/associations/association.rb +0 -30
- data/lib/active_data/model/attributable.rb +0 -122
- data/lib/active_data/model/collectionizable.rb +0 -55
- data/lib/active_data/model/collectionizable/proxy.rb +0 -42
- data/lib/active_data/model/extensions.rb +0 -9
- data/lib/active_data/model/extensions/array.rb +0 -24
- data/lib/active_data/model/extensions/big_decimal.rb +0 -17
- data/lib/active_data/model/extensions/boolean.rb +0 -38
- data/lib/active_data/model/extensions/date.rb +0 -17
- data/lib/active_data/model/extensions/date_time.rb +0 -17
- data/lib/active_data/model/extensions/float.rb +0 -17
- data/lib/active_data/model/extensions/hash.rb +0 -22
- data/lib/active_data/model/extensions/integer.rb +0 -17
- data/lib/active_data/model/extensions/localized.rb +0 -22
- data/lib/active_data/model/extensions/object.rb +0 -17
- data/lib/active_data/model/extensions/string.rb +0 -17
- data/lib/active_data/model/extensions/time.rb +0 -17
- data/lib/active_data/model/localizable.rb +0 -31
- data/lib/active_data/model/nested_attributes.rb +0 -58
- data/lib/active_data/model/parameterizable.rb +0 -29
- data/lib/active_data/validations.rb +0 -7
- data/lib/active_data/validations/associated.rb +0 -17
- data/spec/lib/active_data/model/attributable_spec.rb +0 -191
- data/spec/lib/active_data/model/collectionizable_spec.rb +0 -60
- data/spec/lib/active_data/model/nested_attributes_spec.rb +0 -67
- data/spec/lib/active_data/model/type_cast_spec.rb +0 -116
- data/spec/lib/active_data/validations/associated_spec.rb +0 -88
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Associations
|
4
|
+
module Validations
|
5
|
+
def valid_ancestry?
|
6
|
+
errors.clear
|
7
|
+
validate_nested!
|
8
|
+
run_validations!
|
9
|
+
end
|
10
|
+
alias_method :validate_ancestry, :valid_ancestry?
|
11
|
+
|
12
|
+
def invalid_ancestry?
|
13
|
+
!valid_ancestry?
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_ancestry!
|
17
|
+
valid_ancestry? || raise_validation_error
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def run_validations! #:nodoc:
|
23
|
+
super
|
24
|
+
emerge_represented_attributes_errors!
|
25
|
+
errors.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_nested!
|
29
|
+
association_names.each do |name|
|
30
|
+
association = association(name)
|
31
|
+
invalid_block = if association.reflection.klass.method_defined?(:invalid_ansestry?)
|
32
|
+
lambda { |object| object.invalid_ansestry? }
|
33
|
+
else
|
34
|
+
lambda { |object| object.invalid? }
|
35
|
+
end
|
36
|
+
|
37
|
+
ActiveData::Model::Validations::NestedValidator
|
38
|
+
.validate_nested(self, name, association.target, &invalid_block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Move represent attribute errors to the top level:
|
43
|
+
#
|
44
|
+
# {:'role.email' => ['Some error']}
|
45
|
+
#
|
46
|
+
# to:
|
47
|
+
#
|
48
|
+
# {email: ['Some error']}
|
49
|
+
#
|
50
|
+
def emerge_represented_attributes_errors!
|
51
|
+
self.class.represented_attributes.each do |attribute|
|
52
|
+
key = :"#{attribute.reference}.#{attribute.column}"
|
53
|
+
messages = errors.messages[key]
|
54
|
+
if messages.present?
|
55
|
+
errors[attribute.column].concat(messages)
|
56
|
+
errors.delete(key)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'active_data/model/attributes/reflections/base'
|
2
|
+
require 'active_data/model/attributes/reflections/reference_one'
|
3
|
+
require 'active_data/model/attributes/reflections/reference_many'
|
4
|
+
require 'active_data/model/attributes/reflections/attribute'
|
5
|
+
require 'active_data/model/attributes/reflections/collection'
|
6
|
+
require 'active_data/model/attributes/reflections/dictionary'
|
7
|
+
require 'active_data/model/attributes/reflections/localized'
|
8
|
+
require 'active_data/model/attributes/reflections/represents'
|
9
|
+
|
10
|
+
require 'active_data/model/attributes/base'
|
11
|
+
require 'active_data/model/attributes/reference_one'
|
12
|
+
require 'active_data/model/attributes/reference_many'
|
13
|
+
require 'active_data/model/attributes/attribute'
|
14
|
+
require 'active_data/model/attributes/collection'
|
15
|
+
require 'active_data/model/attributes/dictionary'
|
16
|
+
require 'active_data/model/attributes/localized'
|
17
|
+
require 'active_data/model/attributes/represents'
|
18
|
+
|
19
|
+
module ActiveData
|
20
|
+
module Model
|
21
|
+
module Attributes
|
22
|
+
extend ActiveSupport::Concern
|
23
|
+
|
24
|
+
included do
|
25
|
+
class_attribute :_attributes, :_attribute_aliases, :_sanitize, instance_reader: false, instance_writer: false
|
26
|
+
self._attributes = {}
|
27
|
+
self._attribute_aliases = {}
|
28
|
+
self._sanitize = true
|
29
|
+
|
30
|
+
delegate :attribute_names, :has_attribute?, to: 'self.class'
|
31
|
+
|
32
|
+
%w[attribute collection dictionary].each do |kind|
|
33
|
+
define_singleton_method kind do |*args, &block|
|
34
|
+
add_attribute("ActiveData::Model::Attributes::Reflections::#{kind.camelize}".constantize, *args, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
def represents(*names, &block)
|
41
|
+
options = names.extract_options!
|
42
|
+
names.each do |name|
|
43
|
+
add_attribute(Reflections::Represents, name, options, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_attribute(reflection_class, *args, &block)
|
48
|
+
reflection = reflection_class.build(self, generated_attributes_methods, *args, &block)
|
49
|
+
self._attributes = _attributes.merge(reflection.name => reflection)
|
50
|
+
if dirty? && reflection_class != ActiveData::Model::Attributes::Reflections::Base
|
51
|
+
define_dirty reflection.name, generated_attributes_methods
|
52
|
+
end
|
53
|
+
reflection
|
54
|
+
end
|
55
|
+
|
56
|
+
def alias_attribute(alias_name, attribute_name)
|
57
|
+
reflection = reflect_on_attribute(attribute_name)
|
58
|
+
raise ArgumentError.new("Unable to alias undefined attribute `#{attribute_name}` on #{self}") unless reflection
|
59
|
+
raise ArgumentError.new("Unable to alias base attribute `#{attribute_name}`") if reflection.class == ActiveData::Model::Attributes::Reflections::Base
|
60
|
+
reflection.class.generate_methods alias_name, generated_attributes_methods
|
61
|
+
self._attribute_aliases = _attribute_aliases.merge(alias_name.to_s => reflection.name)
|
62
|
+
if dirty?
|
63
|
+
define_dirty alias_name, generated_attributes_methods
|
64
|
+
end
|
65
|
+
reflection
|
66
|
+
end
|
67
|
+
|
68
|
+
def reflect_on_attribute(name)
|
69
|
+
name = name.to_s
|
70
|
+
_attributes[_attribute_aliases[name] || name]
|
71
|
+
end
|
72
|
+
|
73
|
+
def has_attribute? name
|
74
|
+
name = name.to_s
|
75
|
+
_attributes.key?(_attribute_aliases[name] || name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def attribute_names(include_associations = true)
|
79
|
+
if include_associations
|
80
|
+
_attributes.keys
|
81
|
+
else
|
82
|
+
_attributes.map do |name, attribute|
|
83
|
+
name unless attribute.class == ActiveData::Model::Attributes::Reflections::Base
|
84
|
+
end.compact
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
"#{original_inspect}(#{attributes_for_inspect.presence || 'no attributes'})"
|
90
|
+
end
|
91
|
+
|
92
|
+
def represented_attributes
|
93
|
+
@represented_attributes ||= _attributes.values.select do |attribute|
|
94
|
+
attribute.is_a? ActiveData::Model::Attributes::Reflections::Represents
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def represented_names_and_aliases
|
99
|
+
@represented_names_and_aliases ||= represented_attributes.flat_map do |attribute|
|
100
|
+
[attribute.name, *inverted_attribute_aliases[attribute.name]]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def dirty?
|
105
|
+
false
|
106
|
+
end
|
107
|
+
|
108
|
+
def with_sanitize(value)
|
109
|
+
previous_sanitize, self._sanitize = _sanitize, value
|
110
|
+
yield
|
111
|
+
ensure
|
112
|
+
self._sanitize = previous_sanitize
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def original_inspect
|
118
|
+
Object.method(:inspect).unbind.bind(self).call
|
119
|
+
end
|
120
|
+
|
121
|
+
def attributes_for_inspect
|
122
|
+
attribute_names(false).map do |name|
|
123
|
+
prefix = respond_to?(:_primary_name) && _primary_name == name ? ?* : ''
|
124
|
+
"#{prefix}#{_attributes[name].inspect_reflection}"
|
125
|
+
end.join(', ')
|
126
|
+
end
|
127
|
+
|
128
|
+
def generated_attributes_methods
|
129
|
+
@generated_attributes_methods ||=
|
130
|
+
const_set(:GeneratedAttributesMethods, Module.new)
|
131
|
+
.tap { |proxy| include proxy }
|
132
|
+
end
|
133
|
+
|
134
|
+
def inverted_attribute_aliases
|
135
|
+
@inverted_attribute_aliases ||=
|
136
|
+
_attribute_aliases.each.with_object({}) do |(alias_name, attribute_name), result|
|
137
|
+
(result[attribute_name] ||= []).push(alias_name)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def initialize attrs = {}
|
143
|
+
assign_attributes attrs
|
144
|
+
end
|
145
|
+
|
146
|
+
def == other
|
147
|
+
super || other.instance_of?(self.class) && other.attributes(false) == attributes(false)
|
148
|
+
end
|
149
|
+
alias_method :eql?, :==
|
150
|
+
|
151
|
+
def attribute(name)
|
152
|
+
if reflection = self.class.reflect_on_attribute(name)
|
153
|
+
(@_attributes ||= {})[reflection.name] ||= reflection
|
154
|
+
.build_attribute(self, @initial_attributes.try(:[], reflection.name))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def write_attribute name, value
|
159
|
+
attribute(name).write(value)
|
160
|
+
end
|
161
|
+
alias_method :[]=, :write_attribute
|
162
|
+
|
163
|
+
def read_attribute name
|
164
|
+
attribute(name).read
|
165
|
+
end
|
166
|
+
alias_method :[], :read_attribute
|
167
|
+
|
168
|
+
def read_attribute_before_type_cast name
|
169
|
+
attribute(name).read_before_type_cast
|
170
|
+
end
|
171
|
+
|
172
|
+
def attribute_present? name
|
173
|
+
attribute(name).value_present?
|
174
|
+
end
|
175
|
+
|
176
|
+
def attributes(include_associations = true)
|
177
|
+
Hash[attribute_names(include_associations).map { |name| [name, read_attribute(name)] }]
|
178
|
+
end
|
179
|
+
|
180
|
+
def update attrs
|
181
|
+
assign_attributes(attrs)
|
182
|
+
end
|
183
|
+
alias_method :update_attributes, :update
|
184
|
+
|
185
|
+
def assign_attributes attrs
|
186
|
+
if self.class.represented_attributes.present? ||
|
187
|
+
(self.class.is_a?(ActiveData::Model::Associations::NestedAttributes) &&
|
188
|
+
self.class.nested_attributes_options.present?)
|
189
|
+
attrs.stringify_keys!
|
190
|
+
represented_attrs = self.class.represented_names_and_aliases
|
191
|
+
.each_with_object({}) do |name, result|
|
192
|
+
result[name] = attrs.delete(name) if attrs.has_key?(name)
|
193
|
+
end
|
194
|
+
if self.class.is_a?(ActiveData::Model::Associations::NestedAttributes)
|
195
|
+
nested_attrs = self.class.nested_attributes_options.keys
|
196
|
+
.each_with_object({}) do |association_name, result|
|
197
|
+
name = "#{association_name}_attributes"
|
198
|
+
result[name] = attrs.delete(name) if attrs.has_key?(name)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
_assign_attributes(attrs)
|
203
|
+
_assign_attributes(represented_attrs)
|
204
|
+
_assign_attributes(nested_attrs) if nested_attrs
|
205
|
+
else
|
206
|
+
_assign_attributes(attrs)
|
207
|
+
end
|
208
|
+
true
|
209
|
+
end
|
210
|
+
alias_method :attributes=, :assign_attributes
|
211
|
+
|
212
|
+
def inspect
|
213
|
+
"#<#{self.class.send(:original_inspect)} #{attributes_for_inspect.presence || '(no attributes)'}>"
|
214
|
+
end
|
215
|
+
|
216
|
+
def initialize_copy _
|
217
|
+
@initial_attributes = Hash[attribute_names.map do |name|
|
218
|
+
[name, read_attribute_before_type_cast(name)]
|
219
|
+
end]
|
220
|
+
@_attributes = nil
|
221
|
+
super
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
def _assign_attributes attrs
|
227
|
+
attrs.each do |name, value|
|
228
|
+
name = name.to_s
|
229
|
+
sanitize_value = self.class._sanitize && name == self.class.primary_name
|
230
|
+
|
231
|
+
if respond_to?("#{name}=") && !sanitize_value
|
232
|
+
public_send("#{name}=", value)
|
233
|
+
else
|
234
|
+
logger.info("Ignoring #{sanitize_value ? 'primary' : 'undefined'} `#{name}` attribute value for #{self} during mass-assignment")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def attributes_for_inspect
|
240
|
+
attribute_names(false).map do |name|
|
241
|
+
prefix = self.class.primary_name == name ? ?* : ''
|
242
|
+
"#{prefix}#{attribute(name).inspect_attribute}"
|
243
|
+
end.join(', ')
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Attributes
|
4
|
+
class Attribute < Base
|
5
|
+
delegate :defaultizer, :enumerizer, :normalizers, to: :reflection
|
6
|
+
|
7
|
+
def write value
|
8
|
+
return if readonly?
|
9
|
+
pollute do
|
10
|
+
write_value value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def read
|
15
|
+
variable_cache(:value) do
|
16
|
+
normalize(enumerize(typecast(read_before_type_cast)))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_before_type_cast
|
21
|
+
variable_cache(:value_before_type_cast) do
|
22
|
+
defaultize(@value_cache)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def default
|
27
|
+
defaultizer.is_a?(Proc) ? evaluate(&defaultizer) : defaultizer
|
28
|
+
end
|
29
|
+
|
30
|
+
def defaultize value, default_value = nil
|
31
|
+
defaultizer && value.nil? ? default_value || default : value
|
32
|
+
end
|
33
|
+
|
34
|
+
def enum
|
35
|
+
source = enumerizer.is_a?(Proc) ? evaluate(&enumerizer) : enumerizer
|
36
|
+
|
37
|
+
case source
|
38
|
+
when Range
|
39
|
+
source.to_a
|
40
|
+
when Set
|
41
|
+
source
|
42
|
+
else
|
43
|
+
Array.wrap(source)
|
44
|
+
end.to_set
|
45
|
+
end
|
46
|
+
|
47
|
+
def enumerize value
|
48
|
+
set = enum if enumerizer
|
49
|
+
value if !set || (set.none? || set.include?(value))
|
50
|
+
end
|
51
|
+
|
52
|
+
def normalize value
|
53
|
+
if normalizers.none?
|
54
|
+
value
|
55
|
+
else
|
56
|
+
normalizers.inject(value) do |value, normalizer|
|
57
|
+
case normalizer
|
58
|
+
when Proc
|
59
|
+
evaluate(value, &normalizer)
|
60
|
+
when Hash
|
61
|
+
normalizer.inject(value) do |value, (name, options)|
|
62
|
+
ActiveData.normalizer(name).call(value, options, self)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
ActiveData.normalizer(normalizer).call(value, {}, self)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Attributes
|
4
|
+
class Base
|
5
|
+
attr_reader :name, :owner
|
6
|
+
delegate :type, :typecaster, :readonly, to: :reflection
|
7
|
+
|
8
|
+
def initialize name, owner
|
9
|
+
@name, @owner = name, owner
|
10
|
+
end
|
11
|
+
|
12
|
+
def reflection
|
13
|
+
@owner.class._attributes[name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def write_value value
|
17
|
+
reset
|
18
|
+
@value_cache = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def write value
|
22
|
+
return if readonly?
|
23
|
+
write_value value
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset
|
27
|
+
remove_variable(:value, :value_before_type_cast)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read
|
31
|
+
@value_cache
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_before_type_cast
|
35
|
+
@value_cache
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_present?
|
39
|
+
!read.nil? && !(read.respond_to?(:empty?) && read.empty?)
|
40
|
+
end
|
41
|
+
|
42
|
+
def query
|
43
|
+
!(read.respond_to?(:zero?) ? read.zero? : read.blank?)
|
44
|
+
end
|
45
|
+
|
46
|
+
def typecast value
|
47
|
+
if value.instance_of?(type)
|
48
|
+
value
|
49
|
+
else
|
50
|
+
typecaster.call(value, self) unless value.nil?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def readonly?
|
55
|
+
!!(readonly.is_a?(Proc) ? evaluate(&readonly) : readonly)
|
56
|
+
end
|
57
|
+
|
58
|
+
def inspect_attribute
|
59
|
+
value = case read
|
60
|
+
when Date, Time, DateTime
|
61
|
+
%("#{read.to_s(:db)}")
|
62
|
+
else
|
63
|
+
inspection = read.inspect
|
64
|
+
inspection.size > 100 ? inspection.truncate(50) : inspection
|
65
|
+
end
|
66
|
+
"#{name}: #{value}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def pollute
|
70
|
+
pollute = owner.class.dirty? && !owner.send(:attribute_changed?, name)
|
71
|
+
|
72
|
+
if pollute
|
73
|
+
previous_value = read
|
74
|
+
result = yield
|
75
|
+
if previous_value != read || (
|
76
|
+
read.respond_to?(:changed?) &&
|
77
|
+
read.changed?
|
78
|
+
)
|
79
|
+
owner.send(:set_attribute_was, name, previous_value)
|
80
|
+
end
|
81
|
+
result
|
82
|
+
else
|
83
|
+
yield
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def evaluate *args, &block
|
90
|
+
if block.arity >= 0 && block.arity <= args.length
|
91
|
+
owner.instance_exec(*args.first(block.arity), &block)
|
92
|
+
else
|
93
|
+
args = block.arity < 0 ? args : args.first(block.arity)
|
94
|
+
block.call(*args, owner)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def remove_variable(*names)
|
99
|
+
names.flatten.each do |name|
|
100
|
+
name = :"@#{name}"
|
101
|
+
remove_instance_variable(name) if instance_variable_defined?(name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def variable_cache(name)
|
106
|
+
name = :"@#{name}"
|
107
|
+
if instance_variable_defined?(name)
|
108
|
+
instance_variable_get(name)
|
109
|
+
else
|
110
|
+
instance_variable_set(name, yield)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|