active_data 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|