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.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +13 -0
  3. data/.rubocop.yml +56 -0
  4. data/.rubocop_todo.yml +53 -0
  5. data/.rvmrc +1 -1
  6. data/.travis.yml +15 -2
  7. data/Appraisals +1 -1
  8. data/CHANGELOG.md +31 -0
  9. data/Guardfile +8 -8
  10. data/README.md +256 -0
  11. data/Rakefile +2 -4
  12. data/active_data.gemspec +8 -7
  13. data/gemfiles/rails.4.0.gemfile +1 -1
  14. data/gemfiles/rails.4.1.gemfile +1 -1
  15. data/gemfiles/rails.4.2.gemfile +1 -1
  16. data/gemfiles/rails.5.0.gemfile +1 -1
  17. data/gemfiles/rails.5.1.gemfile +14 -0
  18. data/lib/active_data/active_record/associations.rb +18 -13
  19. data/lib/active_data/active_record/nested_attributes.rb +8 -14
  20. data/lib/active_data/base.rb +13 -0
  21. data/lib/active_data/config.rb +4 -4
  22. data/lib/active_data/errors.rb +29 -13
  23. data/lib/active_data/extensions.rb +22 -21
  24. data/lib/active_data/model/associations/base.rb +22 -6
  25. data/lib/active_data/model/associations/embeds_any.rb +17 -0
  26. data/lib/active_data/model/associations/embeds_many.rb +29 -19
  27. data/lib/active_data/model/associations/embeds_one.rb +30 -26
  28. data/lib/active_data/model/associations/nested_attributes.rb +82 -50
  29. data/lib/active_data/model/associations/persistence_adapters/active_record/referenced_proxy.rb +31 -0
  30. data/lib/active_data/model/associations/persistence_adapters/active_record.rb +66 -0
  31. data/lib/active_data/model/associations/persistence_adapters/base.rb +53 -0
  32. data/lib/active_data/model/associations/references_any.rb +41 -0
  33. data/lib/active_data/model/associations/references_many.rb +51 -37
  34. data/lib/active_data/model/associations/references_one.rb +43 -41
  35. data/lib/active_data/model/associations/reflections/base.rb +19 -29
  36. data/lib/active_data/model/associations/reflections/embeds_any.rb +43 -0
  37. data/lib/active_data/model/associations/reflections/embeds_many.rb +3 -13
  38. data/lib/active_data/model/associations/reflections/embeds_one.rb +5 -37
  39. data/lib/active_data/model/associations/reflections/references_any.rb +62 -0
  40. data/lib/active_data/model/associations/reflections/references_many.rb +7 -7
  41. data/lib/active_data/model/associations/reflections/references_one.rb +9 -7
  42. data/lib/active_data/model/associations/reflections/singular.rb +35 -0
  43. data/lib/active_data/model/associations/validations.rb +2 -27
  44. data/lib/active_data/model/associations.rb +12 -10
  45. data/lib/active_data/model/attributes/attribute.rb +10 -10
  46. data/lib/active_data/model/attributes/base.rb +8 -7
  47. data/lib/active_data/model/attributes/localized.rb +4 -4
  48. data/lib/active_data/model/attributes/reference_many.rb +6 -8
  49. data/lib/active_data/model/attributes/reference_one.rb +17 -9
  50. data/lib/active_data/model/attributes/reflections/attribute.rb +2 -2
  51. data/lib/active_data/model/attributes/reflections/base.rb +8 -11
  52. data/lib/active_data/model/attributes/reflections/localized.rb +2 -2
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +11 -22
  54. data/lib/active_data/model/attributes/reflections/represents.rb +5 -6
  55. data/lib/active_data/model/attributes/represents.rb +6 -5
  56. data/lib/active_data/model/attributes.rb +33 -87
  57. data/lib/active_data/model/callbacks.rb +6 -7
  58. data/lib/active_data/model/conventions.rb +2 -0
  59. data/lib/active_data/model/dirty.rb +4 -4
  60. data/lib/active_data/model/lifecycle.rb +18 -20
  61. data/lib/active_data/model/localization.rb +5 -2
  62. data/lib/active_data/model/persistence.rb +2 -2
  63. data/lib/active_data/model/primary.rb +19 -14
  64. data/lib/active_data/model/representation.rb +81 -0
  65. data/lib/active_data/model/scopes.rb +22 -12
  66. data/lib/active_data/model/validations/associated.rb +3 -2
  67. data/lib/active_data/model/validations/nested.rb +6 -1
  68. data/lib/active_data/model/validations.rb +3 -3
  69. data/lib/active_data/model.rb +2 -1
  70. data/lib/active_data/undefined_class.rb +9 -0
  71. data/lib/active_data/version.rb +1 -1
  72. data/lib/active_data.rb +40 -17
  73. data/spec/lib/active_data/active_record/associations_spec.rb +107 -45
  74. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +1 -2
  75. data/spec/lib/active_data/config_spec.rb +37 -15
  76. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +475 -172
  77. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +353 -96
  78. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +108 -12
  79. data/spec/lib/active_data/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  80. data/spec/lib/active_data/model/associations/references_many_spec.rb +440 -64
  81. data/spec/lib/active_data/model/associations/references_one_spec.rb +347 -36
  82. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +8 -7
  83. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +7 -6
  84. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +81 -33
  85. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +116 -37
  86. data/spec/lib/active_data/model/associations/validations_spec.rb +27 -43
  87. data/spec/lib/active_data/model/associations_spec.rb +34 -25
  88. data/spec/lib/active_data/model/attributes/attribute_spec.rb +26 -23
  89. data/spec/lib/active_data/model/attributes/base_spec.rb +5 -6
  90. data/spec/lib/active_data/model/attributes/collection_spec.rb +7 -8
  91. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +40 -33
  92. data/spec/lib/active_data/model/attributes/localized_spec.rb +27 -28
  93. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +6 -6
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +10 -78
  95. data/spec/lib/active_data/model/attributes_spec.rb +150 -45
  96. data/spec/lib/active_data/model/callbacks_spec.rb +69 -70
  97. data/spec/lib/active_data/model/conventions_spec.rb +0 -1
  98. data/spec/lib/active_data/model/dirty_spec.rb +22 -13
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +49 -23
  100. data/spec/lib/active_data/model/persistence_spec.rb +5 -6
  101. data/spec/lib/active_data/model/representation_spec.rb +126 -0
  102. data/spec/lib/active_data/model/scopes_spec.rb +1 -3
  103. data/spec/lib/active_data/model/typecasting_spec.rb +6 -5
  104. data/spec/lib/active_data/model/validations/associated_spec.rb +26 -18
  105. data/spec/lib/active_data/model/validations/nested_spec.rb +89 -18
  106. data/spec/lib/active_data/model_spec.rb +1 -2
  107. data/spec/lib/active_data_spec.rb +0 -1
  108. data/spec/shared/nested_attribute_examples.rb +332 -0
  109. data/spec/spec_helper.rb +3 -0
  110. data/spec/support/model_helpers.rb +2 -2
  111. data/spec/support/muffle_helper.rb +7 -0
  112. metadata +52 -18
  113. data/lib/active_data/model/associations/collection/referenced.rb +0 -26
  114. data/lib/active_data/model/associations/reflections/reference_reflection.rb +0 -45
  115. 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 *[:save, :create, :update, :destroy].map { |action| "_#{action}_performer" }
68
- private *[:save, :create, :update, :destroy].map { |action| "_#{action}_performer=" }
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
- [:save, :create, :update, :destroy].each do |action|
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 *args
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! *args
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
- [:save, :create, :update, :destroy].each do |action|
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 attributes, &block
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! attributes, &block
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 options = {}, &block
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! options = {}, &block
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 &block
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! &block
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 &block
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 &block
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 &block
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 &block
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 &block
296
+ def performer_exec(&block)
299
297
  if block.arity == 1
300
- block.call(self)
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 *args, &block
10
+ def localized(*args, &block)
8
11
  add_attribute(ActiveData::Model::Attributes::Reflections::Localized, *args, &block)
9
12
  end
10
13
 
11
- def fallbacks locale
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 data
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 data
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
- type: ActiveData::UUID,
7
- default: -> { ActiveData::UUID.random_create }
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 *args
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.merge!(type: args.second) if args.second
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 == other
41
- other.instance_of?(self.class) &&
42
- has_primary_attribute? ?
43
- primary_attribute ?
44
- primary_attribute == other.primary_attribute :
45
- object_id == other.object_id :
46
- super(other)
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 source = nil, trust = false
23
+ def initialize(source = nil, trust = false)
24
24
  source ||= self.class.superclass.new
25
25
 
26
- source.each do |entity|
27
- raise AssociationTypeMismatch.new(self.class._scope_model, entity.class) unless entity.is_a?(self.class._scope_model)
28
- end unless trust && source.is_a?(self.class)
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? method, _
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 method, *args, &block
39
- with_scope { self.class._scope_model.public_send(method, *args, &block) }
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, self.class._scope_model.current_scope = self.class._scope_model.current_scope, self
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 scope_base = Array
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 *args
69
+ def scope(*args)
60
70
  if args.empty?
61
71
  current_scope
62
72
  else
63
- scope_class.new *args
73
+ scope_class.new(*args)
64
74
  end
65
75
  end
66
76
 
67
- def current_scope= value
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
- if Array.wrap(value).reject { |r| r.respond_to?(:valid?) && r.valid?(record.validation_context) }.any?
7
- record.errors.add(attribute, :invalid, options.merge(value: value))
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! context = nil
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.new(self)
21
+ raise ActiveData::ValidationError, self
22
22
  end
23
23
  end
24
24
  end
25
25
  end
26
26
 
27
- Dir[File.dirname(__FILE__) + "/validations/*.rb"].each { |file| require file }
27
+ Dir[File.dirname(__FILE__) + '/validations/*.rb'].each { |file| require file }
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'singleton'
2
+
3
+ module ActiveData
4
+ class UndefinedClass
5
+ include Singleton
6
+ end
7
+
8
+ UNDEFINED = UndefinedClass.instance.freeze
9
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveData
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'.freeze
3
3
  end
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 *ActiveData::Config.delegated, to: :config
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 = Float(value) rescue value
92
+ value = begin
93
+ Float(value)
94
+ rescue ArgumentError, TypeError
95
+ value
96
+ end
93
97
  ActiveSupport::TimeZone[value]
94
- else
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/model'
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
- include ActiveData::ActiveRecord::NestedAttributes
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