active_data 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +13 -0
  3. data/.rubocop.yml +56 -0
  4. data/.rubocop_todo.yml +53 -0
  5. data/.rvmrc +1 -1
  6. data/.travis.yml +15 -2
  7. data/Appraisals +1 -1
  8. data/CHANGELOG.md +31 -0
  9. data/Guardfile +8 -8
  10. data/README.md +256 -0
  11. data/Rakefile +2 -4
  12. data/active_data.gemspec +8 -7
  13. data/gemfiles/rails.4.0.gemfile +1 -1
  14. data/gemfiles/rails.4.1.gemfile +1 -1
  15. data/gemfiles/rails.4.2.gemfile +1 -1
  16. data/gemfiles/rails.5.0.gemfile +1 -1
  17. data/gemfiles/rails.5.1.gemfile +14 -0
  18. data/lib/active_data/active_record/associations.rb +18 -13
  19. data/lib/active_data/active_record/nested_attributes.rb +8 -14
  20. data/lib/active_data/base.rb +13 -0
  21. data/lib/active_data/config.rb +4 -4
  22. data/lib/active_data/errors.rb +29 -13
  23. data/lib/active_data/extensions.rb +22 -21
  24. data/lib/active_data/model/associations/base.rb +22 -6
  25. data/lib/active_data/model/associations/embeds_any.rb +17 -0
  26. data/lib/active_data/model/associations/embeds_many.rb +29 -19
  27. data/lib/active_data/model/associations/embeds_one.rb +30 -26
  28. data/lib/active_data/model/associations/nested_attributes.rb +82 -50
  29. data/lib/active_data/model/associations/persistence_adapters/active_record/referenced_proxy.rb +31 -0
  30. data/lib/active_data/model/associations/persistence_adapters/active_record.rb +66 -0
  31. data/lib/active_data/model/associations/persistence_adapters/base.rb +53 -0
  32. data/lib/active_data/model/associations/references_any.rb +41 -0
  33. data/lib/active_data/model/associations/references_many.rb +51 -37
  34. data/lib/active_data/model/associations/references_one.rb +43 -41
  35. data/lib/active_data/model/associations/reflections/base.rb +19 -29
  36. data/lib/active_data/model/associations/reflections/embeds_any.rb +43 -0
  37. data/lib/active_data/model/associations/reflections/embeds_many.rb +3 -13
  38. data/lib/active_data/model/associations/reflections/embeds_one.rb +5 -37
  39. data/lib/active_data/model/associations/reflections/references_any.rb +62 -0
  40. data/lib/active_data/model/associations/reflections/references_many.rb +7 -7
  41. data/lib/active_data/model/associations/reflections/references_one.rb +9 -7
  42. data/lib/active_data/model/associations/reflections/singular.rb +35 -0
  43. data/lib/active_data/model/associations/validations.rb +2 -27
  44. data/lib/active_data/model/associations.rb +12 -10
  45. data/lib/active_data/model/attributes/attribute.rb +10 -10
  46. data/lib/active_data/model/attributes/base.rb +8 -7
  47. data/lib/active_data/model/attributes/localized.rb +4 -4
  48. data/lib/active_data/model/attributes/reference_many.rb +6 -8
  49. data/lib/active_data/model/attributes/reference_one.rb +17 -9
  50. data/lib/active_data/model/attributes/reflections/attribute.rb +2 -2
  51. data/lib/active_data/model/attributes/reflections/base.rb +8 -11
  52. data/lib/active_data/model/attributes/reflections/localized.rb +2 -2
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +11 -22
  54. data/lib/active_data/model/attributes/reflections/represents.rb +5 -6
  55. data/lib/active_data/model/attributes/represents.rb +6 -5
  56. data/lib/active_data/model/attributes.rb +33 -87
  57. data/lib/active_data/model/callbacks.rb +6 -7
  58. data/lib/active_data/model/conventions.rb +2 -0
  59. data/lib/active_data/model/dirty.rb +4 -4
  60. data/lib/active_data/model/lifecycle.rb +18 -20
  61. data/lib/active_data/model/localization.rb +5 -2
  62. data/lib/active_data/model/persistence.rb +2 -2
  63. data/lib/active_data/model/primary.rb +19 -14
  64. data/lib/active_data/model/representation.rb +81 -0
  65. data/lib/active_data/model/scopes.rb +22 -12
  66. data/lib/active_data/model/validations/associated.rb +3 -2
  67. data/lib/active_data/model/validations/nested.rb +6 -1
  68. data/lib/active_data/model/validations.rb +3 -3
  69. data/lib/active_data/model.rb +2 -1
  70. data/lib/active_data/undefined_class.rb +9 -0
  71. data/lib/active_data/version.rb +1 -1
  72. data/lib/active_data.rb +40 -17
  73. data/spec/lib/active_data/active_record/associations_spec.rb +107 -45
  74. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +1 -2
  75. data/spec/lib/active_data/config_spec.rb +37 -15
  76. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +475 -172
  77. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +353 -96
  78. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +108 -12
  79. data/spec/lib/active_data/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  80. data/spec/lib/active_data/model/associations/references_many_spec.rb +440 -64
  81. data/spec/lib/active_data/model/associations/references_one_spec.rb +347 -36
  82. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +8 -7
  83. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +7 -6
  84. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +81 -33
  85. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +116 -37
  86. data/spec/lib/active_data/model/associations/validations_spec.rb +27 -43
  87. data/spec/lib/active_data/model/associations_spec.rb +34 -25
  88. data/spec/lib/active_data/model/attributes/attribute_spec.rb +26 -23
  89. data/spec/lib/active_data/model/attributes/base_spec.rb +5 -6
  90. data/spec/lib/active_data/model/attributes/collection_spec.rb +7 -8
  91. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +40 -33
  92. data/spec/lib/active_data/model/attributes/localized_spec.rb +27 -28
  93. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +6 -6
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +10 -78
  95. data/spec/lib/active_data/model/attributes_spec.rb +150 -45
  96. data/spec/lib/active_data/model/callbacks_spec.rb +69 -70
  97. data/spec/lib/active_data/model/conventions_spec.rb +0 -1
  98. data/spec/lib/active_data/model/dirty_spec.rb +22 -13
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +49 -23
  100. data/spec/lib/active_data/model/persistence_spec.rb +5 -6
  101. data/spec/lib/active_data/model/representation_spec.rb +126 -0
  102. data/spec/lib/active_data/model/scopes_spec.rb +1 -3
  103. data/spec/lib/active_data/model/typecasting_spec.rb +6 -5
  104. data/spec/lib/active_data/model/validations/associated_spec.rb +26 -18
  105. data/spec/lib/active_data/model/validations/nested_spec.rb +89 -18
  106. data/spec/lib/active_data/model_spec.rb +1 -2
  107. data/spec/lib/active_data_spec.rb +0 -1
  108. data/spec/shared/nested_attribute_examples.rb +332 -0
  109. data/spec/spec_helper.rb +3 -0
  110. data/spec/support/model_helpers.rb +2 -2
  111. data/spec/support/muffle_helper.rb +7 -0
  112. metadata +52 -18
  113. data/lib/active_data/model/associations/collection/referenced.rb +0 -26
  114. data/lib/active_data/model/associations/reflections/reference_reflection.rb +0 -45
  115. data/spec/lib/active_data/model/nested_attributes.rb +0 -202
@@ -1,45 +0,0 @@
1
- module ActiveData
2
- module Model
3
- module Associations
4
- module Reflections
5
- class ReferenceReflection < Base
6
- def self.build target, generated_methods, name, *args, &block
7
- reflection = new(name, *args)
8
- generate_methods name, generated_methods
9
- reflection
10
- end
11
-
12
- def initialize name, *args
13
- @options = args.extract_options!
14
- @scope_proc = args.first
15
- @name = name.to_sym
16
- end
17
-
18
- def read_source object
19
- object.read_attribute(reference_key)
20
- end
21
-
22
- def write_source object, value
23
- object.write_attribute(reference_key, value)
24
- end
25
-
26
- def primary_key
27
- @primary_key ||= options[:primary_key].presence.try(:to_sym) || :id
28
- end
29
-
30
- def scope
31
- @scope ||= begin
32
- scope = klass.unscoped
33
- scope = scope.instance_exec(&@scope_proc) if @scope_proc
34
- scope
35
- end
36
- end
37
-
38
- def embedded?
39
- false
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,202 +0,0 @@
1
- require 'spec_helper'
2
-
3
- shared_examples 'nested attributes' do
4
- before do
5
- stub_model :project do
6
- include ActiveData::Model::Primary
7
- include ActiveData::Model::Lifecycle
8
-
9
- primary :slug, String
10
- attribute :title, String
11
- end
12
-
13
- stub_model :profile do
14
- include ActiveData::Model::Primary
15
- include ActiveData::Model::Lifecycle
16
-
17
- primary :identifier
18
- attribute :first_name, String
19
- end
20
- end
21
-
22
- context 'embeds_one' do
23
- let(:user) { User.new }
24
-
25
- specify { expect { user.profile_attributes = {} }.to change { user.profile }.to(an_instance_of(Profile)) }
26
- specify { expect { user.profile_attributes = {first_name: 'User'} }.to change { user.profile.try(:first_name) }.to('User') }
27
- specify { expect { user.profile_attributes = {identifier: 42, first_name: 'User'} }.to raise_error ActiveData::ObjectNotFound }
28
-
29
- context ':reject_if' do
30
- context do
31
- before { User.accepts_nested_attributes_for :profile, reject_if: :all_blank }
32
- specify { expect { user.profile_attributes = {first_name: ''} }.not_to change { user.profile } }
33
- end
34
-
35
- context do
36
- before { User.accepts_nested_attributes_for :profile, reject_if: ->(attributes) { attributes['first_name'].blank? } }
37
- specify { expect { user.profile_attributes = {first_name: ''} }.not_to change { user.profile } }
38
- end
39
- end
40
-
41
- context 'existing' do
42
- let(:profile) { Profile.new(first_name: 'User') }
43
- let(:user) { User.new profile: profile }
44
-
45
- specify { expect { user.profile_attributes = {identifier: 42, first_name: 'User'} }.to raise_error ActiveData::ObjectNotFound }
46
- specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1'} }.to change { user.profile.first_name }.to('User 1') }
47
- specify { expect { user.profile_attributes = {first_name: 'User 1'} }.to change { user.profile.first_name }.to('User 1') }
48
- specify { expect { user.profile_attributes = {first_name: 'User 1', _destroy: '1'} }.not_to change { user.profile.first_name } }
49
- specify { expect { user.profile_attributes = {first_name: 'User 1', _destroy: '1'}; user.save { true } }.not_to change { user.profile.first_name } }
50
- specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'} }.to change { user.profile.first_name }.to('User 1') }
51
- specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'}; user.save { true } }.to change { user.profile.first_name }.to('User 1') }
52
-
53
- context ':allow_destroy' do
54
- before { User.accepts_nested_attributes_for :profile, allow_destroy: true }
55
-
56
- specify { expect { user.profile_attributes = {first_name: 'User 1', _destroy: '1'} }.not_to change { user.profile.first_name } }
57
- specify { expect { user.profile_attributes = {first_name: 'User 1', _destroy: '1'}; user.save { true } }.not_to change { user.profile.first_name } }
58
- specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'} }.to change { user.profile.first_name }.to('User 1') }
59
- specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'}; user.save { true } }.to change { user.profile }.to(nil) }
60
- end
61
-
62
- context ':update_only' do
63
- before { User.accepts_nested_attributes_for :profile, update_only: true }
64
-
65
- specify { expect { user.profile_attributes = {identifier: 42, first_name: 'User 1'} }
66
- .to change { user.profile.first_name }.to('User 1') }
67
- end
68
- end
69
-
70
- context 'not primary' do
71
- before do
72
- stub_model :profile do
73
- include ActiveData::Model::Lifecycle
74
-
75
- attribute :identifier, Integer
76
- attribute :first_name, String
77
- end
78
- end
79
-
80
- specify { expect { user.profile_attributes = {} }.to change { user.profile }.to(an_instance_of(Profile)) }
81
- specify { expect { user.profile_attributes = {first_name: 'User'} }.to change { user.profile.try(:first_name) }.to('User') }
82
-
83
- context do
84
- let(:profile) { Profile.new(first_name: 'User') }
85
- let(:user) { User.new profile: profile }
86
-
87
- specify { expect { user.profile_attributes = {identifier: 42, first_name: 'User 1'} }
88
- .to change { user.profile.first_name }.to('User 1') }
89
- end
90
- end
91
- end
92
-
93
- context 'embeds_many' do
94
- let(:user) { User.new }
95
-
96
- specify { expect { user.projects_attributes = {} }.not_to change { user.projects } }
97
- specify { expect { user.projects_attributes = [{title: 'Project 1'}, {title: 'Project 2'}] }
98
- .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 2']) }
99
- specify { expect { user.projects_attributes = {1 => {title: 'Project 1'}, 2 => {title: 'Project 2'}} }
100
- .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 2']) }
101
- specify { expect { user.projects_attributes = [{slug: 42, title: 'Project 1'}, {title: 'Project 2'}] }
102
- .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 2']) }
103
- specify { expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
104
- .to change { user.projects.map(&:title) }.to(['', 'Project 2']) }
105
-
106
- context ':limit' do
107
- before { User.accepts_nested_attributes_for :projects, limit: 1 }
108
-
109
- specify { expect { user.projects_attributes = [{title: 'Project 1'}] }
110
- .to change { user.projects.map(&:title) }.to(['Project 1']) }
111
- specify { expect { user.projects_attributes = [{title: 'Project 1'}, {title: 'Project 2'}] }
112
- .to raise_error ActiveData::TooManyObjects }
113
- end
114
-
115
- context ':reject_if' do
116
- context do
117
- before { User.accepts_nested_attributes_for :projects, reject_if: :all_blank }
118
- specify { expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
119
- .to change { user.projects.map(&:title) }.to(['Project 2']) }
120
- end
121
-
122
- context do
123
- before { User.accepts_nested_attributes_for :projects, reject_if: ->(attributes) { attributes['title'].blank? } }
124
- specify { expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
125
- .to change { user.projects.map(&:title) }.to(['Project 2']) }
126
- end
127
-
128
- context do
129
- before { User.accepts_nested_attributes_for :projects, reject_if: ->(attributes) { attributes['foobar'].blank? } }
130
- specify { expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
131
- .not_to change { user.projects } }
132
- end
133
- end
134
-
135
- context 'existing' do
136
- let(:projects) { 2.times.map { |i| Project.new(title: "Project #{i.next}").tap { |pr| pr.slug = 42+i } } }
137
- let(:user) { User.new projects: projects }
138
-
139
- specify { expect { user.projects_attributes = [
140
- {slug: projects.first.slug.to_i, title: 'Project 3'},
141
- {title: 'Project 4'}
142
- ] }
143
- .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2', 'Project 4']) }
144
- specify { expect { user.projects_attributes = [
145
- {slug: projects.first.slug.to_i, title: 'Project 3'},
146
- {slug: 33, title: 'Project 4'}
147
- ] }
148
- .to change { user.projects.map(&:slug) }.to(['42', '43', '33']) }
149
- specify { expect { user.projects_attributes = [
150
- {slug: projects.first.slug.to_i, title: 'Project 3'},
151
- {slug: 33, title: 'Project 4', _destroy: 1}
152
- ] }
153
- .not_to change { user.projects.map(&:slug) } }
154
- specify { expect { user.projects_attributes = {
155
- 1 => {slug: projects.first.slug.to_i, title: 'Project 3'},
156
- 2 => {title: 'Project 4'}
157
- } }
158
- .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2', 'Project 4']) }
159
- specify { expect { user.projects_attributes = [
160
- {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
161
- {title: 'Project 4', _destroy: '1'}
162
- ] }
163
- .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2']) }
164
- specify { expect { user.projects_attributes = [
165
- {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
166
- {title: 'Project 4', _destroy: '1'}
167
- ]
168
- user.save { true } }
169
- .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2']) }
170
-
171
- context ':allow_destroy' do
172
- before { User.accepts_nested_attributes_for :projects, allow_destroy: true }
173
-
174
- specify { expect { user.projects_attributes = [
175
- {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
176
- {title: 'Project 4', _destroy: '1'}
177
- ] }
178
- .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2']) }
179
- specify { expect { user.projects_attributes = [
180
- {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
181
- {title: 'Project 4', _destroy: '1'}
182
- ]
183
- user.save { true } }
184
- .to change { user.projects.map(&:title) }.to(['Project 2']) }
185
- end
186
- end
187
-
188
- context 'primary absence causes exception' do
189
- before do
190
- stub_model :project do
191
- include ActiveData::Model::Primary
192
- include ActiveData::Model::Lifecycle
193
-
194
- attribute :slug, String
195
- attribute :title, String
196
- end
197
- end
198
-
199
- specify { expect { user.projects_attributes = {} }.to raise_error ActiveData::UndefinedPrimaryAttribute }
200
- end
201
- end
202
- end