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.
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
@@ -4,37 +4,62 @@ module ActiveData
4
4
  module NestedAttributes
5
5
  extend ActiveSupport::Concern
6
6
 
7
- DESTROY_ATTRIBUTE = '_destroy'
7
+ DESTROY_ATTRIBUTE = '_destroy'.freeze
8
8
 
9
9
  included do
10
10
  class_attribute :nested_attributes_options, instance_writer: false
11
11
  self.nested_attributes_options = {}
12
+
13
+ extend NestedAttributesMethodsExtension
14
+ prepend PrependMethods
15
+ end
16
+
17
+ module PrependMethods
18
+ def assign_attributes(attrs)
19
+ if self.class.nested_attributes_options.present?
20
+ attrs = attrs.to_unsafe_hash if attrs.respond_to?(:to_unsafe_hash)
21
+ attrs = attrs.stringify_keys
22
+
23
+ nested_attrs = self.class.nested_attributes_options.keys
24
+ .each_with_object({}) do |association_name, result|
25
+ name = "#{association_name}_attributes"
26
+ result[name] = attrs.delete(name) if attrs.key?(name)
27
+ end
28
+
29
+ super(attrs.merge!(nested_attrs))
30
+ else
31
+ super(attrs)
32
+ end
33
+ end
34
+ alias_method :attributes=, :assign_attributes
12
35
  end
13
36
 
14
37
  class NestedAttributesMethods
15
38
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == DESTROY_ATTRIBUTE || value.blank? } }
16
39
 
17
40
  def self.accepts_nested_attributes_for(klass, *attr_names)
18
- options = { allow_destroy: false, update_only: false }
41
+ options = {allow_destroy: false, update_only: false}
19
42
  options.update(attr_names.extract_options!)
20
43
  options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
21
44
  options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
22
45
 
46
+ NestedAttributesMethodsExtension.ensure_extended!(klass)
47
+
23
48
  attr_names.each do |association_name|
24
- if reflection = klass.reflect_on_association(association_name)
25
- klass.nested_attributes_options = klass.nested_attributes_options.merge(association_name.to_sym => options)
26
-
27
- klass.validates_nested association_name if klass.respond_to?(:validates_nested)
28
- type = (reflection.collection? ? :collection : :one_to_one)
29
- klass.class_eval <<-METHOD, __FILE__, __LINE__ + 1
30
- def #{association_name}_attributes=(attributes)
31
- ActiveData::Model::Associations::NestedAttributes::NestedAttributesMethods
32
- .assign_nested_attributes_for_#{type}_association(self, :#{association_name}, attributes)
33
- end
34
- METHOD
35
- else
36
- raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
37
- end
49
+ reflection = klass.reflect_on_association(association_name)
50
+ raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" unless reflection
51
+ klass.nested_attributes_options = klass.nested_attributes_options.merge(association_name.to_sym => options)
52
+
53
+ should_validate_nested = klass.respond_to?(:validates_nested) && !klass.validates_nested?(association_name)
54
+ klass.validates_nested(association_name) if should_validate_nested
55
+
56
+ type = (reflection.collection? ? :collection : :one_to_one)
57
+ klass.nested_attributes_methods_module.class_eval <<-METHOD, __FILE__, __LINE__ + 1
58
+ def #{association_name}_attributes=(attributes)
59
+ ActiveData::Model::Associations::NestedAttributes::NestedAttributesMethods
60
+ .assign_nested_attributes_for_#{type}_association(self, :#{association_name}, attributes)
61
+ end
62
+ METHOD
38
63
  end
39
64
  end
40
65
 
@@ -54,7 +79,7 @@ module ActiveData
54
79
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(object, association_name, attributes)
55
80
  elsif attributes[primary_attribute_name].present?
56
81
  raise ActiveData::ObjectNotFound.new(object, association_name, attributes[primary_attribute_name])
57
- elsif !reject_new_object?(object, association_name, attributes)
82
+ elsif !reject_new_object?(object, association_name, attributes, options)
58
83
  assignable_attributes = attributes.except(*unassignable_keys(object))
59
84
 
60
85
  if existing_record && !existing_record.persisted?
@@ -92,9 +117,7 @@ module ActiveData
92
117
  attributes = attributes.with_indifferent_access
93
118
 
94
119
  if attributes[primary_attribute_name].blank?
95
- unless reject_new_object?(object, association_name, attributes)
96
- association.build(attributes.except(*unassignable_keys(object)))
97
- end
120
+ association.build(attributes.except(*unassignable_keys(object))) unless reject_new_object?(object, association_name, attributes, options)
98
121
  else
99
122
  existing_record = association.target.detect do |record|
100
123
  primary_attribute_value = record.attribute(primary_attribute_name)
@@ -102,59 +125,53 @@ module ActiveData
102
125
  record.primary_attribute == primary_attribute_value
103
126
  end
104
127
  if existing_record
105
- if !call_reject_if(object, association_name, attributes)
106
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
107
- end
108
- else
109
- if association.reflection.embedded?
110
- unless reject_new_object?(object, association_name, attributes)
111
- association.reflection.klass.with_sanitize(false) do
112
- association.build(attributes.except(DESTROY_ATTRIBUTE))
113
- end
128
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(object, association_name, attributes)
129
+ elsif association.reflection.embedded?
130
+ unless reject_new_object?(object, association_name, attributes, options)
131
+ association.reflection.klass.with_sanitize(false) do
132
+ association.build(attributes.except(DESTROY_ATTRIBUTE))
114
133
  end
115
- else
116
- raise ActiveData::ObjectNotFound.new(object, association_name, attributes[primary_attribute_name])
117
134
  end
135
+ else
136
+ raise ActiveData::ObjectNotFound.new(object, association_name, attributes[primary_attribute_name])
118
137
  end
119
138
  end
120
139
  end
121
140
  end
122
141
 
123
142
  def self.check_record_limit!(limit, attributes_collection)
124
- if limit
125
- limit = case limit
126
- when Symbol
127
- send(limit)
128
- when Proc
129
- limit.call
130
- else
131
- limit
132
- end
133
-
134
- if limit && attributes_collection.size > limit
135
- raise ActiveData::TooManyObjects.new(limit, attributes_collection.size)
136
- end
143
+ limit = case limit
144
+ when Symbol
145
+ send(limit)
146
+ when Proc
147
+ limit.call
148
+ else
149
+ limit
137
150
  end
151
+
152
+ return unless limit && attributes_collection.size > limit
153
+
154
+ raise ActiveData::TooManyObjects.new(limit, attributes_collection.size)
138
155
  end
139
156
 
140
157
  def self.assign_to_or_mark_for_destruction(object, attributes, allow_destroy)
141
158
  object.assign_attributes(attributes.except(*unassignable_keys(object)))
142
- object.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
159
+ object.mark_for_destruction if destroy_flag?(attributes) && allow_destroy
143
160
  end
144
161
 
145
- def self.has_destroy_flag?(hash)
162
+ def self.destroy_flag?(hash)
146
163
  ActiveData.typecaster(Boolean).call(hash[DESTROY_ATTRIBUTE])
147
164
  end
148
165
 
149
- def self.reject_new_object?(object, association_name, attributes)
150
- has_destroy_flag?(attributes) || call_reject_if(object, association_name, attributes)
166
+ def self.reject_new_object?(object, association_name, attributes, options)
167
+ options[:update_only] || destroy_flag?(attributes) || call_reject_if(object, association_name, attributes)
151
168
  end
152
169
 
153
170
  def self.call_reject_if(object, association_name, attributes)
154
- return false if has_destroy_flag?(attributes)
171
+ return false if destroy_flag?(attributes)
155
172
  case callback = object.nested_attributes_options[association_name][:reject_if]
156
173
  when Symbol
157
- method(callback).arity == 0 ? send(callback) : send(callback, attributes)
174
+ method(callback).arity.zero? ? send(callback) : send(callback, attributes)
158
175
  when Proc
159
176
  callback.call(attributes)
160
177
  end
@@ -174,6 +191,21 @@ module ActiveData
174
191
  NestedAttributesMethods.accepts_nested_attributes_for self, *attr_names
175
192
  end
176
193
  end
194
+
195
+ module NestedAttributesMethodsExtension
196
+ def self.ensure_extended!(klass)
197
+ return if klass.singleton_class.ancestors.include?(self)
198
+ klass.extend(self)
199
+ end
200
+
201
+ def nested_attributes_methods_module
202
+ @nested_attributes_methods_module ||= begin
203
+ mod = const_set(:NestedAttributesMethods, Module.new)
204
+ include(mod)
205
+ mod
206
+ end
207
+ end
208
+ end
177
209
  end
178
210
  end
179
211
  end
@@ -0,0 +1,31 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module PersistenceAdapters
5
+ class ActiveRecord < Base
6
+ class ReferencedProxy < ActiveData::Model::Associations::Collection::Proxy
7
+ # You can't create data directly through ActiveRecord::Relation
8
+ METHODS_EXCLUDED_FROM_DELEGATION = %w[build create create!].map(&:to_sym).freeze
9
+
10
+ attr_reader :association
11
+ delegate :scope, to: :@association
12
+
13
+ def method_missing(method, *args, &block)
14
+ delegate_to_scope?(method) ? scope.send(method, *args, &block) : super
15
+ end
16
+
17
+ def respond_to_missing?(method, include_private = false)
18
+ delegate_to_scope?(method) || super
19
+ end
20
+
21
+ private
22
+
23
+ def delegate_to_scope?(method)
24
+ METHODS_EXCLUDED_FROM_DELEGATION.exclude?(method) && scope.respond_to?(method)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,66 @@
1
+ require 'active_data/model/associations/persistence_adapters/active_record/referenced_proxy'
2
+
3
+ module ActiveData
4
+ module Model
5
+ module Associations
6
+ module PersistenceAdapters
7
+ class ActiveRecord < Base
8
+ TYPES = {
9
+ integer: Integer,
10
+ float: Float,
11
+ decimal: BigDecimal,
12
+ datetime: Time,
13
+ timestamp: Time,
14
+ time: Time,
15
+ date: Date,
16
+ text: String,
17
+ string: String,
18
+ binary: String,
19
+ boolean: Boolean
20
+ }.freeze
21
+
22
+ alias_method :data_type, :data_source
23
+
24
+ def build(attributes)
25
+ data_source.new(attributes)
26
+ end
27
+
28
+ def persist(object, raise_error: false)
29
+ raise_error ? object.save! : object.save
30
+ end
31
+
32
+ def scope(owner, source)
33
+ scope = data_source.unscoped
34
+
35
+ if scope_proc
36
+ scope = if scope_proc.arity.zero?
37
+ scope.instance_exec(&scope_proc)
38
+ else
39
+ scope.instance_exec(owner, &scope_proc)
40
+ end
41
+ end
42
+
43
+ scope.where(primary_key => source)
44
+ end
45
+
46
+ def identify(object)
47
+ object[primary_key] if object
48
+ end
49
+
50
+ def primary_key
51
+ @primary_key ||= :id
52
+ end
53
+
54
+ def primary_key_type
55
+ column = data_source.columns_hash[primary_key.to_s]
56
+ TYPES[column.type]
57
+ end
58
+
59
+ def referenced_proxy(association)
60
+ ReferencedProxy.new(association)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,53 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module PersistenceAdapters
5
+ class Base
6
+ attr_reader :data_source, :primary_key, :scope_proc
7
+
8
+ def initialize(data_source, primary_key, scope_proc = nil)
9
+ @data_source = data_source
10
+ @primary_key = primary_key
11
+ @scope_proc = scope_proc
12
+ end
13
+
14
+ def build(_attributes)
15
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Build new instance of data object by attributes'
16
+ end
17
+
18
+ def persist(_object, *)
19
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Build new instance of data object by attributes'
20
+ end
21
+
22
+ def scope(_owner, _source)
23
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Better to be Enumerable'
24
+ end
25
+
26
+ def find_one(owner, identificator)
27
+ scope(owner, identificator).first
28
+ end
29
+
30
+ def find_all(owner, identificators)
31
+ scope(owner, identificators).to_a
32
+ end
33
+
34
+ def identify(_object)
35
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Field to be used as primary_key for object'
36
+ end
37
+
38
+ def data_type
39
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Type of data object for type_check'
40
+ end
41
+
42
+ def primary_key_type
43
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Ruby data type'
44
+ end
45
+
46
+ def referenced_proxy
47
+ raise NotImplementedError, 'Should be implemented in inhereted adapter. Object to manage proxying of methods to scope.'
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,41 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ class ReferencesAny < Base
5
+ def scope(source = read_source)
6
+ reflection.persistence_adapter.scope(owner, source)
7
+ end
8
+
9
+ private
10
+
11
+ def read_source
12
+ attribute.read_before_type_cast
13
+ end
14
+
15
+ def write_source(value)
16
+ attribute.write_value value
17
+ end
18
+
19
+ def attribute
20
+ @attribute ||= owner.attribute(reflection.reference_key)
21
+ end
22
+
23
+ def build_object(attributes)
24
+ reflection.persistence_adapter.build(attributes)
25
+ end
26
+
27
+ def persist_object(object, **options)
28
+ reflection.persistence_adapter.persist(object, **options)
29
+ end
30
+
31
+ def matches_type?(object)
32
+ object.is_a?(reflection.persistence_adapter.data_type)
33
+ end
34
+
35
+ def raise_type_mismatch(object)
36
+ raise AssociationTypeMismatch.new(reflection.persistence_adapter.data_type, object.class)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,50 +1,71 @@
1
1
  module ActiveData
2
2
  module Model
3
3
  module Associations
4
- class ReferencesMany < Base
4
+ class ReferencesMany < ReferencesAny
5
+ def build(attributes = {})
6
+ append([build_object(attributes)]).last
7
+ end
8
+
9
+ def create(attributes = {})
10
+ object = build(attributes)
11
+ persist_object(object)
12
+ object
13
+ end
14
+
15
+ def create!(attributes = {})
16
+ object = build(attributes)
17
+ persist_object(object, raise_error: true)
18
+ object
19
+ end
20
+
5
21
  def apply_changes
6
- present_keys = target.reject { |t| t.marked_for_destruction? }.map(&reflection.primary_key).compact
7
- write_source(present_keys)
8
- true
22
+ target.all? do |object|
23
+ if object
24
+ if object.marked_for_destruction? && reflection.autosave?
25
+ object.destroy
26
+ elsif object.new_record? || (reflection.autosave? && object.changed?)
27
+ persist_object(object)
28
+ else
29
+ true
30
+ end
31
+ else
32
+ true
33
+ end
34
+ end
9
35
  end
10
36
 
11
- def target= object
37
+ def target=(object)
12
38
  loaded!
13
39
  @target = object.to_a
14
40
  end
15
41
 
16
42
  def load_target
17
43
  source = read_source
18
- source.present? ? scope(source).to_a : default
44
+ source.present? ? reflection.persistence_adapter.find_all(owner, source) : default
19
45
  end
20
46
 
21
47
  def default
22
- unless evar_loaded?
23
- default = Array.wrap(reflection.default(owner))
24
- if default.all? { |object| object.is_a?(reflection.klass) }
25
- default
26
- elsif default.all? { |object| object.is_a?(Hash) }
27
- default.map { |attributes| reflection.klass.new(attributes) }
28
- else
29
- scope(default).to_a
30
- end if default.present?
31
- end || []
32
- end
48
+ return [] if evar_loaded?
33
49
 
34
- def read_source
35
- attribute.read_before_type_cast
36
- end
50
+ default = Array.wrap(reflection.default(owner))
37
51
 
38
- def write_source value
39
- attribute.write_value value
52
+ return [] unless default
53
+
54
+ if default.all? { |object| object.is_a?(reflection.persistence_adapter.data_type) }
55
+ default
56
+ elsif default.all? { |object| object.is_a?(Hash) }
57
+ default.map { |attributes| build_object(attributes) }
58
+ else
59
+ reflection.persistence_adapter.find_all(owner, default)
60
+ end || []
40
61
  end
41
62
 
42
- def reader force_reload = false
63
+ def reader(force_reload = false)
43
64
  reload if force_reload
44
- @proxy ||= Collection::Referenced.new self
65
+ @proxy ||= reflection.persistence_adapter.referenced_proxy(self)
45
66
  end
46
67
 
47
- def replace objects
68
+ def replace(objects)
48
69
  loaded!
49
70
  transaction do
50
71
  clear
@@ -65,31 +86,24 @@ module ActiveData
65
86
  reload.empty?
66
87
  end
67
88
 
68
- def scope source = read_source
69
- reflection.scope.where(reflection.primary_key => source)
70
- end
71
-
72
89
  def identify
73
- target.map(&reflection.primary_key)
90
+ target.map { |obj| reflection.persistence_adapter.identify(obj) }
74
91
  end
75
92
 
76
93
  private
77
94
 
78
- def append objects
95
+ def append(objects)
79
96
  attribute.pollute do
80
97
  objects.each do |object|
81
98
  next if target.include?(object)
82
- raise AssociationTypeMismatch.new(reflection.klass, object.class) unless object.is_a?(reflection.klass)
99
+ raise_type_mismatch(object) unless matches_type?(object)
100
+
83
101
  target.push(object)
84
- apply_changes!
102
+ write_source(identify)
85
103
  end
86
104
  end
87
105
  target
88
106
  end
89
-
90
- def attribute
91
- @attribute ||= owner.attribute(reflection.reference_key)
92
- end
93
107
  end
94
108
  end
95
109
  end
@@ -1,62 +1,74 @@
1
1
  module ActiveData
2
2
  module Model
3
3
  module Associations
4
- class ReferencesOne < Base
4
+ class ReferencesOne < ReferencesAny
5
+ def build(attributes = {})
6
+ replace(build_object(attributes))
7
+ end
8
+
9
+ def create(attributes = {})
10
+ persist_object(build(attributes))
11
+ target
12
+ end
13
+
14
+ def create!(attributes = {})
15
+ persist_object(build(attributes), raise_error: true)
16
+ target
17
+ end
18
+
5
19
  def apply_changes
6
- if target && !target.marked_for_destruction?
7
- write_source identify
20
+ if target
21
+ if target.marked_for_destruction? && reflection.autosave?
22
+ target.destroy
23
+ elsif target.new_record? || (reflection.autosave? && target.changed?)
24
+ persist_object(target)
25
+ else
26
+ true
27
+ end
8
28
  else
9
- write_source nil
29
+ true
10
30
  end
11
- true
12
31
  end
13
32
 
14
- def target= object
33
+ def target=(object)
15
34
  loaded!
16
35
  @target = object
17
36
  end
18
37
 
19
38
  def load_target
20
39
  source = read_source
21
- source ? scope(source).first : default
40
+ source ? reflection.persistence_adapter.find_one(owner, source) : default
22
41
  end
23
42
 
24
43
  def default
25
- unless evar_loaded?
26
- default = reflection.default(owner)
27
- case default
28
- when reflection.klass
29
- default
30
- when Hash
31
- reflection.klass.new(default)
32
- else
33
- scope(default).first
34
- end if default
35
- end
36
- end
44
+ return if evar_loaded?
37
45
 
38
- def read_source
39
- attribute.read_before_type_cast
40
- end
46
+ default = reflection.default(owner)
47
+
48
+ return unless default
41
49
 
42
- def write_source value
43
- attribute.write_value value
50
+ case default
51
+ when reflection.persistence_adapter.data_type
52
+ default
53
+ when Hash
54
+ build_object(default)
55
+ else
56
+ reflection.persistence_adapter.find_one(owner, default)
57
+ end
44
58
  end
45
59
 
46
- def reader force_reload = false
60
+ def reader(force_reload = false)
47
61
  reset if force_reload
48
62
  target
49
63
  end
50
64
 
51
- def replace object
52
- unless object.nil? || object.is_a?(reflection.klass)
53
- raise AssociationTypeMismatch.new(reflection.klass, object.class)
54
- end
65
+ def replace(object)
66
+ raise_type_mismatch(object) unless object.nil? || matches_type?(object)
55
67
 
56
68
  transaction do
57
69
  attribute.pollute do
58
70
  self.target = object
59
- apply_changes!
71
+ write_source identify
60
72
  end
61
73
  end
62
74
 
@@ -64,18 +76,8 @@ module ActiveData
64
76
  end
65
77
  alias_method :writer, :replace
66
78
 
67
- def scope source = read_source
68
- reflection.scope.where(reflection.primary_key => source)
69
- end
70
-
71
79
  def identify
72
- target.try(reflection.primary_key)
73
- end
74
-
75
- private
76
-
77
- def attribute
78
- @attribute ||= owner.attribute(reflection.reference_key)
80
+ reflection.persistence_adapter.identify(target)
79
81
  end
80
82
  end
81
83
  end