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
@@ -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