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