active_data 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +13 -0
  3. data/.rubocop.yml +56 -0
  4. data/.rubocop_todo.yml +53 -0
  5. data/.rvmrc +1 -1
  6. data/.travis.yml +15 -2
  7. data/Appraisals +1 -1
  8. data/CHANGELOG.md +31 -0
  9. data/Guardfile +8 -8
  10. data/README.md +256 -0
  11. data/Rakefile +2 -4
  12. data/active_data.gemspec +8 -7
  13. data/gemfiles/rails.4.0.gemfile +1 -1
  14. data/gemfiles/rails.4.1.gemfile +1 -1
  15. data/gemfiles/rails.4.2.gemfile +1 -1
  16. data/gemfiles/rails.5.0.gemfile +1 -1
  17. data/gemfiles/rails.5.1.gemfile +14 -0
  18. data/lib/active_data/active_record/associations.rb +18 -13
  19. data/lib/active_data/active_record/nested_attributes.rb +8 -14
  20. data/lib/active_data/base.rb +13 -0
  21. data/lib/active_data/config.rb +4 -4
  22. data/lib/active_data/errors.rb +29 -13
  23. data/lib/active_data/extensions.rb +22 -21
  24. data/lib/active_data/model/associations/base.rb +22 -6
  25. data/lib/active_data/model/associations/embeds_any.rb +17 -0
  26. data/lib/active_data/model/associations/embeds_many.rb +29 -19
  27. data/lib/active_data/model/associations/embeds_one.rb +30 -26
  28. data/lib/active_data/model/associations/nested_attributes.rb +82 -50
  29. data/lib/active_data/model/associations/persistence_adapters/active_record/referenced_proxy.rb +31 -0
  30. data/lib/active_data/model/associations/persistence_adapters/active_record.rb +66 -0
  31. data/lib/active_data/model/associations/persistence_adapters/base.rb +53 -0
  32. data/lib/active_data/model/associations/references_any.rb +41 -0
  33. data/lib/active_data/model/associations/references_many.rb +51 -37
  34. data/lib/active_data/model/associations/references_one.rb +43 -41
  35. data/lib/active_data/model/associations/reflections/base.rb +19 -29
  36. data/lib/active_data/model/associations/reflections/embeds_any.rb +43 -0
  37. data/lib/active_data/model/associations/reflections/embeds_many.rb +3 -13
  38. data/lib/active_data/model/associations/reflections/embeds_one.rb +5 -37
  39. data/lib/active_data/model/associations/reflections/references_any.rb +62 -0
  40. data/lib/active_data/model/associations/reflections/references_many.rb +7 -7
  41. data/lib/active_data/model/associations/reflections/references_one.rb +9 -7
  42. data/lib/active_data/model/associations/reflections/singular.rb +35 -0
  43. data/lib/active_data/model/associations/validations.rb +2 -27
  44. data/lib/active_data/model/associations.rb +12 -10
  45. data/lib/active_data/model/attributes/attribute.rb +10 -10
  46. data/lib/active_data/model/attributes/base.rb +8 -7
  47. data/lib/active_data/model/attributes/localized.rb +4 -4
  48. data/lib/active_data/model/attributes/reference_many.rb +6 -8
  49. data/lib/active_data/model/attributes/reference_one.rb +17 -9
  50. data/lib/active_data/model/attributes/reflections/attribute.rb +2 -2
  51. data/lib/active_data/model/attributes/reflections/base.rb +8 -11
  52. data/lib/active_data/model/attributes/reflections/localized.rb +2 -2
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +11 -22
  54. data/lib/active_data/model/attributes/reflections/represents.rb +5 -6
  55. data/lib/active_data/model/attributes/represents.rb +6 -5
  56. data/lib/active_data/model/attributes.rb +33 -87
  57. data/lib/active_data/model/callbacks.rb +6 -7
  58. data/lib/active_data/model/conventions.rb +2 -0
  59. data/lib/active_data/model/dirty.rb +4 -4
  60. data/lib/active_data/model/lifecycle.rb +18 -20
  61. data/lib/active_data/model/localization.rb +5 -2
  62. data/lib/active_data/model/persistence.rb +2 -2
  63. data/lib/active_data/model/primary.rb +19 -14
  64. data/lib/active_data/model/representation.rb +81 -0
  65. data/lib/active_data/model/scopes.rb +22 -12
  66. data/lib/active_data/model/validations/associated.rb +3 -2
  67. data/lib/active_data/model/validations/nested.rb +6 -1
  68. data/lib/active_data/model/validations.rb +3 -3
  69. data/lib/active_data/model.rb +2 -1
  70. data/lib/active_data/undefined_class.rb +9 -0
  71. data/lib/active_data/version.rb +1 -1
  72. data/lib/active_data.rb +40 -17
  73. data/spec/lib/active_data/active_record/associations_spec.rb +107 -45
  74. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +1 -2
  75. data/spec/lib/active_data/config_spec.rb +37 -15
  76. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +475 -172
  77. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +353 -96
  78. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +108 -12
  79. data/spec/lib/active_data/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  80. data/spec/lib/active_data/model/associations/references_many_spec.rb +440 -64
  81. data/spec/lib/active_data/model/associations/references_one_spec.rb +347 -36
  82. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +8 -7
  83. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +7 -6
  84. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +81 -33
  85. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +116 -37
  86. data/spec/lib/active_data/model/associations/validations_spec.rb +27 -43
  87. data/spec/lib/active_data/model/associations_spec.rb +34 -25
  88. data/spec/lib/active_data/model/attributes/attribute_spec.rb +26 -23
  89. data/spec/lib/active_data/model/attributes/base_spec.rb +5 -6
  90. data/spec/lib/active_data/model/attributes/collection_spec.rb +7 -8
  91. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +40 -33
  92. data/spec/lib/active_data/model/attributes/localized_spec.rb +27 -28
  93. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +6 -6
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +10 -78
  95. data/spec/lib/active_data/model/attributes_spec.rb +150 -45
  96. data/spec/lib/active_data/model/callbacks_spec.rb +69 -70
  97. data/spec/lib/active_data/model/conventions_spec.rb +0 -1
  98. data/spec/lib/active_data/model/dirty_spec.rb +22 -13
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +49 -23
  100. data/spec/lib/active_data/model/persistence_spec.rb +5 -6
  101. data/spec/lib/active_data/model/representation_spec.rb +126 -0
  102. data/spec/lib/active_data/model/scopes_spec.rb +1 -3
  103. data/spec/lib/active_data/model/typecasting_spec.rb +6 -5
  104. data/spec/lib/active_data/model/validations/associated_spec.rb +26 -18
  105. data/spec/lib/active_data/model/validations/nested_spec.rb +89 -18
  106. data/spec/lib/active_data/model_spec.rb +1 -2
  107. data/spec/lib/active_data_spec.rb +0 -1
  108. data/spec/shared/nested_attribute_examples.rb +332 -0
  109. data/spec/spec_helper.rb +3 -0
  110. data/spec/support/model_helpers.rb +2 -2
  111. data/spec/support/muffle_helper.rb +7 -0
  112. metadata +52 -18
  113. data/lib/active_data/model/associations/collection/referenced.rb +0 -26
  114. data/lib/active_data/model/associations/reflections/reference_reflection.rb +0 -45
  115. data/spec/lib/active_data/model/nested_attributes.rb +0 -202
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Model::Associations::EmbedsMany do
@@ -22,17 +21,12 @@ describe ActiveData::Model::Associations::EmbedsMany do
22
21
  end
23
22
  end
24
23
 
25
- let(:user) { User.new }
24
+ let(:user) { User.new(name: 'User') }
26
25
  let(:association) { user.association(:projects) }
27
26
 
28
27
  let(:existing_user) { User.instantiate name: 'Rick', projects: [{title: 'Genesis'}] }
29
28
  let(:existing_association) { existing_user.association(:projects) }
30
29
 
31
- describe 'user#association' do
32
- specify { expect(association).to be_a described_class }
33
- specify { expect(association).to eq(user.association(:projects)) }
34
- end
35
-
36
30
  context 'performers' do
37
31
  let(:user) { User.new(projects: [Project.new(title: 'Project 1')]) }
38
32
 
@@ -56,7 +50,8 @@ describe ActiveData::Model::Associations::EmbedsMany do
56
50
  user.projects.map(&:save!)
57
51
  expect(user.read_attribute(:projects)).to eq([
58
52
  {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 3'},
59
- {'title' => 'Project 4'}, {'title' => 'Project 5'}])
53
+ {'title' => 'Project 4'}, {'title' => 'Project 5'}
54
+ ])
60
55
  user.projects.map(&:destroy!)
61
56
  expect(user.read_attribute(:projects)).to eq([])
62
57
  user.projects.first(2).map(&:save!)
@@ -64,12 +59,184 @@ describe ActiveData::Model::Associations::EmbedsMany do
64
59
  expect(user.projects.reload.count).to eq(2)
65
60
  p3 = user.projects.create!(title: 'Project 3')
66
61
  expect(user.read_attribute(:projects)).to eq([
67
- {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 3'}])
62
+ {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 3'}
63
+ ])
68
64
  p3.destroy!
69
65
  expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
70
- p4 = user.projects.create(title: 'Project 4')
66
+ user.projects.create!(title: 'Project 4')
71
67
  expect(user.read_attribute(:projects)).to eq([
72
- {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 4'}])
68
+ {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 4'}
69
+ ])
70
+ end
71
+ end
72
+
73
+ context 'callbacks' do
74
+ before do
75
+ User.class_eval do
76
+ embeds_many :projects,
77
+ before_add: ->(object) { callbacks.push([:before_add, object]) },
78
+ after_add: ->(object) { callbacks.push([:after_add, object]) }
79
+
80
+ collection :callbacks, Array
81
+ end
82
+ end
83
+ let(:project1) { Project.new(title: 'Project1') }
84
+ let(:project2) { Project.new(title: 'Project2') }
85
+
86
+ specify do
87
+ expect { association.build(title: 'Project1') }
88
+ .to change { user.callbacks }
89
+ .to([[:before_add, project1], [:after_add, project1]])
90
+ end
91
+
92
+ specify do
93
+ expect do
94
+ association.build(title: 'Project1')
95
+ association.build(title: 'Project2')
96
+ end
97
+ .to change { user.callbacks }
98
+ .to([
99
+ [:before_add, project1], [:after_add, project1],
100
+ [:before_add, project2], [:after_add, project2]
101
+ ])
102
+ end
103
+
104
+ specify do
105
+ expect { association.create(title: 'Project1') }
106
+ .to change { user.callbacks }
107
+ .to([[:before_add, project1], [:after_add, project1]])
108
+ end
109
+
110
+ specify do
111
+ expect do
112
+ association.create(title: 'Project1')
113
+ association.create(title: 'Project2')
114
+ end
115
+ .to change { user.callbacks }
116
+ .to([
117
+ [:before_add, project1], [:after_add, project1],
118
+ [:before_add, project2], [:after_add, project2]
119
+ ])
120
+ end
121
+
122
+ specify do
123
+ expect { association.concat(project1, project2) }
124
+ .to change { user.callbacks }
125
+ .to([
126
+ [:before_add, project1], [:after_add, project1],
127
+ [:before_add, project2], [:after_add, project2]
128
+ ])
129
+ end
130
+
131
+ specify do
132
+ expect do
133
+ association.concat(project1)
134
+ association.concat(project2)
135
+ end
136
+ .to change { user.callbacks }
137
+ .to([
138
+ [:before_add, project1], [:after_add, project1],
139
+ [:before_add, project2], [:after_add, project2]
140
+ ])
141
+ end
142
+
143
+ specify do
144
+ expect { association.writer([project2, project1]) }
145
+ .to change { user.callbacks }
146
+ .to([
147
+ [:before_add, project2], [:after_add, project2],
148
+ [:before_add, project1], [:after_add, project1]
149
+ ])
150
+ end
151
+
152
+ specify do
153
+ expect do
154
+ association.writer([project1])
155
+ association.writer([])
156
+ association.writer([project2])
157
+ end
158
+ .to change { user.callbacks }
159
+ .to([
160
+ [:before_add, project1], [:after_add, project1],
161
+ [:before_add, project2], [:after_add, project2]
162
+ ])
163
+ end
164
+
165
+ context 'default' do
166
+ before do
167
+ User.class_eval do
168
+ embeds_many :projects,
169
+ before_add: ->(owner, object) { owner.callbacks.push([:before_add, object]) },
170
+ after_add: ->(owner, object) { owner.callbacks.push([:after_add, object]) },
171
+ default: -> { {title: 'Project1'} }
172
+
173
+ collection :callbacks, Array
174
+ end
175
+ end
176
+
177
+ specify do
178
+ expect { association.concat(project2) }
179
+ .to change { user.callbacks }
180
+ .to([
181
+ [:before_add, project2], [:after_add, project2],
182
+ [:before_add, project1], [:after_add, project1]
183
+ ])
184
+ end
185
+ end
186
+ end
187
+
188
+ describe 'user#association' do
189
+ specify { expect(association).to be_a described_class }
190
+ specify { expect(association).to eq(user.association(:projects)) }
191
+ end
192
+
193
+ describe 'project#embedder' do
194
+ let(:project) { Project.new(title: 'Project') }
195
+
196
+ specify { expect(association.build.embedder).to eq(user) }
197
+ specify { expect(association.create.embedder).to eq(user) }
198
+ specify do
199
+ expect { association.writer([project]) }
200
+ .to change { project.embedder }.from(nil).to(user)
201
+ end
202
+ specify do
203
+ expect { association.concat(project) }
204
+ .to change { project.embedder }.from(nil).to(user)
205
+ end
206
+ specify do
207
+ expect { association.target = [project] }
208
+ .to change { project.embedder }.from(nil).to(user)
209
+ end
210
+
211
+ context 'default' do
212
+ before do
213
+ User.class_eval do
214
+ embeds_many :projects, default: -> { {title: 'Project1'} }
215
+ end
216
+ end
217
+
218
+ specify { expect(association.target.first.embedder).to eq(user) }
219
+
220
+ context do
221
+ before do
222
+ User.class_eval do
223
+ embeds_many :projects, default: -> { Project.new(title: 'Project1') }
224
+ end
225
+ end
226
+
227
+ specify { expect(association.target.first.embedder).to eq(user) }
228
+ end
229
+ end
230
+
231
+ context 'embedding goes before attributes' do
232
+ before do
233
+ Project.class_eval do
234
+ attribute :title, String, normalize: ->(value) { "#{value}#{embedder.name}" }
235
+ end
236
+ end
237
+
238
+ specify { expect(association.build(title: 'Project').title).to eq('ProjectUser') }
239
+ specify { expect(association.create(title: 'Project').title).to eq('ProjectUser') }
73
240
  end
74
241
  end
75
242
 
@@ -77,17 +244,25 @@ describe ActiveData::Model::Associations::EmbedsMany do
77
244
  specify { expect(association.build).to be_a Project }
78
245
  specify { expect(association.build).not_to be_persisted }
79
246
 
80
- specify { expect { association.build(title: 'Swordfish') }
81
- .not_to change { user.read_attribute(:projects) } }
82
- specify { expect { association.build(title: 'Swordfish') }
83
- .to change { association.reader.map(&:attributes) }
84
- .from([]).to([{'title' => 'Swordfish'}]) }
85
-
86
- specify { expect { existing_association.build(title: 'Swordfish') }
87
- .not_to change { existing_user.read_attribute(:projects) } }
88
- specify { expect { existing_association.build(title: 'Swordfish') }
89
- .to change { existing_association.reader.map(&:attributes) }
90
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}]) }
247
+ specify do
248
+ expect { association.build(title: 'Swordfish') }
249
+ .not_to change { user.read_attribute(:projects) }
250
+ end
251
+ specify do
252
+ expect { association.build(title: 'Swordfish') }
253
+ .to change { association.reader.map(&:attributes) }
254
+ .from([]).to([{'title' => 'Swordfish'}])
255
+ end
256
+
257
+ specify do
258
+ expect { existing_association.build(title: 'Swordfish') }
259
+ .not_to change { existing_user.read_attribute(:projects) }
260
+ end
261
+ specify do
262
+ expect { existing_association.build(title: 'Swordfish') }
263
+ .to change { existing_association.reader.map(&:attributes) }
264
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}])
265
+ end
91
266
  end
92
267
 
93
268
  describe '#create' do
@@ -97,22 +272,35 @@ describe ActiveData::Model::Associations::EmbedsMany do
97
272
  specify { expect(association.create(title: 'Swordfish')).to be_a Project }
98
273
  specify { expect(association.create(title: 'Swordfish')).to be_persisted }
99
274
 
100
- specify { expect { association.create }
101
- .not_to change { user.read_attribute(:projects) } }
102
- specify { expect { association.create(title: 'Swordfish') }
103
- .to change { user.read_attribute(:projects) }.from(nil).to([{'title' => 'Swordfish'}]) }
104
- specify { expect { association.create(title: 'Swordfish') }
105
- .to change { association.reader.map(&:attributes) }
106
- .from([]).to([{'title' => 'Swordfish'}]) }
107
-
108
- specify { expect { existing_association.create }
109
- .not_to change { existing_user.read_attribute(:projects) } }
110
- specify { expect { existing_association.create(title: 'Swordfish') }
111
- .to change { existing_user.read_attribute(:projects) }
112
- .from([{title: 'Genesis'}]).to([{title: 'Genesis'}, {'title' => 'Swordfish'}]) }
113
- specify { expect { existing_association.create(title: 'Swordfish') }
114
- .to change { existing_association.reader.map(&:attributes) }
115
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}]) }
275
+ specify do
276
+ expect { association.create }
277
+ .not_to change { user.read_attribute(:projects) }
278
+ end
279
+ specify do
280
+ expect { association.create(title: 'Swordfish') }
281
+ .to change { user.read_attribute(:projects) }
282
+ .from(nil).to([{'title' => 'Swordfish'}])
283
+ end
284
+ specify do
285
+ expect { association.create(title: 'Swordfish') }
286
+ .to change { association.reader.map(&:attributes) }
287
+ .from([]).to([{'title' => 'Swordfish'}])
288
+ end
289
+
290
+ specify do
291
+ expect { existing_association.create }
292
+ .not_to change { existing_user.read_attribute(:projects) }
293
+ end
294
+ specify do
295
+ expect { existing_association.create(title: 'Swordfish') }
296
+ .to change { existing_user.read_attribute(:projects) }
297
+ .from([{title: 'Genesis'}]).to([{title: 'Genesis'}, {'title' => 'Swordfish'}])
298
+ end
299
+ specify do
300
+ expect { existing_association.create(title: 'Swordfish') }
301
+ .to change { existing_association.reader.map(&:attributes) }
302
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}])
303
+ end
116
304
  end
117
305
 
118
306
  describe '#create!' do
@@ -121,77 +309,128 @@ describe ActiveData::Model::Associations::EmbedsMany do
121
309
  specify { expect(association.create!(title: 'Swordfish')).to be_a Project }
122
310
  specify { expect(association.create!(title: 'Swordfish')).to be_persisted }
123
311
 
124
- specify { expect { association.create! rescue nil }
125
- .not_to change { user.read_attribute(:projects) } }
126
- specify { expect { association.create! rescue nil }
127
- .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => nil}]) }
128
- specify { expect { association.create!(title: 'Swordfish') }
129
- .to change { user.read_attribute(:projects) }.from(nil).to([{'title' => 'Swordfish'}]) }
130
- specify { expect { association.create!(title: 'Swordfish') }
131
- .to change { association.reader.map(&:attributes) }
132
- .from([]).to([{'title' => 'Swordfish'}]) }
133
-
134
- specify { expect { existing_association.create! rescue nil }
135
- .not_to change { existing_user.read_attribute(:projects) } }
136
- specify { expect { existing_association.create! rescue nil }
137
- .to change { existing_association.reader.map(&:attributes) }
138
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => nil}]) }
139
- specify { expect { existing_association.create!(title: 'Swordfish') }
140
- .to change { existing_user.read_attribute(:projects) }
141
- .from([{title: 'Genesis'}]).to([{title: 'Genesis'}, {'title' => 'Swordfish'}]) }
142
- specify { expect { existing_association.create!(title: 'Swordfish') }
143
- .to change { existing_association.reader.map(&:attributes) }
144
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}]) }
312
+ specify do
313
+ expect { muffle(ActiveData::ValidationError) { association.create! } }
314
+ .not_to change { user.read_attribute(:projects) }
315
+ end
316
+ specify do
317
+ expect { muffle(ActiveData::ValidationError) { association.create! } }
318
+ .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => nil}])
319
+ end
320
+ specify do
321
+ expect { association.create!(title: 'Swordfish') }
322
+ .to change { user.read_attribute(:projects) }.from(nil).to([{'title' => 'Swordfish'}])
323
+ end
324
+ specify do
325
+ expect { association.create!(title: 'Swordfish') }
326
+ .to change { association.reader.map(&:attributes) }
327
+ .from([]).to([{'title' => 'Swordfish'}])
328
+ end
329
+
330
+ specify do
331
+ expect { muffle(ActiveData::ValidationError) { existing_association.create! } }
332
+ .not_to change { existing_user.read_attribute(:projects) }
333
+ end
334
+ specify do
335
+ expect { muffle(ActiveData::ValidationError) { existing_association.create! } }
336
+ .to change { existing_association.reader.map(&:attributes) }
337
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => nil}])
338
+ end
339
+ specify do
340
+ expect { existing_association.create!(title: 'Swordfish') }
341
+ .to change { existing_user.read_attribute(:projects) }
342
+ .from([{title: 'Genesis'}]).to([{title: 'Genesis'}, {'title' => 'Swordfish'}])
343
+ end
344
+ specify do
345
+ expect { existing_association.create!(title: 'Swordfish') }
346
+ .to change { existing_association.reader.map(&:attributes) }
347
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}])
348
+ end
145
349
  end
146
350
 
147
351
  describe '#apply_changes' do
148
- specify { expect { association.build; association.apply_changes }.to change { association.target.map(&:persisted?) }.to([false]) }
149
- specify { expect { association.build(title: 'Genesis'); association.apply_changes }.to change { association.target.map(&:persisted?) }.to([true]) }
150
- specify { expect {
352
+ specify do
353
+ association.build
354
+ expect { association.apply_changes }
355
+ .not_to change { association.target.map(&:persisted?) }
356
+ .from([false])
357
+ end
358
+ specify do
359
+ association.build(title: 'Genesis')
360
+ expect { association.apply_changes }
361
+ .to change { association.target.map(&:persisted?) }
362
+ .from([false]).to([true])
363
+ end
364
+ specify do
151
365
  existing_association.target.first.mark_for_destruction
152
- existing_association.build(title: 'Swordfish');
153
- existing_association.apply_changes
154
- }.to change { existing_association.target.map(&:title) }.to(['Swordfish']) }
155
- specify { expect {
366
+ existing_association.build(title: 'Swordfish')
367
+ expect { existing_association.apply_changes }
368
+ .to change { existing_association.target.map(&:title) }
369
+ .to(['Swordfish'])
370
+ end
371
+ specify do
156
372
  existing_association.target.first.mark_for_destruction
157
- existing_association.build(title: 'Swordfish');
158
- existing_association.apply_changes
159
- }.not_to change { existing_association.target.map(&:persisted?) } }
160
- specify { expect {
373
+ existing_association.build(title: 'Swordfish')
374
+ expect { existing_association.apply_changes }
375
+ .to change { existing_association.target.map(&:persisted?) }
376
+ .from([true, false]).to([true])
377
+ end
378
+ specify do
161
379
  existing_association.target.first.mark_for_destruction
162
- existing_association.build(title: 'Swordfish');
163
- existing_association.apply_changes
164
- }.to change { existing_association.destroyed.map(&:title) }.from([]).to(['Genesis']) }
165
- specify { expect {
380
+ existing_association.build(title: 'Swordfish')
381
+ expect { existing_association.apply_changes }
382
+ .to change { existing_association.destroyed.map(&:title) }
383
+ .from([]).to(['Genesis'])
384
+ end
385
+ specify do
166
386
  existing_association.target.first.destroy!
167
- existing_association.build(title: 'Swordfish');
168
- existing_association.apply_changes
169
- }.to change { existing_association.target.map(&:title) }.to(['Swordfish']) }
170
- specify { expect {
387
+ existing_association.build(title: 'Swordfish')
388
+ expect { existing_association.apply_changes }
389
+ .to change { existing_association.target.map(&:title) }
390
+ .to(['Swordfish'])
391
+ end
392
+ specify do
171
393
  existing_association.target.first.destroy!
172
- existing_association.build(title: 'Swordfish');
173
- existing_association.apply_changes
174
- }.to change { existing_association.destroyed.map(&:title) }.from([]).to(['Genesis']) }
394
+ existing_association.build(title: 'Swordfish')
395
+ expect { existing_association.apply_changes }
396
+ .to change { existing_association.destroyed.map(&:title) }
397
+ .from([]).to(['Genesis'])
398
+ end
175
399
  end
176
400
 
177
401
  describe '#apply_changes!' do
178
- specify { expect { association.build; association.apply_changes! }.to raise_error ActiveData::AssociationChangesNotApplied }
179
- specify { expect { association.build(title: 'Genesis'); association.apply_changes! }.to change { association.target.map(&:persisted?) }.to([true]) }
180
- specify { expect {
402
+ specify do
403
+ association.build
404
+ expect { association.apply_changes! }
405
+ .to raise_error ActiveData::AssociationChangesNotApplied
406
+ end
407
+ specify do
408
+ association.build(title: 'Genesis')
409
+ expect { association.apply_changes! }
410
+ .to change { association.target.map(&:persisted?) }
411
+ .to([true])
412
+ end
413
+ specify do
181
414
  existing_association.target.first.mark_for_destruction
182
- existing_association.build(title: 'Swordfish');
183
- existing_association.apply_changes!
184
- }.to change { existing_association.target.map(&:title) }.to(['Swordfish']) }
185
- specify { expect {
415
+ existing_association.build(title: 'Swordfish')
416
+ expect { existing_association.apply_changes! }
417
+ .to change { existing_association.target.map(&:title) }
418
+ .to(['Swordfish'])
419
+ end
420
+ specify do
186
421
  existing_association.target.first.mark_for_destruction
187
- existing_association.build(title: 'Swordfish');
188
- existing_association.apply_changes!
189
- }.not_to change { existing_association.target.map(&:persisted?) } }
190
- specify { expect {
422
+ existing_association.build(title: 'Swordfish')
423
+ expect { existing_association.apply_changes! }
424
+ .to change { existing_association.target.map(&:persisted?) }
425
+ .from([true, false]).to([true])
426
+ end
427
+ specify do
191
428
  existing_association.target.first.destroy!
192
- existing_association.build(title: 'Swordfish');
193
- existing_association.apply_changes!
194
- }.to change { existing_association.target.map(&:title) }.to(['Swordfish']) }
429
+ existing_association.build(title: 'Swordfish')
430
+ expect { existing_association.apply_changes! }
431
+ .to change { existing_association.target.map(&:title) }
432
+ .to(['Swordfish'])
433
+ end
195
434
  end
196
435
 
197
436
  describe '#target' do
@@ -201,7 +440,7 @@ describe ActiveData::Model::Associations::EmbedsMany do
201
440
  end
202
441
 
203
442
  describe '#default' do
204
- before { User.embeds_many :projects, default: -> { { title: 'Default' } } }
443
+ before { User.embeds_many :projects, default: -> { {title: 'Default'} } }
205
444
  before do
206
445
  Project.class_eval do
207
446
  include ActiveData::Model::Primary
@@ -241,15 +480,19 @@ describe ActiveData::Model::Associations::EmbedsMany do
241
480
 
242
481
  context do
243
482
  before { association.build(title: 'Swordfish') }
244
- specify { expect { association.reload }
245
- .to change { association.reader.map(&:attributes) }.from([{'title' => 'Swordfish'}]).to([]) }
483
+ specify do
484
+ expect { association.reload }
485
+ .to change { association.reader.map(&:attributes) }.from([{'title' => 'Swordfish'}]).to([])
486
+ end
246
487
  end
247
488
 
248
489
  context do
249
490
  before { existing_association.build(title: 'Swordfish') }
250
- specify { expect { existing_association.reload }
251
- .to change { existing_association.reader.map(&:attributes) }
252
- .from([{'title' => 'Genesis'}, {'title' => 'Swordfish'}]).to([{'title' => 'Genesis'}]) }
491
+ specify do
492
+ expect { existing_association.reload }
493
+ .to change { existing_association.reader.map(&:attributes) }
494
+ .from([{'title' => 'Genesis'}, {'title' => 'Swordfish'}]).to([{'title' => 'Genesis'}])
495
+ end
253
496
  end
254
497
  end
255
498
 
@@ -258,10 +501,14 @@ describe ActiveData::Model::Associations::EmbedsMany do
258
501
  specify { expect { association.clear }.not_to change { association.reader } }
259
502
 
260
503
  specify { expect(existing_association.clear).to eq(true) }
261
- specify { expect { existing_association.clear }
262
- .to change { existing_association.reader.map(&:attributes) }.from([{'title' => 'Genesis'}]).to([]) }
263
- specify { expect { existing_association.clear }
264
- .to change { existing_user.read_attribute(:projects) }.from([{title: 'Genesis'}]).to([]) }
504
+ specify do
505
+ expect { existing_association.clear }
506
+ .to change { existing_association.reader.map(&:attributes) }.from([{'title' => 'Genesis'}]).to([])
507
+ end
508
+ specify do
509
+ expect { existing_association.clear }
510
+ .to change { existing_user.read_attribute(:projects) }.from([{title: 'Genesis'}]).to([])
511
+ end
265
512
 
266
513
  context do
267
514
  let(:existing_user) { User.instantiate name: 'Rick', projects: [{title: 'Genesis'}, {title: 'Swordfish'}] }
@@ -273,10 +520,14 @@ describe ActiveData::Model::Associations::EmbedsMany do
273
520
  end
274
521
 
275
522
  specify { expect(existing_association.clear).to eq(false) }
276
- specify { expect { existing_association.clear }
277
- .not_to change { existing_association.reader } }
278
- specify { expect { existing_association.clear }
279
- .not_to change { existing_user.read_attribute(:projects) } }
523
+ specify do
524
+ expect { existing_association.clear }
525
+ .not_to change { existing_association.reader }
526
+ end
527
+ specify do
528
+ expect { existing_association.clear }
529
+ .not_to change { existing_user.read_attribute(:projects) }
530
+ end
280
531
  end
281
532
  end
282
533
 
@@ -308,52 +559,82 @@ describe ActiveData::Model::Associations::EmbedsMany do
308
559
  let(:new_project2) { Project.new(title: 'Project 2') }
309
560
  let(:invalid_project) { Project.new }
310
561
 
311
- specify { expect { association.writer([Dummy.new]) }
312
- .to raise_error ActiveData::AssociationTypeMismatch }
562
+ specify do
563
+ expect { association.writer([Dummy.new]) }
564
+ .to raise_error ActiveData::AssociationTypeMismatch
565
+ end
313
566
 
314
567
  specify { expect { association.writer(nil) }.to raise_error NoMethodError }
315
568
  specify { expect { association.writer(new_project1) }.to raise_error NoMethodError }
316
569
  specify { expect(association.writer([])).to eq([]) }
317
570
 
318
571
  specify { expect(association.writer([new_project1])).to eq([new_project1]) }
319
- specify { expect { association.writer([new_project1]) }
320
- .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}]) }
321
- specify { expect { association.writer([new_project1]) }
322
- .not_to change { user.read_attribute(:projects) } }
323
-
324
- specify { expect { existing_association.writer([new_project1, invalid_project]) }
325
- .to raise_error ActiveData::AssociationChangesNotApplied }
326
- specify { expect { existing_association.writer([new_project1, invalid_project]) rescue nil }
327
- .not_to change { existing_user.read_attribute(:projects) } }
328
- specify { expect { existing_association.writer([new_project1, invalid_project]) rescue nil }
329
- .not_to change { existing_association.reader } }
330
-
331
- specify { expect { existing_association.writer([new_project1, Dummy.new, new_project2]) }
332
- .to raise_error ActiveData::AssociationTypeMismatch }
333
- specify { expect { existing_association.writer([new_project1, Dummy.new, new_project2]) rescue nil }
334
- .not_to change { existing_user.read_attribute(:projects) } }
335
- specify { expect { existing_association.writer([new_project1, Dummy.new, new_project2]) rescue nil }
336
- .not_to change { existing_association.reader } }
572
+ specify do
573
+ expect { association.writer([new_project1]) }
574
+ .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}])
575
+ end
576
+ specify do
577
+ expect { association.writer([new_project1]) }
578
+ .not_to change { user.read_attribute(:projects) }
579
+ end
580
+
581
+ specify do
582
+ expect { existing_association.writer([new_project1, invalid_project]) }
583
+ .to raise_error ActiveData::AssociationChangesNotApplied
584
+ end
585
+ specify do
586
+ expect { muffle(ActiveData::AssociationChangesNotApplied) { existing_association.writer([new_project1, invalid_project]) } }
587
+ .not_to change { existing_user.read_attribute(:projects) }
588
+ end
589
+ specify do
590
+ expect { muffle(ActiveData::AssociationChangesNotApplied) { existing_association.writer([new_project1, invalid_project]) } }
591
+ .not_to change { existing_association.reader }
592
+ end
593
+
594
+ specify do
595
+ expect { existing_association.writer([new_project1, Dummy.new, new_project2]) }
596
+ .to raise_error ActiveData::AssociationTypeMismatch
597
+ end
598
+ specify do
599
+ expect { muffle(ActiveData::AssociationTypeMismatch) { existing_association.writer([new_project1, Dummy.new, new_project2]) } }
600
+ .not_to change { existing_user.read_attribute(:projects) }
601
+ end
602
+ specify do
603
+ expect { muffle(ActiveData::AssociationTypeMismatch) { existing_association.writer([new_project1, Dummy.new, new_project2]) } }
604
+ .not_to change { existing_association.reader }
605
+ end
337
606
 
338
607
  specify { expect { existing_association.writer(nil) }.to raise_error NoMethodError }
339
- specify { expect { existing_association.writer(nil) rescue nil }
340
- .not_to change { existing_user.read_attribute(:projects) } }
341
- specify { expect { existing_association.writer(nil) rescue nil }
342
- .not_to change { existing_association.reader } }
608
+ specify do
609
+ expect { muffle(NoMethodError) { existing_association.writer(nil) } }
610
+ .not_to change { existing_user.read_attribute(:projects) }
611
+ end
612
+ specify do
613
+ expect { muffle(NoMethodError) { existing_association.writer(nil) } }
614
+ .not_to change { existing_association.reader }
615
+ end
343
616
 
344
617
  specify { expect(existing_association.writer([])).to eq([]) }
345
- specify { expect { existing_association.writer([]) }
346
- .to change { existing_user.read_attribute(:projects) }.to([]) }
347
- specify { expect { existing_association.writer([]) }
348
- .to change { existing_association.reader }.to([]) }
618
+ specify do
619
+ expect { existing_association.writer([]) }
620
+ .to change { existing_user.read_attribute(:projects) }.to([])
621
+ end
622
+ specify do
623
+ expect { existing_association.writer([]) }
624
+ .to change { existing_association.reader }.to([])
625
+ end
349
626
 
350
627
  specify { expect(existing_association.writer([new_project1, new_project2])).to eq([new_project1, new_project2]) }
351
- specify { expect { existing_association.writer([new_project1, new_project2]) }
352
- .to change { existing_association.reader.map(&:attributes) }
353
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}]) }
354
- specify { expect { existing_association.writer([new_project1, new_project2]) }
355
- .to change { existing_user.read_attribute(:projects) }
356
- .from([{title: 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}]) }
628
+ specify do
629
+ expect { existing_association.writer([new_project1, new_project2]) }
630
+ .to change { existing_association.reader.map(&:attributes) }
631
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
632
+ end
633
+ specify do
634
+ expect { existing_association.writer([new_project1, new_project2]) }
635
+ .to change { existing_user.read_attribute(:projects) }
636
+ .from([{title: 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
637
+ end
357
638
  end
358
639
 
359
640
  describe '#concat' do
@@ -361,8 +642,10 @@ describe ActiveData::Model::Associations::EmbedsMany do
361
642
  let(:new_project2) { Project.new(title: 'Project 2') }
362
643
  let(:invalid_project) { Project.new }
363
644
 
364
- specify { expect { association.concat(Dummy.new) }
365
- .to raise_error ActiveData::AssociationTypeMismatch }
645
+ specify do
646
+ expect { association.concat(Dummy.new) }
647
+ .to raise_error ActiveData::AssociationTypeMismatch
648
+ end
366
649
 
367
650
  specify { expect { association.concat(nil) }.to raise_error ActiveData::AssociationTypeMismatch }
368
651
  specify { expect(association.concat([])).to eq([]) }
@@ -370,34 +653,54 @@ describe ActiveData::Model::Associations::EmbedsMany do
370
653
  specify { expect(existing_association.concat).to eq(existing_user.projects) }
371
654
 
372
655
  specify { expect(association.concat(new_project1)).to eq([new_project1]) }
373
- specify { expect { association.concat(new_project1) }
374
- .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}]) }
375
- specify { expect { association.concat(new_project1) }
376
- .not_to change { user.read_attribute(:projects) } }
656
+ specify do
657
+ expect { association.concat(new_project1) }
658
+ .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}])
659
+ end
660
+ specify do
661
+ expect { association.concat(new_project1) }
662
+ .not_to change { user.read_attribute(:projects) }
663
+ end
377
664
 
378
665
  specify { expect(existing_association.concat(new_project1, invalid_project)).to eq(false) }
379
- specify { expect { existing_association.concat(new_project1, invalid_project) }
380
- .to change { existing_user.read_attribute(:projects) }
381
- .from([{title: 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}]) }
382
- specify { expect { existing_association.concat(new_project1, invalid_project) }
383
- .to change { existing_association.reader.map(&:attributes) }
384
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => nil}]) }
385
-
386
- specify { expect { existing_association.concat(new_project1, Dummy.new, new_project2) }
387
- .to raise_error ActiveData::AssociationTypeMismatch }
388
- specify { expect { existing_association.concat(new_project1, Dummy.new, new_project2) rescue nil }
389
- .not_to change { existing_user.read_attribute(:projects) } }
390
- specify { expect { existing_association.concat(new_project1, Dummy.new, new_project2) rescue nil }
391
- .to change { existing_association.reader.map(&:attributes) }
392
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}]) }
393
-
394
- specify { expect(existing_association.concat(new_project1, new_project2))
395
- .to eq([existing_user.projects.first, new_project1, new_project2]) }
396
- specify { expect { existing_association.concat([new_project1, new_project2]) }
397
- .to change { existing_association.reader.map(&:attributes) }
398
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}]) }
399
- specify { expect { existing_association.concat([new_project1, new_project2]) }
400
- .to change { existing_user.read_attribute(:projects) }
401
- .from([{title: 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}]) }
666
+ specify do
667
+ expect { existing_association.concat(new_project1, invalid_project) }
668
+ .to change { existing_user.read_attribute(:projects) }
669
+ .from([{title: 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}])
670
+ end
671
+ specify do
672
+ expect { existing_association.concat(new_project1, invalid_project) }
673
+ .to change { existing_association.reader.map(&:attributes) }
674
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => nil}])
675
+ end
676
+
677
+ specify do
678
+ expect { existing_association.concat(new_project1, Dummy.new, new_project2) }
679
+ .to raise_error ActiveData::AssociationTypeMismatch
680
+ end
681
+ specify do
682
+ expect { muffle(ActiveData::AssociationTypeMismatch) { existing_association.concat(new_project1, Dummy.new, new_project2) } }
683
+ .not_to change { existing_user.read_attribute(:projects) }
684
+ end
685
+ specify do
686
+ expect { muffle(ActiveData::AssociationTypeMismatch) { existing_association.concat(new_project1, Dummy.new, new_project2) } }
687
+ .to change { existing_association.reader.map(&:attributes) }
688
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}])
689
+ end
690
+
691
+ specify do
692
+ expect(existing_association.concat(new_project1, new_project2))
693
+ .to eq([existing_user.projects.first, new_project1, new_project2])
694
+ end
695
+ specify do
696
+ expect { existing_association.concat([new_project1, new_project2]) }
697
+ .to change { existing_association.reader.map(&:attributes) }
698
+ .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}])
699
+ end
700
+ specify do
701
+ expect { existing_association.concat([new_project1, new_project2]) }
702
+ .to change { existing_user.read_attribute(:projects) }
703
+ .from([{title: 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}])
704
+ end
402
705
  end
403
706
  end