active_data 1.0.0 → 1.1.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 +5 -5
- data/.codeclimate.yml +13 -0
- data/.rubocop.yml +56 -0
- data/.rubocop_todo.yml +53 -0
- data/.rvmrc +1 -1
- data/.travis.yml +15 -2
- data/Appraisals +1 -1
- data/CHANGELOG.md +31 -0
- data/Guardfile +8 -8
- data/README.md +256 -0
- data/Rakefile +2 -4
- data/active_data.gemspec +8 -7
- data/gemfiles/rails.4.0.gemfile +1 -1
- data/gemfiles/rails.4.1.gemfile +1 -1
- data/gemfiles/rails.4.2.gemfile +1 -1
- data/gemfiles/rails.5.0.gemfile +1 -1
- data/gemfiles/rails.5.1.gemfile +14 -0
- data/lib/active_data/active_record/associations.rb +18 -13
- data/lib/active_data/active_record/nested_attributes.rb +8 -14
- data/lib/active_data/base.rb +13 -0
- data/lib/active_data/config.rb +4 -4
- data/lib/active_data/errors.rb +29 -13
- data/lib/active_data/extensions.rb +22 -21
- data/lib/active_data/model/associations/base.rb +22 -6
- data/lib/active_data/model/associations/embeds_any.rb +17 -0
- data/lib/active_data/model/associations/embeds_many.rb +29 -19
- data/lib/active_data/model/associations/embeds_one.rb +30 -26
- data/lib/active_data/model/associations/nested_attributes.rb +82 -50
- data/lib/active_data/model/associations/persistence_adapters/active_record/referenced_proxy.rb +31 -0
- data/lib/active_data/model/associations/persistence_adapters/active_record.rb +66 -0
- data/lib/active_data/model/associations/persistence_adapters/base.rb +53 -0
- data/lib/active_data/model/associations/references_any.rb +41 -0
- data/lib/active_data/model/associations/references_many.rb +51 -37
- data/lib/active_data/model/associations/references_one.rb +43 -41
- data/lib/active_data/model/associations/reflections/base.rb +19 -29
- data/lib/active_data/model/associations/reflections/embeds_any.rb +43 -0
- data/lib/active_data/model/associations/reflections/embeds_many.rb +3 -13
- data/lib/active_data/model/associations/reflections/embeds_one.rb +5 -37
- data/lib/active_data/model/associations/reflections/references_any.rb +62 -0
- data/lib/active_data/model/associations/reflections/references_many.rb +7 -7
- data/lib/active_data/model/associations/reflections/references_one.rb +9 -7
- data/lib/active_data/model/associations/reflections/singular.rb +35 -0
- data/lib/active_data/model/associations/validations.rb +2 -27
- data/lib/active_data/model/associations.rb +12 -10
- data/lib/active_data/model/attributes/attribute.rb +10 -10
- data/lib/active_data/model/attributes/base.rb +8 -7
- data/lib/active_data/model/attributes/localized.rb +4 -4
- data/lib/active_data/model/attributes/reference_many.rb +6 -8
- data/lib/active_data/model/attributes/reference_one.rb +17 -9
- data/lib/active_data/model/attributes/reflections/attribute.rb +2 -2
- data/lib/active_data/model/attributes/reflections/base.rb +8 -11
- data/lib/active_data/model/attributes/reflections/localized.rb +2 -2
- data/lib/active_data/model/attributes/reflections/reference_one.rb +11 -22
- data/lib/active_data/model/attributes/reflections/represents.rb +5 -6
- data/lib/active_data/model/attributes/represents.rb +6 -5
- data/lib/active_data/model/attributes.rb +33 -87
- data/lib/active_data/model/callbacks.rb +6 -7
- data/lib/active_data/model/conventions.rb +2 -0
- data/lib/active_data/model/dirty.rb +4 -4
- data/lib/active_data/model/lifecycle.rb +18 -20
- data/lib/active_data/model/localization.rb +5 -2
- data/lib/active_data/model/persistence.rb +2 -2
- data/lib/active_data/model/primary.rb +19 -14
- data/lib/active_data/model/representation.rb +81 -0
- data/lib/active_data/model/scopes.rb +22 -12
- data/lib/active_data/model/validations/associated.rb +3 -2
- data/lib/active_data/model/validations/nested.rb +6 -1
- data/lib/active_data/model/validations.rb +3 -3
- data/lib/active_data/model.rb +2 -1
- data/lib/active_data/undefined_class.rb +9 -0
- data/lib/active_data/version.rb +1 -1
- data/lib/active_data.rb +40 -17
- data/spec/lib/active_data/active_record/associations_spec.rb +107 -45
- data/spec/lib/active_data/active_record/nested_attributes_spec.rb +1 -2
- data/spec/lib/active_data/config_spec.rb +37 -15
- data/spec/lib/active_data/model/associations/embeds_many_spec.rb +475 -172
- data/spec/lib/active_data/model/associations/embeds_one_spec.rb +353 -96
- data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +108 -12
- data/spec/lib/active_data/model/associations/persistence_adapters/active_record_spec.rb +58 -0
- data/spec/lib/active_data/model/associations/references_many_spec.rb +440 -64
- data/spec/lib/active_data/model/associations/references_one_spec.rb +347 -36
- data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +8 -7
- data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +7 -6
- data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +81 -33
- data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +116 -37
- data/spec/lib/active_data/model/associations/validations_spec.rb +27 -43
- data/spec/lib/active_data/model/associations_spec.rb +34 -25
- data/spec/lib/active_data/model/attributes/attribute_spec.rb +26 -23
- data/spec/lib/active_data/model/attributes/base_spec.rb +5 -6
- data/spec/lib/active_data/model/attributes/collection_spec.rb +7 -8
- data/spec/lib/active_data/model/attributes/dictionary_spec.rb +40 -33
- data/spec/lib/active_data/model/attributes/localized_spec.rb +27 -28
- data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +6 -6
- data/spec/lib/active_data/model/attributes/represents_spec.rb +10 -78
- data/spec/lib/active_data/model/attributes_spec.rb +150 -45
- data/spec/lib/active_data/model/callbacks_spec.rb +69 -70
- data/spec/lib/active_data/model/conventions_spec.rb +0 -1
- data/spec/lib/active_data/model/dirty_spec.rb +22 -13
- data/spec/lib/active_data/model/lifecycle_spec.rb +49 -23
- data/spec/lib/active_data/model/persistence_spec.rb +5 -6
- data/spec/lib/active_data/model/representation_spec.rb +126 -0
- data/spec/lib/active_data/model/scopes_spec.rb +1 -3
- data/spec/lib/active_data/model/typecasting_spec.rb +6 -5
- data/spec/lib/active_data/model/validations/associated_spec.rb +26 -18
- data/spec/lib/active_data/model/validations/nested_spec.rb +89 -18
- data/spec/lib/active_data/model_spec.rb +1 -2
- data/spec/lib/active_data_spec.rb +0 -1
- data/spec/shared/nested_attribute_examples.rb +332 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/model_helpers.rb +2 -2
- data/spec/support/muffle_helper.rb +7 -0
- metadata +52 -18
- data/lib/active_data/model/associations/collection/referenced.rb +0 -26
- data/lib/active_data/model/associations/reflections/reference_reflection.rb +0 -45
- data/spec/lib/active_data/model/nested_attributes.rb +0 -202
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
module ActiveData
|
|
2
2
|
module Model
|
|
3
|
-
|
|
4
3
|
# == Lifecycle methods for ActiveData::Model
|
|
5
4
|
#
|
|
6
5
|
# Provides methods +save+ and +destroy+ and its bang variants.
|
|
@@ -64,12 +63,11 @@ module ActiveData
|
|
|
64
63
|
included do
|
|
65
64
|
include Persistence
|
|
66
65
|
|
|
67
|
-
class_attribute
|
|
68
|
-
private
|
|
66
|
+
class_attribute(*%i[save create update destroy].map { |action| "_#{action}_performer" })
|
|
67
|
+
private(*%i[save create update destroy].map { |action| "_#{action}_performer=" })
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
module ClassMethods
|
|
72
|
-
|
|
73
71
|
# <tt>define_<action></tt> methods define performers for lifecycle
|
|
74
72
|
# actions. Every action block must return boolean result, which
|
|
75
73
|
# would mean the action success. If action performed unsuccessfully
|
|
@@ -108,7 +106,7 @@ module ActiveData
|
|
|
108
106
|
# author = Author.create # using define_save performer
|
|
109
107
|
# author.update_attributes(...) # using define_update performer
|
|
110
108
|
#
|
|
111
|
-
[
|
|
109
|
+
%i[save create update destroy].each do |action|
|
|
112
110
|
define_method "define_#{action}" do |&block|
|
|
113
111
|
send("_#{action}_performer=", block)
|
|
114
112
|
end
|
|
@@ -117,7 +115,7 @@ module ActiveData
|
|
|
117
115
|
# Initializes new instance with attributes passed and calls +save+
|
|
118
116
|
# on it. Returns instance in any case.
|
|
119
117
|
#
|
|
120
|
-
def create
|
|
118
|
+
def create(*args)
|
|
121
119
|
new(*args).tap(&:save)
|
|
122
120
|
end
|
|
123
121
|
|
|
@@ -125,7 +123,7 @@ module ActiveData
|
|
|
125
123
|
# on it. Returns instance in case of success and raises ActiveData::ValidationError
|
|
126
124
|
# or ActiveData::ObjectNotSaved in case of validation or saving fail respectively.
|
|
127
125
|
#
|
|
128
|
-
def create!
|
|
126
|
+
def create!(*args)
|
|
129
127
|
new(*args).tap(&:save!)
|
|
130
128
|
end
|
|
131
129
|
end
|
|
@@ -139,7 +137,7 @@ module ActiveData
|
|
|
139
137
|
# end
|
|
140
138
|
# user.save! # => will use instance-level performer
|
|
141
139
|
#
|
|
142
|
-
[
|
|
140
|
+
%i[save create update destroy].each do |action|
|
|
143
141
|
define_method "define_#{action}" do |&block|
|
|
144
142
|
send("_#{action}_performer=", block)
|
|
145
143
|
end
|
|
@@ -158,7 +156,7 @@ module ActiveData
|
|
|
158
156
|
#
|
|
159
157
|
# author.update(name: 'Donald') { REDIS.set(id, attributes.to_json) }
|
|
160
158
|
#
|
|
161
|
-
def update
|
|
159
|
+
def update(attributes, &block)
|
|
162
160
|
assign_attributes(attributes) && save(&block)
|
|
163
161
|
end
|
|
164
162
|
alias_method :update_attributes, :update
|
|
@@ -177,7 +175,7 @@ module ActiveData
|
|
|
177
175
|
#
|
|
178
176
|
# author.update!(name: 'Donald') { REDIS.set(id, attributes.to_json) }
|
|
179
177
|
#
|
|
180
|
-
def update!
|
|
178
|
+
def update!(attributes, &block)
|
|
181
179
|
assign_attributes(attributes) && save!(&block)
|
|
182
180
|
end
|
|
183
181
|
alias_method :update_attributes!, :update!
|
|
@@ -196,7 +194,7 @@ module ActiveData
|
|
|
196
194
|
#
|
|
197
195
|
# author.save { REDIS.set(id, attributes.to_json) }
|
|
198
196
|
#
|
|
199
|
-
def save
|
|
197
|
+
def save(_options = {}, &block)
|
|
200
198
|
raise ActiveData::UnsavableObject unless block || savable?
|
|
201
199
|
valid? && save_object(&block)
|
|
202
200
|
end
|
|
@@ -216,7 +214,7 @@ module ActiveData
|
|
|
216
214
|
#
|
|
217
215
|
# author.save! { REDIS.set(id, attributes.to_json) }
|
|
218
216
|
#
|
|
219
|
-
def save!
|
|
217
|
+
def save!(_options = {}, &block)
|
|
220
218
|
raise ActiveData::UnsavableObject unless block || savable?
|
|
221
219
|
validate!
|
|
222
220
|
save_object(&block) or raise ActiveData::ObjectNotSaved
|
|
@@ -235,7 +233,7 @@ module ActiveData
|
|
|
235
233
|
#
|
|
236
234
|
# author.destroy { REDIS.del(id) }
|
|
237
235
|
#
|
|
238
|
-
def destroy
|
|
236
|
+
def destroy(&block)
|
|
239
237
|
raise ActiveData::UndestroyableObject unless block || destroyable?
|
|
240
238
|
destroy_object(&block)
|
|
241
239
|
self
|
|
@@ -255,7 +253,7 @@ module ActiveData
|
|
|
255
253
|
#
|
|
256
254
|
# author.destroy! { REDIS.del(id) }
|
|
257
255
|
#
|
|
258
|
-
def destroy!
|
|
256
|
+
def destroy!(&block)
|
|
259
257
|
raise ActiveData::UndestroyableObject unless block || destroyable?
|
|
260
258
|
destroy_object(&block) or raise ActiveData::ObjectNotDestroyed
|
|
261
259
|
self
|
|
@@ -267,19 +265,19 @@ module ActiveData
|
|
|
267
265
|
!!((persisted? ? _update_performer : _create_performer) || _save_performer)
|
|
268
266
|
end
|
|
269
267
|
|
|
270
|
-
def save_object
|
|
268
|
+
def save_object(&block)
|
|
271
269
|
apply_association_changes! if respond_to?(:apply_association_changes!)
|
|
272
270
|
result = persisted? ? update_object(&block) : create_object(&block)
|
|
273
271
|
mark_persisted! if result
|
|
274
272
|
result
|
|
275
273
|
end
|
|
276
274
|
|
|
277
|
-
def create_object
|
|
275
|
+
def create_object(&block)
|
|
278
276
|
performer = block || _create_performer || _save_performer
|
|
279
277
|
!!performer_exec(&performer)
|
|
280
278
|
end
|
|
281
279
|
|
|
282
|
-
def update_object
|
|
280
|
+
def update_object(&block)
|
|
283
281
|
performer = block || _update_performer || _save_performer
|
|
284
282
|
!!performer_exec(&performer)
|
|
285
283
|
end
|
|
@@ -288,16 +286,16 @@ module ActiveData
|
|
|
288
286
|
!!_destroy_performer
|
|
289
287
|
end
|
|
290
288
|
|
|
291
|
-
def destroy_object
|
|
289
|
+
def destroy_object(&block)
|
|
292
290
|
performer = block || _destroy_performer
|
|
293
291
|
result = !!performer_exec(&performer)
|
|
294
292
|
mark_destroyed! if result
|
|
295
293
|
result
|
|
296
294
|
end
|
|
297
295
|
|
|
298
|
-
def performer_exec
|
|
296
|
+
def performer_exec(&block)
|
|
299
297
|
if block.arity == 1
|
|
300
|
-
|
|
298
|
+
yield(self)
|
|
301
299
|
else
|
|
302
300
|
instance_exec(&block)
|
|
303
301
|
end
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
require 'active_data/model/attributes/reflections/localized'
|
|
2
|
+
require 'active_data/model/attributes/localized'
|
|
3
|
+
|
|
1
4
|
module ActiveData
|
|
2
5
|
module Model
|
|
3
6
|
module Localization
|
|
4
7
|
extend ActiveSupport::Concern
|
|
5
8
|
|
|
6
9
|
module ClassMethods
|
|
7
|
-
def localized
|
|
10
|
+
def localized(*args, &block)
|
|
8
11
|
add_attribute(ActiveData::Model::Attributes::Reflections::Localized, *args, &block)
|
|
9
12
|
end
|
|
10
13
|
|
|
11
|
-
def fallbacks
|
|
14
|
+
def fallbacks(locale)
|
|
12
15
|
::I18n.respond_to?(:fallbacks) ? ::I18n.fallbacks[locale] : [locale]
|
|
13
16
|
end
|
|
14
17
|
|
|
@@ -4,7 +4,7 @@ module ActiveData
|
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
6
|
module ClassMethods
|
|
7
|
-
def instantiate
|
|
7
|
+
def instantiate(data)
|
|
8
8
|
data = data.stringify_keys
|
|
9
9
|
instance = allocate
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ module ActiveData
|
|
|
14
14
|
instance
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def instantiate_collection
|
|
17
|
+
def instantiate_collection(data)
|
|
18
18
|
collection = Array.wrap(data).map { |attrs| instantiate attrs }
|
|
19
19
|
collection = scope(collection, true) if respond_to?(:scope)
|
|
20
20
|
collection
|
|
@@ -2,10 +2,12 @@ module ActiveData
|
|
|
2
2
|
module Model
|
|
3
3
|
module Primary
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
|
-
DEFAULT_PRIMARY_ATTRIBUTE_OPTIONS =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
DEFAULT_PRIMARY_ATTRIBUTE_OPTIONS = lambda do
|
|
6
|
+
{
|
|
7
|
+
type: ActiveData::UUID,
|
|
8
|
+
default: -> { ActiveData::UUID.random_create }
|
|
9
|
+
}
|
|
10
|
+
end
|
|
9
11
|
|
|
10
12
|
included do
|
|
11
13
|
class_attribute :_primary_name, instance_writer: false
|
|
@@ -16,18 +18,18 @@ module ActiveData
|
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
module ClassMethods
|
|
19
|
-
def primary
|
|
21
|
+
def primary(*args)
|
|
20
22
|
options = args.extract_options!
|
|
21
23
|
self._primary_name = (args.first.presence || ActiveData.primary_attribute).to_s
|
|
22
24
|
unless has_attribute?(_primary_name)
|
|
23
|
-
options
|
|
25
|
+
options[:type] = args.second if args.second
|
|
24
26
|
attribute _primary_name, options.presence || DEFAULT_PRIMARY_ATTRIBUTE_OPTIONS.call
|
|
25
27
|
end
|
|
26
28
|
alias_attribute :primary_attribute, _primary_name
|
|
27
29
|
end
|
|
28
30
|
alias_method :primary_attribute, :primary
|
|
29
31
|
|
|
30
|
-
def has_primary_attribute?
|
|
32
|
+
def has_primary_attribute? # rubocop:disable Naming/PredicateName
|
|
31
33
|
has_attribute? _primary_name
|
|
32
34
|
end
|
|
33
35
|
|
|
@@ -37,13 +39,16 @@ module ActiveData
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
module PrependMethods
|
|
40
|
-
def ==
|
|
41
|
-
other.instance_of?(self.class) &&
|
|
42
|
-
|
|
43
|
-
primary_attribute
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
def ==(other)
|
|
43
|
+
if other.instance_of?(self.class) && has_primary_attribute?
|
|
44
|
+
if primary_attribute
|
|
45
|
+
primary_attribute == other.primary_attribute
|
|
46
|
+
else
|
|
47
|
+
object_id == other.object_id
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
super(other)
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'active_data/model/attributes/reflections/represents'
|
|
2
|
+
require 'active_data/model/attributes/represents'
|
|
3
|
+
|
|
4
|
+
module ActiveData
|
|
5
|
+
module Model
|
|
6
|
+
module Representation
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
prepend PrependMethods
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module PrependMethods
|
|
14
|
+
def assign_attributes(attrs)
|
|
15
|
+
if self.class.represented_attributes.present?
|
|
16
|
+
attrs = attrs.to_unsafe_hash if attrs.respond_to?(:to_unsafe_hash)
|
|
17
|
+
attrs = attrs.stringify_keys
|
|
18
|
+
represented_attrs = self.class.represented_names_and_aliases
|
|
19
|
+
.each_with_object({}) do |name, result|
|
|
20
|
+
result[name] = attrs.delete(name) if attrs.key?(name)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
super(attrs.merge!(represented_attrs))
|
|
24
|
+
else
|
|
25
|
+
super(attrs)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
alias_method :attributes=, :assign_attributes
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module ClassMethods
|
|
32
|
+
def represents(*names, &block)
|
|
33
|
+
options = names.extract_options!
|
|
34
|
+
names.each do |name|
|
|
35
|
+
add_attribute(ActiveData::Model::Attributes::Reflections::Represents, name, options, &block)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def represented_attributes
|
|
40
|
+
@represented_attributes ||= _attributes.values.select do |attribute|
|
|
41
|
+
attribute.is_a? ActiveData::Model::Attributes::Reflections::Represents
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def represented_names_and_aliases
|
|
46
|
+
@represented_names_and_aliases ||= represented_attributes.flat_map do |attribute|
|
|
47
|
+
[attribute.name, *inverted_attribute_aliases[attribute.name]]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def run_validations! #:nodoc:
|
|
55
|
+
super
|
|
56
|
+
emerge_represented_attributes_errors!
|
|
57
|
+
errors.empty?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Move represent attribute errors to the top level:
|
|
61
|
+
#
|
|
62
|
+
# {:'role.email' => ['Some error']}
|
|
63
|
+
#
|
|
64
|
+
# to:
|
|
65
|
+
#
|
|
66
|
+
# {email: ['Some error']}
|
|
67
|
+
#
|
|
68
|
+
def emerge_represented_attributes_errors!
|
|
69
|
+
self.class.represented_attributes.each do |attribute|
|
|
70
|
+
key = :"#{attribute.reference}.#{attribute.column}"
|
|
71
|
+
# Rails 5 pollutes messages with an empty array on key data fetch attempt
|
|
72
|
+
messages = errors.messages[key] if errors.messages.key?(key)
|
|
73
|
+
if messages.present?
|
|
74
|
+
errors[attribute.column].concat(messages)
|
|
75
|
+
errors.delete(key)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -20,27 +20,37 @@ module ActiveData
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
included do
|
|
23
|
-
def initialize
|
|
23
|
+
def initialize(source = nil, trust = false)
|
|
24
24
|
source ||= self.class.superclass.new
|
|
25
25
|
|
|
26
|
-
source.
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
unless trust && source.is_a?(self.class)
|
|
27
|
+
source.each do |entity|
|
|
28
|
+
raise AssociationTypeMismatch.new(self.class._scope_model, entity.class) unless entity.is_a?(self.class._scope_model)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
29
31
|
|
|
30
32
|
super source
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
def respond_to_missing?
|
|
36
|
+
def respond_to_missing?(method, _)
|
|
35
37
|
super || self.class._scope_model.respond_to?(method)
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
def method_missing
|
|
39
|
-
with_scope
|
|
40
|
+
def method_missing(method, *args, &block)
|
|
41
|
+
with_scope do
|
|
42
|
+
model = self.class._scope_model
|
|
43
|
+
if model.respond_to?(method)
|
|
44
|
+
self.class._scope_model.public_send(method, *args, &block)
|
|
45
|
+
else
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
end
|
|
40
49
|
end
|
|
41
50
|
|
|
42
51
|
def with_scope
|
|
43
|
-
previous_scope
|
|
52
|
+
previous_scope = self.class._scope_model.current_scope
|
|
53
|
+
self.class._scope_model.current_scope = self
|
|
44
54
|
result = yield
|
|
45
55
|
self.class._scope_model.current_scope = previous_scope
|
|
46
56
|
result
|
|
@@ -48,7 +58,7 @@ module ActiveData
|
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
module ClassMethods
|
|
51
|
-
def scopify
|
|
61
|
+
def scopify(scope_base = Array)
|
|
52
62
|
self._scope_base = scope_base
|
|
53
63
|
end
|
|
54
64
|
|
|
@@ -56,15 +66,15 @@ module ActiveData
|
|
|
56
66
|
@scope_class ||= ActiveData::Model::Scopes::ScopeProxy.for(self)
|
|
57
67
|
end
|
|
58
68
|
|
|
59
|
-
def scope
|
|
69
|
+
def scope(*args)
|
|
60
70
|
if args.empty?
|
|
61
71
|
current_scope
|
|
62
72
|
else
|
|
63
|
-
scope_class.new
|
|
73
|
+
scope_class.new(*args)
|
|
64
74
|
end
|
|
65
75
|
end
|
|
66
76
|
|
|
67
|
-
def current_scope=
|
|
77
|
+
def current_scope=(value)
|
|
68
78
|
@current_scope = value
|
|
69
79
|
end
|
|
70
80
|
|
|
@@ -3,9 +3,10 @@ module ActiveData
|
|
|
3
3
|
module Validations
|
|
4
4
|
class AssociatedValidator < ActiveModel::EachValidator
|
|
5
5
|
def validate_each(record, attribute, value)
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
invalid_records = Array.wrap(value).reject do |r|
|
|
7
|
+
r.respond_to?(:valid?) && r.valid?(record.validation_context)
|
|
8
8
|
end
|
|
9
|
+
record.errors.add(attribute, :invalid, options.merge(value: value)) if invalid_records.present?
|
|
9
10
|
end
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -24,7 +24,7 @@ module ActiveData
|
|
|
24
24
|
|
|
25
25
|
def validate_each(record, attribute, value)
|
|
26
26
|
self.class.validate_nested(record, attribute, value) do |object|
|
|
27
|
-
object.invalid?
|
|
27
|
+
object.invalid? && !(object.respond_to?(:marked_for_destruction?) && object.marked_for_destruction?)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
@@ -33,6 +33,11 @@ module ActiveData
|
|
|
33
33
|
def validates_nested(*attr_names)
|
|
34
34
|
validates_with NestedValidator, _merge_attributes(attr_names)
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
def validates_nested?(attr)
|
|
38
|
+
_validators[attr.to_sym]
|
|
39
|
+
.grep(ActiveData::Model::Validations::NestedValidator).present?
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
end
|
|
@@ -11,17 +11,17 @@ module ActiveData
|
|
|
11
11
|
alias_method :validate, :valid?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def validate!
|
|
14
|
+
def validate!(context = nil)
|
|
15
15
|
valid?(context) || raise_validation_error
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
protected
|
|
19
19
|
|
|
20
20
|
def raise_validation_error
|
|
21
|
-
raise ActiveData::ValidationError
|
|
21
|
+
raise ActiveData::ValidationError, self
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
Dir[File.dirname(__FILE__) +
|
|
27
|
+
Dir[File.dirname(__FILE__) + '/validations/*.rb'].each { |file| require file }
|
data/lib/active_data/model.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
require 'active_data/model/conventions'
|
|
2
2
|
require 'active_data/model/attributes'
|
|
3
|
+
require 'active_data/model/validations'
|
|
3
4
|
require 'active_data/model/scopes'
|
|
4
5
|
require 'active_data/model/primary'
|
|
5
6
|
require 'active_data/model/lifecycle'
|
|
6
7
|
require 'active_data/model/persistence'
|
|
7
8
|
require 'active_data/model/callbacks'
|
|
8
9
|
require 'active_data/model/associations'
|
|
9
|
-
require 'active_data/model/validations'
|
|
10
10
|
require 'active_data/model/localization'
|
|
11
|
+
require 'active_data/model/representation'
|
|
11
12
|
require 'active_data/model/dirty'
|
|
12
13
|
|
|
13
14
|
module ActiveData
|
data/lib/active_data/version.rb
CHANGED
data/lib/active_data.rb
CHANGED
|
@@ -10,8 +10,12 @@ require 'active_model'
|
|
|
10
10
|
require 'active_data/version'
|
|
11
11
|
require 'active_data/errors'
|
|
12
12
|
require 'active_data/extensions'
|
|
13
|
+
require 'active_data/undefined_class'
|
|
13
14
|
require 'active_data/config'
|
|
14
15
|
require 'active_data/railtie' if defined? Rails
|
|
16
|
+
require 'active_data/model'
|
|
17
|
+
require 'active_data/model/associations/persistence_adapters/base'
|
|
18
|
+
require 'active_data/model/associations/persistence_adapters/active_record'
|
|
15
19
|
|
|
16
20
|
module ActiveData
|
|
17
21
|
BOOLEAN_MAPPING = {
|
|
@@ -32,33 +36,29 @@ module ActiveData
|
|
|
32
36
|
'y' => true,
|
|
33
37
|
'n' => false,
|
|
34
38
|
'yes' => true,
|
|
35
|
-
'no' => false
|
|
36
|
-
}
|
|
39
|
+
'no' => false
|
|
40
|
+
}.freeze
|
|
37
41
|
|
|
38
42
|
def self.config
|
|
39
43
|
ActiveData::Config.instance
|
|
40
44
|
end
|
|
41
45
|
|
|
42
|
-
singleton_class.delegate
|
|
46
|
+
singleton_class.delegate(*ActiveData::Config.delegated, to: :config)
|
|
43
47
|
|
|
44
48
|
typecaster('Object') { |value, attribute| value if value.class < attribute.type }
|
|
45
|
-
typecaster('String') { |value| value.to_s }
|
|
49
|
+
typecaster('String') { |value, _| value.to_s }
|
|
46
50
|
typecaster('Array') do |value|
|
|
47
51
|
case value
|
|
48
52
|
when ::Array then
|
|
49
53
|
value
|
|
50
54
|
when ::String then
|
|
51
55
|
value.split(',').map(&:strip)
|
|
52
|
-
else
|
|
53
|
-
nil
|
|
54
56
|
end
|
|
55
57
|
end
|
|
56
58
|
typecaster('Hash') do |value|
|
|
57
59
|
case value
|
|
58
60
|
when ::Hash then
|
|
59
61
|
value
|
|
60
|
-
else
|
|
61
|
-
nil
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
typecaster('Date') do |value|
|
|
@@ -89,15 +89,36 @@ module ActiveData
|
|
|
89
89
|
when ::TZInfo::Timezone
|
|
90
90
|
ActiveSupport::TimeZone[value.name]
|
|
91
91
|
when String, Numeric, ActiveSupport::Duration
|
|
92
|
-
value =
|
|
92
|
+
value = begin
|
|
93
|
+
Float(value)
|
|
94
|
+
rescue ArgumentError, TypeError
|
|
95
|
+
value
|
|
96
|
+
end
|
|
93
97
|
ActiveSupport::TimeZone[value]
|
|
94
|
-
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
typecaster('BigDecimal') do |value|
|
|
101
|
+
next unless value
|
|
102
|
+
begin
|
|
103
|
+
::BigDecimal.new Float(value).to_s
|
|
104
|
+
rescue ArgumentError, TypeError
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
typecaster('Float') do |value|
|
|
109
|
+
begin
|
|
110
|
+
Float(value)
|
|
111
|
+
rescue ArgumentError, TypeError
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
typecaster('Integer') do |value|
|
|
116
|
+
begin
|
|
117
|
+
Float(value).to_i
|
|
118
|
+
rescue ArgumentError, TypeError
|
|
95
119
|
nil
|
|
96
120
|
end
|
|
97
121
|
end
|
|
98
|
-
typecaster('BigDecimal') { |value| ::BigDecimal.new Float(value).to_s rescue nil if value }
|
|
99
|
-
typecaster('Float') { |value| Float(value) rescue nil }
|
|
100
|
-
typecaster('Integer') { |value| Float(value).to_i rescue nil }
|
|
101
122
|
typecaster('Boolean') { |value| BOOLEAN_MAPPING[value] }
|
|
102
123
|
typecaster('ActiveData::UUID') do |value|
|
|
103
124
|
case value
|
|
@@ -109,18 +130,20 @@ module ActiveData
|
|
|
109
130
|
ActiveData::UUID.parse_string value
|
|
110
131
|
when Integer
|
|
111
132
|
ActiveData::UUID.parse_int value
|
|
112
|
-
else
|
|
113
|
-
nil
|
|
114
133
|
end
|
|
115
134
|
end
|
|
116
135
|
end
|
|
117
136
|
|
|
118
|
-
require 'active_data/
|
|
137
|
+
require 'active_data/base'
|
|
119
138
|
|
|
120
139
|
ActiveSupport.on_load :active_record do
|
|
121
140
|
require 'active_data/active_record/associations'
|
|
122
141
|
require 'active_data/active_record/nested_attributes'
|
|
123
142
|
|
|
124
143
|
include ActiveData::ActiveRecord::Associations
|
|
125
|
-
|
|
144
|
+
singleton_class.prepend ActiveData::ActiveRecord::NestedAttributes
|
|
145
|
+
|
|
146
|
+
def self.active_data_persistence_adapter
|
|
147
|
+
ActiveData::Model::Associations::PersistenceAdapters::ActiveRecord
|
|
148
|
+
end
|
|
126
149
|
end
|