active_data 0.3.0 → 1.0.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 (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