active_data 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rspec +0 -1
  4. data/.rvmrc +1 -1
  5. data/.travis.yml +13 -6
  6. data/Appraisals +7 -0
  7. data/Gemfile +1 -5
  8. data/Guardfile +68 -15
  9. data/README.md +144 -2
  10. data/active_data.gemspec +19 -11
  11. data/gemfiles/rails.4.0.gemfile +14 -0
  12. data/gemfiles/rails.4.1.gemfile +14 -0
  13. data/gemfiles/rails.4.2.gemfile +14 -0
  14. data/gemfiles/rails.5.0.gemfile +14 -0
  15. data/lib/active_data.rb +120 -3
  16. data/lib/active_data/active_record/associations.rb +50 -0
  17. data/lib/active_data/active_record/nested_attributes.rb +24 -0
  18. data/lib/active_data/config.rb +40 -0
  19. data/lib/active_data/errors.rb +93 -0
  20. data/lib/active_data/extensions.rb +33 -0
  21. data/lib/active_data/model.rb +16 -74
  22. data/lib/active_data/model/associations.rb +84 -15
  23. data/lib/active_data/model/associations/base.rb +79 -0
  24. data/lib/active_data/model/associations/collection/embedded.rb +12 -0
  25. data/lib/active_data/model/associations/collection/proxy.rb +32 -0
  26. data/lib/active_data/model/associations/collection/referenced.rb +26 -0
  27. data/lib/active_data/model/associations/embeds_many.rb +124 -18
  28. data/lib/active_data/model/associations/embeds_one.rb +90 -15
  29. data/lib/active_data/model/associations/nested_attributes.rb +180 -0
  30. data/lib/active_data/model/associations/references_many.rb +96 -0
  31. data/lib/active_data/model/associations/references_one.rb +83 -0
  32. data/lib/active_data/model/associations/reflections/base.rb +100 -0
  33. data/lib/active_data/model/associations/reflections/embeds_many.rb +25 -0
  34. data/lib/active_data/model/associations/reflections/embeds_one.rb +49 -0
  35. data/lib/active_data/model/associations/reflections/reference_reflection.rb +45 -0
  36. data/lib/active_data/model/associations/reflections/references_many.rb +28 -0
  37. data/lib/active_data/model/associations/reflections/references_one.rb +28 -0
  38. data/lib/active_data/model/associations/validations.rb +63 -0
  39. data/lib/active_data/model/attributes.rb +247 -0
  40. data/lib/active_data/model/attributes/attribute.rb +73 -0
  41. data/lib/active_data/model/attributes/base.rb +116 -0
  42. data/lib/active_data/model/attributes/collection.rb +17 -0
  43. data/lib/active_data/model/attributes/dictionary.rb +26 -0
  44. data/lib/active_data/model/attributes/localized.rb +42 -0
  45. data/lib/active_data/model/attributes/reference_many.rb +21 -0
  46. data/lib/active_data/model/attributes/reference_one.rb +42 -0
  47. data/lib/active_data/model/attributes/reflections/attribute.rb +55 -0
  48. data/lib/active_data/model/attributes/reflections/base.rb +62 -0
  49. data/lib/active_data/model/attributes/reflections/collection.rb +10 -0
  50. data/lib/active_data/model/attributes/reflections/dictionary.rb +13 -0
  51. data/lib/active_data/model/attributes/reflections/localized.rb +43 -0
  52. data/lib/active_data/model/attributes/reflections/reference_many.rb +10 -0
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +58 -0
  54. data/lib/active_data/model/attributes/reflections/represents.rb +55 -0
  55. data/lib/active_data/model/attributes/represents.rb +64 -0
  56. data/lib/active_data/model/callbacks.rb +71 -0
  57. data/lib/active_data/model/conventions.rb +35 -0
  58. data/lib/active_data/model/dirty.rb +77 -0
  59. data/lib/active_data/model/lifecycle.rb +307 -0
  60. data/lib/active_data/model/localization.rb +21 -0
  61. data/lib/active_data/model/persistence.rb +57 -0
  62. data/lib/active_data/model/primary.rb +51 -0
  63. data/lib/active_data/model/scopes.rb +77 -0
  64. data/lib/active_data/model/validations.rb +27 -0
  65. data/lib/active_data/model/validations/associated.rb +19 -0
  66. data/lib/active_data/model/validations/nested.rb +39 -0
  67. data/lib/active_data/railtie.rb +7 -0
  68. data/lib/active_data/version.rb +1 -1
  69. data/spec/lib/active_data/active_record/associations_spec.rb +149 -0
  70. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +16 -0
  71. data/spec/lib/active_data/config_spec.rb +44 -0
  72. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +362 -52
  73. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +250 -31
  74. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +23 -0
  75. data/spec/lib/active_data/model/associations/references_many_spec.rb +196 -0
  76. data/spec/lib/active_data/model/associations/references_one_spec.rb +134 -0
  77. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +144 -0
  78. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +116 -0
  79. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +255 -0
  80. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +208 -0
  81. data/spec/lib/active_data/model/associations/validations_spec.rb +153 -0
  82. data/spec/lib/active_data/model/associations_spec.rb +189 -0
  83. data/spec/lib/active_data/model/attributes/attribute_spec.rb +144 -0
  84. data/spec/lib/active_data/model/attributes/base_spec.rb +82 -0
  85. data/spec/lib/active_data/model/attributes/collection_spec.rb +73 -0
  86. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +93 -0
  87. data/spec/lib/active_data/model/attributes/localized_spec.rb +88 -33
  88. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +72 -0
  89. data/spec/lib/active_data/model/attributes/reflections/base_spec.rb +56 -0
  90. data/spec/lib/active_data/model/attributes/reflections/collection_spec.rb +37 -0
  91. data/spec/lib/active_data/model/attributes/reflections/dictionary_spec.rb +43 -0
  92. data/spec/lib/active_data/model/attributes/reflections/localized_spec.rb +37 -0
  93. data/spec/lib/active_data/model/attributes/reflections/represents_spec.rb +70 -0
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +153 -0
  95. data/spec/lib/active_data/model/attributes_spec.rb +243 -0
  96. data/spec/lib/active_data/model/callbacks_spec.rb +338 -0
  97. data/spec/lib/active_data/model/conventions_spec.rb +12 -0
  98. data/spec/lib/active_data/model/dirty_spec.rb +75 -0
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +330 -0
  100. data/spec/lib/active_data/model/nested_attributes.rb +202 -0
  101. data/spec/lib/active_data/model/persistence_spec.rb +47 -0
  102. data/spec/lib/active_data/model/primary_spec.rb +84 -0
  103. data/spec/lib/active_data/model/scopes_spec.rb +88 -0
  104. data/spec/lib/active_data/model/typecasting_spec.rb +192 -0
  105. data/spec/lib/active_data/model/validations/associated_spec.rb +94 -0
  106. data/spec/lib/active_data/model/validations/nested_spec.rb +93 -0
  107. data/spec/lib/active_data/model/validations_spec.rb +31 -0
  108. data/spec/lib/active_data/model_spec.rb +1 -32
  109. data/spec/lib/active_data_spec.rb +12 -0
  110. data/spec/spec_helper.rb +39 -0
  111. data/spec/support/model_helpers.rb +10 -0
  112. metadata +246 -54
  113. data/gemfiles/Gemfile.rails-3 +0 -14
  114. data/lib/active_data/attributes/base.rb +0 -69
  115. data/lib/active_data/attributes/localized.rb +0 -42
  116. data/lib/active_data/model/associations/association.rb +0 -30
  117. data/lib/active_data/model/attributable.rb +0 -122
  118. data/lib/active_data/model/collectionizable.rb +0 -55
  119. data/lib/active_data/model/collectionizable/proxy.rb +0 -42
  120. data/lib/active_data/model/extensions.rb +0 -9
  121. data/lib/active_data/model/extensions/array.rb +0 -24
  122. data/lib/active_data/model/extensions/big_decimal.rb +0 -17
  123. data/lib/active_data/model/extensions/boolean.rb +0 -38
  124. data/lib/active_data/model/extensions/date.rb +0 -17
  125. data/lib/active_data/model/extensions/date_time.rb +0 -17
  126. data/lib/active_data/model/extensions/float.rb +0 -17
  127. data/lib/active_data/model/extensions/hash.rb +0 -22
  128. data/lib/active_data/model/extensions/integer.rb +0 -17
  129. data/lib/active_data/model/extensions/localized.rb +0 -22
  130. data/lib/active_data/model/extensions/object.rb +0 -17
  131. data/lib/active_data/model/extensions/string.rb +0 -17
  132. data/lib/active_data/model/extensions/time.rb +0 -17
  133. data/lib/active_data/model/localizable.rb +0 -31
  134. data/lib/active_data/model/nested_attributes.rb +0 -58
  135. data/lib/active_data/model/parameterizable.rb +0 -29
  136. data/lib/active_data/validations.rb +0 -7
  137. data/lib/active_data/validations/associated.rb +0 -17
  138. data/spec/lib/active_data/model/attributable_spec.rb +0 -191
  139. data/spec/lib/active_data/model/collectionizable_spec.rb +0 -60
  140. data/spec/lib/active_data/model/nested_attributes_spec.rb +0 -67
  141. data/spec/lib/active_data/model/type_cast_spec.rb +0 -116
  142. data/spec/lib/active_data/validations/associated_spec.rb +0 -88
@@ -0,0 +1,50 @@
1
+ module ActiveData
2
+ module ActiveRecord
3
+ module Associations
4
+ module Reflections
5
+ class EmbedsOne < ActiveData::Model::Associations::Reflections::EmbedsOne
6
+ def is_a? klass
7
+ super || klass == ::ActiveRecord::Reflection::AssociationReflection
8
+ end
9
+ end
10
+
11
+ class EmbedsMany < ActiveData::Model::Associations::Reflections::EmbedsMany
12
+ def is_a? klass
13
+ super || klass == ::ActiveRecord::Reflection::AssociationReflection
14
+ end
15
+ end
16
+ end
17
+
18
+ extend ActiveSupport::Concern
19
+
20
+ included do
21
+ { embeds_many: Reflections::EmbedsMany, embeds_one: Reflections::EmbedsOne }.each do |(name, reflection_class)|
22
+ define_singleton_method name do |name, options = {}, &block|
23
+ reflection = reflection_class.build(self, self, name, options.reverse_merge(
24
+ read: ->(reflection, object) {
25
+ value = object.read_attribute(reflection.name)
26
+ JSON.parse(value) if value.present?
27
+ },
28
+ write: ->(reflection, object, value) {
29
+ object.send(:write_attribute, reflection.name, value ? value.to_json : nil)
30
+ }
31
+ ), &block)
32
+ if ::ActiveRecord::Reflection.respond_to? :add_reflection
33
+ ::ActiveRecord::Reflection.add_reflection self, reflection.name, reflection
34
+ else
35
+ self.reflections = reflections.merge(reflection.name => reflection)
36
+ end
37
+
38
+ callback_name = :"update_#{reflection.name}_association"
39
+ before_save callback_name
40
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
41
+ def #{callback_name}
42
+ association(:#{reflection.name}).apply_changes!
43
+ end
44
+ METHOD
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveData
2
+ module ActiveRecord
3
+ module NestedAttributes
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ singleton_class.alias_method_chain :accepts_nested_attributes_for, :active_data
8
+ end
9
+
10
+ module ClassMethods
11
+ def accepts_nested_attributes_for_with_active_data *attr_names
12
+ options = attr_names.extract_options!
13
+ active_data_associations, active_record_association = attr_names.partition do |association_name|
14
+ reflect_on_association(association_name).is_a?(ActiveData::Model::Associations::Reflections::Base)
15
+ end
16
+
17
+ ActiveData::Model::Associations::NestedAttributes::NestedAttributesMethods
18
+ .accepts_nested_attributes_for self, *active_data_associations, options.dup
19
+ accepts_nested_attributes_for_without_active_data *active_record_association, options.dup
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveData
2
+ class Config
3
+ include Singleton
4
+
5
+ attr_accessor :include_root_in_json, :i18n_scope, :logger, :primary_attribute,
6
+ :_normalizers, :_typecasters
7
+
8
+ def self.delegated
9
+ public_instance_methods - superclass.public_instance_methods - Singleton.public_instance_methods
10
+ end
11
+
12
+ def initialize
13
+ @include_root_in_json = false
14
+ @i18n_scope = :active_data
15
+ @logger = Logger.new(STDERR)
16
+ @primary_attribute = :id
17
+ @_normalizers = {}
18
+ @_typecasters = {}
19
+ end
20
+
21
+ def normalizer name, &block
22
+ if block
23
+ _normalizers[name.to_sym] = block
24
+ else
25
+ _normalizers[name.to_sym] or raise NormalizerMissing.new(name)
26
+ end
27
+ end
28
+
29
+ def typecaster *classes, &block
30
+ classes = classes.flatten
31
+ if block
32
+ _typecasters[classes.first.to_s.camelize] = block
33
+ else
34
+ _typecasters[classes.detect do |klass|
35
+ _typecasters[klass.to_s.camelize]
36
+ end.to_s.camelize] or raise TypecasterMissing.new(*classes)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,93 @@
1
+ module ActiveData
2
+ class ActiveDataError < StandardError
3
+ end
4
+
5
+ class NotFound < ActiveDataError
6
+ end
7
+
8
+ # Backported from active_model 5
9
+ class ValidationError < ActiveDataError
10
+ attr_reader :model
11
+
12
+ def initialize(model)
13
+ @model = model
14
+ errors = @model.errors.full_messages.join(", ")
15
+ super(I18n.t(:"#{@model.class.i18n_scope}.errors.messages.model_invalid", errors: errors, default: :'errors.messages.model_invalid'))
16
+ end
17
+ end
18
+
19
+ class UnsavableObject < ActiveDataError
20
+ end
21
+
22
+ class UndestroyableObject < ActiveDataError
23
+ end
24
+
25
+ class ObjectNotSaved < ActiveDataError
26
+ end
27
+
28
+ class ObjectNotDestroyed < ActiveDataError
29
+ end
30
+
31
+ class AssociationChangesNotApplied < ActiveDataError
32
+ end
33
+
34
+ class AssociationTypeMismatch < ActiveDataError
35
+ def initialize expected, got
36
+ super "Expected `#{expected}` (##{expected.object_id}), but got `#{got}` (##{got.object_id})"
37
+ end
38
+ end
39
+
40
+ class ObjectNotFound < ActiveDataError
41
+ def initialize object, association_name, record_id
42
+ message = "Couldn't find #{object.class.reflect_on_association(association_name).klass.name}" \
43
+ "with #{object.respond_to?(:_primary_name) ? object._primary_name : 'id'} = #{record_id} for #{object.inspect}"
44
+ super message
45
+ end
46
+ end
47
+
48
+ class TooManyObjects < ActiveDataError
49
+ def initialize limit, actual_size
50
+ super "Maximum #{limit} objects are allowed. Got #{actual_size} objects instead."
51
+ end
52
+ end
53
+
54
+ class UndefinedPrimaryAttribute < ActiveDataError
55
+ def initialize klass, association_name
56
+ super <<-EOS
57
+ Undefined primary attribute for `#{association_name}` in #{klass}.
58
+ It is required for embeds_many nested attributes proper operation.
59
+ You can define this association as:
60
+
61
+ embeds_many :#{association_name} do
62
+ primary :attribute_name
63
+ end
64
+ EOS
65
+ end
66
+ end
67
+
68
+ class NormalizerMissing < NoMethodError
69
+ def initialize name
70
+ super <<-EOS
71
+ Could not find normalizer `:#{name}`
72
+ You can define it with:
73
+
74
+ ActiveData.normalizer(:#{name}) do |value, options|
75
+ # do some staff with value and options
76
+ end
77
+ EOS
78
+ end
79
+ end
80
+
81
+ class TypecasterMissing < NoMethodError
82
+ def initialize *classes
83
+ super <<-EOS
84
+ Could not find typecaster for #{classes}
85
+ You can define it with:
86
+
87
+ ActiveData.typecaster('#{classes.first}') do |value|
88
+ # do some staff with value and options
89
+ end
90
+ EOS
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,33 @@
1
+ unless defined?(Boolean)
2
+ class Boolean; end
3
+ end
4
+
5
+ begin
6
+ require 'uuidtools'
7
+ rescue LoadError
8
+ else
9
+ class ActiveData::UUID < UUIDTools::UUID
10
+ def as_json *_
11
+ to_s
12
+ end
13
+
14
+ def to_param
15
+ to_s
16
+ end
17
+
18
+ def self.parse_string value
19
+ return nil if value.length == 0
20
+ if value.length == 36
21
+ parse value
22
+ elsif value.length == 32
23
+ parse_hexdigest value
24
+ else
25
+ parse_raw value
26
+ end
27
+ end
28
+
29
+ def inspect
30
+ "#<ActiveData::UUID:#{to_s}>"
31
+ end
32
+ end
33
+ end
@@ -1,88 +1,30 @@
1
- require 'active_data/model/extensions'
2
- require 'active_data/model/collectionizable'
3
- require 'active_data/model/attributable'
4
- require 'active_data/model/localizable'
1
+ require 'active_data/model/conventions'
2
+ require 'active_data/model/attributes'
3
+ require 'active_data/model/scopes'
4
+ require 'active_data/model/primary'
5
+ require 'active_data/model/lifecycle'
6
+ require 'active_data/model/persistence'
7
+ require 'active_data/model/callbacks'
5
8
  require 'active_data/model/associations'
6
- require 'active_data/model/nested_attributes'
7
- require 'active_data/model/parameterizable'
8
-
9
- require 'active_data/attributes/base'
10
- require 'active_data/attributes/localized'
9
+ require 'active_data/model/validations'
10
+ require 'active_data/model/localization'
11
+ require 'active_data/model/dirty'
11
12
 
12
13
  module ActiveData
13
14
  module Model
14
- class NotFound < ::StandardError
15
- end
16
-
17
15
  extend ActiveSupport::Concern
18
16
 
19
17
  included do
20
- include ActiveModel::Conversion
21
- include ActiveModel::Validations
22
- include ActiveModel::Serialization
23
- include ActiveModel::Serializers::JSON
24
-
25
- include Attributable
26
- include Localizable
27
- include Collectionizable
28
- include Associations
29
- include NestedAttributes
30
- include Parameterizable
31
18
  extend ActiveModel::Naming
32
19
  extend ActiveModel::Translation
33
20
 
34
- self.include_root_in_json = false
35
-
36
- def initialize attributes = {}
37
- @attributes = self.class.initialize_attributes
38
- @new_record = true
39
- assign_attributes attributes
40
- end
41
-
42
- def self.i18n_scope
43
- :active_data
44
- end
45
-
46
- def serializable_hash *args
47
- super(*args).stringify_keys!
48
- end
49
- end
50
-
51
- module ClassMethods
52
- def instantiate data
53
- return data if data.class.include? ActiveData::Model
54
-
55
- data = data.symbolize_keys
56
- instance = allocate
57
-
58
- attributes = initialize_attributes
59
- attributes.merge!(data.slice(*attributes.keys))
60
-
61
- data.slice(*association_names.map(&:to_sym)).each do |association, data|
62
- instance.send(:"#{association}=", data)
63
- end
64
-
65
- instance.instance_variable_set(:@attributes, attributes)
66
- instance.instance_variable_set(:@new_record, false)
67
-
68
- instance
69
- end
70
- end
71
-
72
- def errors
73
- @errors ||= ActiveModel::Errors.new(self)
74
- end
75
-
76
- def persisted?
77
- !@new_record
78
- end
79
-
80
- def == other
81
- other.instance_of?(self.class) && other.attributes == attributes
82
- end
21
+ include ActiveModel::Conversion
22
+ include ActiveModel::Serialization
23
+ include ActiveModel::Serializers::JSON
83
24
 
84
- def inspect
85
- "#<#{self.class} #{attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(' ')}>"
25
+ include Conventions
26
+ include Attributes
27
+ include Validations
86
28
  end
87
29
  end
88
30
  end
@@ -1,6 +1,22 @@
1
- require 'active_data/model/associations/association'
2
- require 'active_data/model/associations/embeds_many'
1
+ require 'active_data/model/associations/collection/proxy'
2
+ require 'active_data/model/associations/collection/embedded'
3
+ require 'active_data/model/associations/collection/referenced'
4
+
5
+ require 'active_data/model/associations/reflections/base'
6
+ require 'active_data/model/associations/reflections/embeds_one'
7
+ require 'active_data/model/associations/reflections/embeds_many'
8
+ require 'active_data/model/associations/reflections/reference_reflection'
9
+ require 'active_data/model/associations/reflections/references_one'
10
+ require 'active_data/model/associations/reflections/references_many'
11
+
12
+ require 'active_data/model/associations/base'
3
13
  require 'active_data/model/associations/embeds_one'
14
+ require 'active_data/model/associations/embeds_many'
15
+ require 'active_data/model/associations/references_one'
16
+ require 'active_data/model/associations/references_many'
17
+
18
+ require 'active_data/model/associations/nested_attributes'
19
+ require 'active_data/model/associations/validations'
4
20
 
5
21
  module ActiveData
6
22
  module Model
@@ -8,39 +24,92 @@ module ActiveData
8
24
  extend ActiveSupport::Concern
9
25
 
10
26
  included do
11
- class_attribute :_associations, instance_reader: false, instance_writer: false
27
+ include NestedAttributes
28
+ include Validations
29
+
30
+ class_attribute :_associations, :_association_aliases, instance_reader: false, instance_writer: false
12
31
  self._associations = {}
32
+ self._association_aliases = {}
33
+
34
+ delegate :association_names, to: 'self.class'
13
35
 
14
- { embeds_many: EmbedsMany, embeds_one: EmbedsOne }.each do |(name, association_class)|
15
- define_singleton_method name do |*args|
16
- association = association_class.new *args
17
- association.define_accessor self
18
- self._associations = _associations.merge(association.name => association)
36
+ {
37
+ embeds_many: Reflections::EmbedsMany,
38
+ embeds_one: Reflections::EmbedsOne,
39
+ references_one: Reflections::ReferencesOne,
40
+ references_many: Reflections::ReferencesMany
41
+ }.each do |(name, reflection_class)|
42
+ define_singleton_method name do |*args, &block|
43
+ reflection = reflection_class.build self, generated_associations_methods, *args, &block
44
+ self._associations = _associations.merge(reflection.name => reflection)
45
+ reflection
19
46
  end
20
47
  end
21
48
  end
22
49
 
23
50
  module ClassMethods
51
+ def reflections
52
+ _associations
53
+ end
24
54
 
25
- def reflect_on_association name
26
- _associations[name.to_s]
55
+ def alias_association(alias_name, association_name)
56
+ reflection = reflect_on_association(association_name)
57
+ raise ArgumentError.new("Can't alias undefined association `#{attribute_name}` on #{self}") unless reflection
58
+ reflection.class.generate_methods alias_name, generated_associations_methods
59
+ self._association_aliases = _association_aliases.merge(alias_name.to_sym => reflection.name)
60
+ reflection
27
61
  end
28
62
 
29
- def associations
30
- _associations
63
+ def reflect_on_association name
64
+ name = name.to_sym
65
+ _associations[_association_aliases[name] || name]
31
66
  end
32
67
 
33
68
  def association_names
34
69
  _associations.keys
35
70
  end
71
+
72
+ private
73
+
74
+ def attributes_for_inspect
75
+ (_associations.map do |name, reflection|
76
+ "#{name}: #{reflection.inspect}"
77
+ end + [super]).join(', ')
78
+ end
79
+
80
+ def generated_associations_methods
81
+ @generated_associations_methods ||= const_set(:GeneratedAssociationsMethods, Module.new)
82
+ .tap { |proxy| include proxy }
83
+ end
36
84
  end
37
85
 
38
86
  def == other
39
- super(other) && self.class.association_names.all? do |association|
40
- send(association) == other.send(association)
87
+ super && association_names.all? do |association|
88
+ public_send(association) == other.public_send(association)
89
+ end
90
+ end
91
+ alias_method :eql?, :==
92
+
93
+ def association name
94
+ if reflection = self.class.reflect_on_association(name)
95
+ (@_associations ||= {})[reflection.name] ||= reflection.build_association(self)
96
+ end
97
+ end
98
+
99
+ def apply_association_changes!
100
+ association_names.all? do |name|
101
+ association(name).apply_changes!
41
102
  end
42
103
  end
43
104
 
105
+ private
106
+
107
+ def attributes_for_inspect
108
+ (association_names.map do |name|
109
+ association = association(name)
110
+ "#{name}: #{association.inspect}"
111
+ end + [super]).join(', ')
112
+ end
44
113
  end
45
114
  end
46
- end
115
+ end