amoeba 1.2.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ module Amoeba
2
+ module Macros
3
+ class HasAndBelongsToMany < ::Amoeba::Macros::Base
4
+ def follow(relation_name, _association)
5
+ clone = @cloner.amoeba.clones.include?(relation_name.to_sym)
6
+ @old_object.__send__(relation_name).each do |old_obj|
7
+ fill_relation(relation_name, old_obj, clone)
8
+ end
9
+ end
10
+
11
+ def fill_relation(relation_name, old_obj, clone)
12
+ # associate this new child to the new parent object
13
+ old_obj = old_obj.amoeba_dup if clone
14
+ relation_name = remapped_relation_name(relation_name)
15
+ @new_object.__send__(relation_name) << old_obj
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ module Amoeba
2
+ module Macros
3
+ class HasMany < ::Amoeba::Macros::Base
4
+ def follow(relation_name, association)
5
+ if @cloner.amoeba.clones.include?(relation_name.to_sym)
6
+ follow_with_clone(relation_name)
7
+ else
8
+ follow_without_clone(relation_name, association)
9
+ end
10
+ end
11
+
12
+ def follow_with_clone(relation_name)
13
+ # This is a M:M "has many through" where we
14
+ # actually copy and reassociate the new children
15
+ # rather than only maintaining the associations
16
+ @old_object.__send__(relation_name).each do |old_obj|
17
+ relation_name = remapped_relation_name(relation_name)
18
+ # associate this new child to the new parent object
19
+ @new_object.__send__(relation_name) << old_obj.amoeba_dup
20
+ end
21
+ end
22
+
23
+ def follow_without_clone(relation_name, association)
24
+ # This is a regular 1:M "has many"
25
+ #
26
+ # copying the children of the regular has many will
27
+ # effectively do what is desired anyway, the through
28
+ # association is really just for convenience usage
29
+ # on the model
30
+ return if association.is_a?(ActiveRecord::Reflection::ThroughReflection)
31
+
32
+ @old_object.__send__(relation_name).each do |old_obj|
33
+ copy_of_obj = old_obj.amoeba_dup(@options)
34
+ copy_of_obj[:"#{association.foreign_key}"] = nil
35
+ relation_name = remapped_relation_name(relation_name)
36
+ # associate this new child to the new parent object
37
+ @new_object.__send__(relation_name) << copy_of_obj
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Amoeba
2
+ module Macros
3
+ class HasOne < ::Amoeba::Macros::Base
4
+ def follow(relation_name, association)
5
+ return if association.is_a?(::ActiveRecord::Reflection::ThroughReflection)
6
+ old_obj = @old_object.__send__(relation_name)
7
+ return unless old_obj
8
+ copy_of_obj = old_obj.amoeba_dup(@options)
9
+ copy_of_obj[:"#{association.foreign_key}"] = nil
10
+ relation_name = remapped_relation_name(relation_name)
11
+ @new_object.__send__(:"#{relation_name}=", copy_of_obj)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module Amoeba
2
- VERSION = "1.2.1"
2
+ VERSION = '3.2.0'
3
3
  end
@@ -1,18 +1,23 @@
1
- require 'active_record'
2
1
  require 'spec_helper'
3
2
 
4
- describe "amoeba" do
5
- context "dup" do
6
- it "duplicates associated child records" do
3
+ describe 'amoeba' do
4
+ context 'dup' do
5
+ before :each do
6
+ require ::File.dirname(__FILE__) + '/../support/data.rb'
7
+ end
8
+
9
+ let(:first_product) { Product.find(1) }
10
+
11
+ it 'duplicates associated child records' do
7
12
  # Posts {{{
8
- old_post = Post.find(1)
9
- old_post.comments.map(&:contents).include?("I love it!").should be true
13
+ old_post = ::Post.find(1)
14
+ expect(old_post.comments.map(&:contents).include?('I love it!')).to be_truthy
10
15
 
11
16
  old_post.class.amoeba do
12
- prepend :contents => "Here's a copy: "
17
+ prepend contents: "Here's a copy: "
13
18
  end
14
19
 
15
- new_post = old_post.dup
20
+ new_post = old_post.amoeba_dup
16
21
 
17
22
  start_account_count = Account.all.count
18
23
  start_history_count = History.all.count
@@ -27,18 +32,16 @@ describe "amoeba" do
27
32
  start_postconfig_count = PostConfig.all.count
28
33
  start_postwidget_count = PostWidget.all.count
29
34
  start_superkitten_count = Superkitten.all.count
30
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS tag_count FROM posts_tags')
31
- start_posttag_count = rs["tag_count"]
32
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS note_count FROM notes_posts')
33
- start_postnote_count = rs["note_count"]
34
-
35
- new_post.save
36
- new_post.errors.messages.length.should == 0
37
-
38
- end_account_count = Account.all.count
39
- end_history_count = History.all.count
40
- end_cat_count = Category.all.count
41
- end_supercat_count = Supercat.all.count
35
+ start_posttag_count = Post.tag_count
36
+ start_postnote_count = Post.note_count
37
+
38
+ expect(new_post.save!).to be_truthy
39
+ expect(new_post.title).to eq("Copy of #{old_post.title}")
40
+
41
+ end_account_count = Account.count
42
+ end_history_count = History.count
43
+ end_cat_count = Category.count
44
+ end_supercat_count = Supercat.count
42
45
  end_tag_count = Tag.all.count
43
46
  end_note_count = Note.all.count
44
47
  end_widget_count = Widget.all.count
@@ -48,132 +51,354 @@ describe "amoeba" do
48
51
  end_postconfig_count = PostConfig.all.count
49
52
  end_postwidget_count = PostWidget.all.count
50
53
  end_superkitten_count = Superkitten.all.count
51
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS tag_count FROM posts_tags')
52
- end_posttag_count = rs["tag_count"]
53
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS note_count FROM notes_posts')
54
- end_postnote_count = rs["note_count"]
55
-
56
- end_tag_count.should == start_tag_count
57
- end_cat_count.should == start_cat_count
58
- end_account_count.should == start_account_count * 2
59
- end_history_count.should == start_history_count * 2
60
- end_supercat_count.should == start_supercat_count * 2
61
- end_post_count.should == start_post_count * 2
62
- end_comment_count.should == (start_comment_count * 2) + 2
63
- end_rating_count.should == start_rating_count * 2
64
- end_postconfig_count.should == start_postconfig_count * 2
65
- end_posttag_count.should == start_posttag_count * 2
66
- end_widget_count.should == start_widget_count * 2
67
- end_postwidget_count.should == start_postwidget_count * 2
68
- end_note_count.should == start_note_count * 2
69
- end_postnote_count.should == start_postnote_count * 2
70
- end_superkitten_count.should == start_superkitten_count * 2
71
-
72
- new_post.supercats.map(&:ramblings).include?("Copy of zomg").should be true
73
- new_post.supercats.map(&:other_ramblings).uniq.length.should == 1
74
- new_post.supercats.map(&:other_ramblings).uniq.include?("La la la").should be true
75
- new_post.title.should == "Copy of #{old_post.title}"
76
- new_post.contents.should == "Here's a copy: #{old_post.contents.gsub(/dog/, 'cat')} (copied version)"
77
- new_post.comments.length.should == 5
78
- new_post.comments.select{ |c| c.nerf == 'ratatat' && c.contents.nil? }.length.should == 1
79
- new_post.comments.select{ |c| c.nerf == 'ratatat' }.length.should == 2
80
- new_post.comments.select{ |c| c.nerf == 'bonk' }.length.should == 1
81
- new_post.comments.select{ |c| c.nerf == 'bonkers' && c.contents.nil? }.length.should == 1
54
+ end_posttag_count = Post.tag_count
55
+ end_postnote_count = Post.note_count
56
+
57
+ expect(end_tag_count).to eq(start_tag_count)
58
+ expect(end_cat_count).to eq(start_cat_count)
59
+ expect(end_account_count).to eq(start_account_count * 2)
60
+ expect(end_history_count).to eq(start_history_count * 2)
61
+ expect(end_supercat_count).to eq(start_supercat_count * 2)
62
+ expect(end_post_count).to eq(start_post_count * 2)
63
+ expect(end_comment_count).to eq((start_comment_count * 2) + 2)
64
+ expect(end_rating_count).to eq(start_rating_count * 2)
65
+ expect(end_postconfig_count).to eq(start_postconfig_count * 2)
66
+ expect(end_posttag_count).to eq(start_posttag_count * 2)
67
+ expect(end_widget_count).to eq(start_widget_count * 2)
68
+ expect(end_postwidget_count).to eq(start_postwidget_count * 2)
69
+ expect(end_note_count).to eq(start_note_count * 2)
70
+ expect(end_postnote_count).to eq(start_postnote_count * 2)
71
+ expect(end_superkitten_count).to eq(start_superkitten_count * 2)
72
+
73
+ expect(new_post.supercats.map(&:ramblings)).to include('Copy of zomg')
74
+ expect(new_post.supercats.map(&:other_ramblings).uniq.length).to eq(1)
75
+ expect(new_post.supercats.map(&:other_ramblings).uniq).to include('La la la')
76
+ expect(new_post.contents).to eq("Here's a copy: #{old_post.contents.gsub(/dog/, 'cat')} (copied version)")
77
+ expect(new_post.comments.length).to eq(5)
78
+ expect(new_post.comments.select { |c| c.nerf == 'ratatat' && c.contents.nil? }.length).to eq(1)
79
+ expect(new_post.comments.select { |c| c.nerf == 'ratatat' }.length).to eq(2)
80
+ expect(new_post.comments.select { |c| c.nerf == 'bonk' }.length).to eq(1)
81
+ expect(new_post.comments.select { |c| c.nerf == 'bonkers' && c.contents.nil? }.length).to eq(1)
82
82
 
83
83
  new_post.widgets.map(&:id).each do |id|
84
- old_post.widgets.map(&:id).include?(id).should_not be true
84
+ expect(old_post.widgets.map(&:id)).not_to include(id)
85
85
  end
86
+
87
+ expect(new_post.custom_things.length).to eq(3)
88
+ expect(new_post.custom_things.select { |ct| ct.value == [] }.length).to eq(1)
89
+ expect(new_post.custom_things.select { |ct| ct.value == [1, 2] }.length).to eq(1)
90
+ expect(new_post.custom_things.select { |ct| ct.value == [78] }.length).to eq(1)
86
91
  # }}}
87
92
  # Author {{{
88
93
  old_author = Author.find(1)
89
- new_author = old_author.dup
90
- new_author.save
91
- new_author.errors.messages.length.should == 0
94
+ new_author = old_author.amoeba_dup
95
+ new_author.save!
96
+ expect(new_author.errors.messages).to be_empty
97
+ expect(new_author.posts.first.custom_things.length).to eq(3)
98
+ expect(new_author.posts.first.custom_things.select { |ct| ct.value == [] }.length).to eq(1)
99
+ expect(new_author.posts.first.custom_things.select { |ct| ct.value == [1, 2] }.length).to eq(1)
100
+ expect(new_author.posts.first.custom_things.select { |ct| ct.value == [78] }.length).to eq(1)
92
101
  # }}}
93
102
  # Products {{{
94
103
  # Base Class {{{
95
- old_product = Product.find(1)
96
104
 
97
- start_image_count = Image.where(:product_id => old_product.id).count
105
+ start_image_count = first_product.images.count
98
106
  start_section_count = Section.all.length
99
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', old_product.id)
100
- start_prodsection_count = rs["section_count"]
107
+ start_prodsection_count = first_product.section_count
101
108
 
102
- new_product = old_product.dup
109
+ new_product = first_product.amoeba_dup
103
110
  new_product.save
104
- new_product.errors.messages.length.should == 0
111
+ expect(new_product.errors.messages).to be_empty
105
112
 
106
- end_image_count = Image.where(:product_id => old_product.id).count
107
- end_newimage_count = Image.where(:product_id => new_product.id).count
113
+ end_image_count = first_product.images.count
114
+ end_newimage_count = new_product.images.count
108
115
  end_section_count = Section.all.length
109
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', 1)
110
- end_prodsection_count = rs["section_count"]
111
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', new_product.id)
112
- end_newprodsection_count = rs["section_count"]
113
-
114
- end_image_count.should == start_image_count
115
- end_newimage_count.should == start_image_count
116
- end_section_count.should == start_section_count
117
- end_prodsection_count.should == start_prodsection_count
118
- end_newprodsection_count.should == start_prodsection_count
116
+ end_prodsection_count = first_product.section_count
117
+ end_newprodsection_count = new_product.section_count
118
+
119
+ expect(end_image_count).to eq(start_image_count)
120
+ expect(end_newimage_count).to eq(start_image_count)
121
+ expect(end_section_count).to eq(start_section_count)
122
+ expect(end_prodsection_count).to eq(start_prodsection_count)
123
+ expect(end_newprodsection_count).to eq(start_prodsection_count)
119
124
  # }}}
120
125
 
121
126
  # Inherited Class {{{
122
127
  # Shirt {{{
123
128
  old_product = Shirt.find(2)
124
129
 
125
- start_image_count = Image.where(:product_id => old_product.id).count
126
- start_section_count = Section.all.length
127
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', old_product.id)
128
- start_prodsection_count = rs["section_count"]
130
+ start_image_count = old_product.images.count
131
+ start_section_count = Section.count
132
+ start_prodsection_count = old_product.section_count
129
133
 
130
- new_product = old_product.dup
134
+ new_product = old_product.amoeba_dup
131
135
  new_product.save
132
- new_product.errors.messages.length.should == 0
136
+ expect(new_product.errors.messages).to be_empty
133
137
 
134
- end_image_count = Image.where(:product_id => old_product.id).count
135
- end_newimage_count = Image.where(:product_id => new_product.id).count
136
- end_section_count = Section.all.length
137
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', 1)
138
- end_prodsection_count = rs["section_count"]
139
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', new_product.id)
140
- end_newprodsection_count = rs["section_count"]
141
-
142
- end_image_count.should == start_image_count
143
- end_newimage_count.should == start_image_count
144
- end_section_count.should == start_section_count
145
- end_prodsection_count.should == start_prodsection_count
146
- end_newprodsection_count.should == start_prodsection_count
138
+ end_image_count = old_product.images.count
139
+ end_newimage_count = new_product.images.count
140
+ end_section_count = Section.count
141
+ end_prodsection_count = first_product.section_count
142
+ end_newprodsection_count = new_product.section_count
143
+
144
+ expect(end_image_count).to eq(start_image_count)
145
+ expect(end_newimage_count).to eq(start_image_count)
146
+ expect(end_section_count).to eq(start_section_count)
147
+ expect(end_prodsection_count).to eq(start_prodsection_count)
148
+ expect(end_newprodsection_count).to eq(start_prodsection_count)
147
149
  # }}}
148
150
 
149
151
  # Necklace {{{
150
152
  old_product = Necklace.find(3)
151
153
 
152
- start_image_count = Image.where(:product_id => old_product.id).count
153
- start_section_count = Section.all.length
154
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', old_product.id)
155
- start_prodsection_count = rs["section_count"]
154
+ start_image_count = old_product.images.count
155
+ start_section_count = Section.count
156
+ start_prodsection_count = old_product.section_count
156
157
 
157
- new_product = old_product.dup
158
+ new_product = old_product.amoeba_dup
158
159
  new_product.save
159
- new_product.errors.messages.length.should == 0
160
+ expect(new_product.errors.messages).to be_empty
160
161
 
161
- end_image_count = Image.where(:product_id => old_product.id).count
162
- end_newimage_count = Image.where(:product_id => new_product.id).count
163
- end_section_count = Section.all.length
164
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', 1)
165
- end_prodsection_count = rs["section_count"]
166
- rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', new_product.id)
167
- end_newprodsection_count = rs["section_count"]
168
-
169
- end_image_count.should == start_image_count
170
- end_newimage_count.should == start_image_count
171
- end_section_count.should == start_section_count
172
- end_prodsection_count.should == start_prodsection_count
173
- end_newprodsection_count.should == start_prodsection_count
162
+ end_image_count = old_product.images.count
163
+ end_newimage_count = new_product.images.count
164
+ end_section_count = Section.count
165
+ end_prodsection_count = first_product.section_count
166
+ end_newprodsection_count = new_product.section_count
167
+
168
+ expect(end_image_count).to eq(start_image_count)
169
+ expect(end_newimage_count).to eq(start_image_count)
170
+ expect(end_section_count).to eq(start_section_count)
171
+ expect(end_prodsection_count).to eq(start_prodsection_count)
172
+ expect(end_newprodsection_count).to eq(start_prodsection_count)
174
173
  # }}}
175
174
  # }}}
176
175
  # }}}
177
176
  end
178
177
  end
178
+
179
+ context 'Using a if condition' do
180
+ before(:all) do
181
+ require ::File.dirname(__FILE__) + '/../support/data.rb'
182
+ end
183
+ before { ::Post.fresh_amoeba }
184
+
185
+ subject { post.amoeba_dup.save! }
186
+ let(:post) { Post.first }
187
+
188
+ it 'includes an association with truthy condition' do
189
+ ::Post.amoeba do
190
+ include_association :comments, if: :truthy?
191
+ end
192
+ expect { subject }.to change { Comment.count }.by(3)
193
+ end
194
+
195
+ it 'does not include an association with a falsey condition' do
196
+ ::Post.amoeba do
197
+ include_association :comments, if: :falsey?
198
+ end
199
+ expect { subject }.not_to change { Comment.count }
200
+ end
201
+
202
+ it 'excludes an association with a truthy condition' do
203
+ ::Post.amoeba do
204
+ exclude_association :comments, if: :truthy?
205
+ end
206
+ expect { subject }.not_to change { Comment.count }
207
+ end
208
+
209
+ it 'does not exclude an association with a falsey condition' do
210
+ ::Post.amoeba do
211
+ exclude_association :comments, if: :falsey?
212
+ end
213
+ expect { subject }.to change { Comment.count }.by(3)
214
+ end
215
+
216
+ it 'includes associations from a given array with a truthy condition' do
217
+ ::Post.amoeba do
218
+ include_association [:comments], if: :truthy?
219
+ end
220
+ expect { subject }.to change { Comment.count }.by(3)
221
+ end
222
+
223
+ it 'does not include associations from a given array with a falsey condition' do
224
+ ::Post.amoeba do
225
+ include_association [:comments], if: :falsey?
226
+ end
227
+ expect { subject }.not_to change { Comment.count }
228
+ end
229
+
230
+ it 'does exclude associations from a given array with a truthy condition' do
231
+ ::Post.amoeba do
232
+ exclude_association [:comments], if: :truthy?
233
+ end
234
+ expect { subject }.not_to change { Comment.count }
235
+ end
236
+
237
+ it 'does not exclude associations from a given array with a falsey condition' do
238
+ ::Post.amoeba do
239
+ exclude_association [:comments], if: :falsey?
240
+ end
241
+ expect { subject }.to change { Comment.count }.by(3)
242
+ end
243
+ end
244
+
245
+ context 'override' do
246
+ before :each do
247
+ ::Image.fresh_amoeba
248
+ ::Image.amoeba do
249
+ override ->(old, new) { new.product_id = 13 if old.filename == 'test.jpg' }
250
+ end
251
+ end
252
+
253
+ it 'should override fields' do
254
+ image = ::Image.create(filename: 'test.jpg', product_id: 12)
255
+ image_dup = image.amoeba_dup
256
+ expect(image_dup.save).to be_truthy
257
+ expect(image_dup.product_id).to eq(13)
258
+ end
259
+
260
+ it 'should not override fields' do
261
+ image = ::Image.create(filename: 'test2.jpg', product_id: 12)
262
+ image_dup = image.amoeba_dup
263
+ expect(image_dup.save).to be_truthy
264
+ expect(image_dup.product_id).to eq(12)
265
+ end
266
+ end
267
+
268
+ context 'nullify' do
269
+ before :each do
270
+ ::Image.fresh_amoeba
271
+ ::Image.amoeba do
272
+ nullify :product_id
273
+ end
274
+ end
275
+
276
+ let(:image) { ::Image.create(filename: 'test.jpg', product_id: 12) }
277
+ let(:image_dup) { image.amoeba_dup }
278
+
279
+ it 'should nullify fields' do
280
+ expect(image_dup.save).to be_truthy
281
+ expect(image_dup.product_id).to be_nil
282
+ end
283
+ end
284
+
285
+ context 'strict propagate' do
286
+ it 'should call #reset_amoeba' do
287
+ expect(::SuperBlackBox).to receive(:reset_amoeba).and_call_original
288
+ box = ::SuperBlackBox.create(title: 'Super Black Box', price: 9.99, length: 1, metal: '1')
289
+ new_box = box.amoeba_dup
290
+ expect(new_box.save).to be_truthy
291
+ end
292
+ end
293
+
294
+ context 'remapping and custom dup method' do
295
+ let(:prototype) { ObjectPrototype.new }
296
+
297
+ context 'through' do
298
+ it do
299
+ real_object = prototype.amoeba_dup
300
+ expect(real_object).to be_a(::RealObject)
301
+ end
302
+ end
303
+
304
+ context 'remapper' do
305
+ it do
306
+ prototype.subobject_prototypes << SubobjectPrototype.new
307
+ real_object = prototype.amoeba_dup
308
+ expect(real_object.subobjects.length).to eq(1)
309
+ end
310
+ end
311
+ end
312
+
313
+ context 'preprocessing fields' do
314
+ subject { super_admin.amoeba_dup }
315
+ let(:super_admin) { ::SuperAdmin.create!(email: 'user@example.com', active: true, password: 'password') }
316
+
317
+ it 'should accept "set" to set false to attribute' do
318
+ expect(subject.active).to be false
319
+ end
320
+
321
+ it 'should skip "prepend" if it equal to false' do
322
+ expect(subject.password).to eq('password')
323
+ end
324
+ end
325
+
326
+ context 'inheritance' do
327
+ let(:box) { Box.create }
328
+
329
+ it 'does not fail with a deep inheritance' do
330
+ sub_sub_product = BoxSubSubProduct.create(title: 'Awesome shoes')
331
+ another_product = BoxAnotherProduct.create(title: 'Cleaning product')
332
+ sub_sub_product.update(box: box, another_product: another_product)
333
+ expect(box.sub_products.first.another_product.title).to eq('Cleaning product')
334
+ expect(box.amoeba_dup.sub_products.first.another_product.title).to eq('Cleaning product')
335
+ end
336
+ end
337
+
338
+ context 'inheritance extended' do
339
+ let(:stage) do
340
+ stage = CustomStage.new(title: 'My Stage', external_id: 213)
341
+ stage.listeners.build(name: 'John')
342
+ stage.listeners.build(name: 'Helen')
343
+ stage.specialists.build(name: 'Jack')
344
+ stage.custom_rules.build(description: 'Kill all humans')
345
+ stage.save!
346
+ stage
347
+ end
348
+
349
+ subject { stage.amoeba_dup }
350
+
351
+ it 'contains parent association and own associations', :aggregate_failures do
352
+ subject
353
+ expect { subject.save! }.to change(Listener, :count).by(2).
354
+ and change(Specialist, :count).by(1).
355
+ and change(CustomRule, :count).by(1)
356
+
357
+ expect(subject.title).to eq 'My Stage'
358
+ expect(subject.external_id).to be_nil
359
+ expect(subject.listeners.find_by(name: 'John')).to_not be_nil
360
+ expect(subject.listeners.find_by(name: 'Helen')).to_not be_nil
361
+ expect(subject.specialists.find_by(name: 'Jack')).to_not be_nil
362
+ expect(subject.custom_rules.first.description).to eq 'Kill all humans'
363
+ end
364
+ end
365
+
366
+ context 'polymorphic' do
367
+ let(:company) { Company.find_by(name: 'ABC Industries') }
368
+ let(:new_company) { company.amoeba_dup }
369
+
370
+ it 'does not fail with a deep inheritance' do
371
+ # employee = company.employees.where(name:'Joe').first
372
+ start_company_count = Company.count
373
+ start_customer_count = Customer.count
374
+ start_employee_count = Employee.count
375
+ start_address_count = Address.count
376
+ start_photo_count = Photo.count
377
+ new_company.name = "Copy of #{new_company.name}"
378
+ new_company.save
379
+ expect(Company.count).to eq(start_company_count + 1)
380
+ expect(Customer.count).to eq(start_customer_count + 1)
381
+ expect(Employee.count).to eq(start_employee_count + 1)
382
+ expect(Address.count).to eq(start_address_count + 4)
383
+ expect(Photo.count).to eq(start_photo_count + 2)
384
+
385
+ new_company.reload # fully reload from database
386
+ new_company_employees = new_company.employees
387
+ expect(new_company_employees.count).to eq(1)
388
+ new_company_employee_joe = new_company_employees.find_by(name: 'Joe')
389
+ expect(new_company_employee_joe.photos.count).to eq(1)
390
+ expect(new_company_employee_joe.photos.first.size).to eq(12_345)
391
+ expect(new_company_employee_joe.addresses.count).to eq(2)
392
+ expect(new_company_employee_joe.addresses.where(street: '123 My Street').count).to eq(1)
393
+ expect(new_company_employee_joe.addresses.where(street: '124 My Street').count).to eq(1)
394
+ new_company_customers = new_company.customers
395
+ expect(new_company_customers.count).to eq(1)
396
+ new_company_customer_my = new_company_customers.where(email: 'my@email.address').first
397
+ expect(new_company_customer_my.photos.count).to eq(1)
398
+ expect(new_company_customer_my.photos.first.size).to eq(54_321)
399
+ expect(new_company_customer_my.addresses.count).to eq(2)
400
+ expect(new_company_customer_my.addresses.where(street: '321 My Street').count).to eq(1)
401
+ expect(new_company_customer_my.addresses.where(street: '321 My Drive').count).to eq(1)
402
+ end
403
+ end
179
404
  end