amoeba 3.0.0 → 3.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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/Appraisals +0 -4
- data/README.md +661 -527
- data/lib/amoeba/class_methods.rb +5 -0
- data/lib/amoeba/cloner.rb +15 -11
- data/lib/amoeba/config.rb +28 -20
- data/lib/amoeba/version.rb +1 -1
- data/spec/lib/amoeba_spec.rb +166 -71
- data/spec/support/data.rb +19 -40
- data/spec/support/models.rb +104 -17
- data/spec/support/schema.rb +33 -0
- metadata +3 -4
- data/gemfiles/activerecord_3.2.gemfile +0 -17
data/lib/amoeba/class_methods.rb
CHANGED
data/lib/amoeba/cloner.rb
CHANGED
@@ -9,10 +9,11 @@ module Amoeba
|
|
9
9
|
def_delegators :old_object, :_parent_amoeba, :_amoeba_settings,
|
10
10
|
:_parent_amoeba_settings
|
11
11
|
|
12
|
-
def_delegators :object_klass, :amoeba, :fresh_amoeba
|
12
|
+
def_delegators :object_klass, :amoeba, :fresh_amoeba, :reset_amoeba
|
13
13
|
|
14
14
|
def initialize(object, options = {})
|
15
|
-
@old_object
|
15
|
+
@old_object = object
|
16
|
+
@options = options
|
16
17
|
@object_klass = @old_object.class
|
17
18
|
inherit_parent_settings
|
18
19
|
@new_object = object.__send__(amoeba.dup_method)
|
@@ -40,12 +41,13 @@ module Amoeba
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def inherit_submissive_parent_settings
|
43
|
-
|
44
|
+
reset_amoeba(&_amoeba_settings)
|
45
|
+
amoeba(&_parent_amoeba_settings)
|
44
46
|
amoeba(&_amoeba_settings)
|
45
47
|
end
|
46
48
|
|
47
49
|
def inherit_parent_settings
|
48
|
-
return if
|
50
|
+
return if !_parent_amoeba.inherit
|
49
51
|
return unless %w(strict relaxed submissive).include?(parenting_style.to_s)
|
50
52
|
__send__("inherit_#{parenting_style}_parent_settings".to_sym)
|
51
53
|
end
|
@@ -64,19 +66,21 @@ module Amoeba
|
|
64
66
|
# table from copying so we don't end up with the new
|
65
67
|
# and old children on the copy
|
66
68
|
return unless association.macro == :has_many ||
|
67
|
-
|
69
|
+
association.is_a?(::ActiveRecord::Reflection::ThroughReflection)
|
68
70
|
amoeba.exclude_association(association.options[:through])
|
69
71
|
end
|
70
72
|
|
71
73
|
def follow_only_includes
|
72
|
-
amoeba.includes.each do |include|
|
74
|
+
amoeba.includes.each do |include, options|
|
75
|
+
next if options[:if] && !@old_object.send(options[:if])
|
73
76
|
follow_association(include, @object_klass.reflect_on_association(include))
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
77
80
|
def follow_all_except_excludes
|
78
81
|
@object_klass.reflections.each do |name, association|
|
79
|
-
|
82
|
+
exclude = amoeba.excludes[name.to_sym]
|
83
|
+
next if exclude && (exclude.blank? || @old_object.send(exclude[:if]))
|
80
84
|
follow_association(name, association)
|
81
85
|
end
|
82
86
|
end
|
@@ -88,9 +92,9 @@ module Amoeba
|
|
88
92
|
end
|
89
93
|
|
90
94
|
def apply_associations
|
91
|
-
if amoeba.includes.
|
95
|
+
if amoeba.includes.present?
|
92
96
|
follow_only_includes
|
93
|
-
elsif amoeba.excludes.
|
97
|
+
elsif amoeba.excludes.present?
|
94
98
|
follow_all_except_excludes
|
95
99
|
else
|
96
100
|
follow_all
|
@@ -110,7 +114,7 @@ module Amoeba
|
|
110
114
|
|
111
115
|
def process_overrides
|
112
116
|
amoeba.overrides.each do |block|
|
113
|
-
block.call(@
|
117
|
+
block.call(@old_object, @new_object)
|
114
118
|
end
|
115
119
|
end
|
116
120
|
|
@@ -124,7 +128,7 @@ module Amoeba
|
|
124
128
|
def process_coercions
|
125
129
|
# prepend any extra strings to indicate uniqueness of the new record(s)
|
126
130
|
amoeba.coercions.each do |field, coercion|
|
127
|
-
@new_object[field] =
|
131
|
+
@new_object[field] = coercion.to_s
|
128
132
|
end
|
129
133
|
end
|
130
134
|
|
data/lib/amoeba/config.rb
CHANGED
@@ -8,8 +8,8 @@ module Amoeba
|
|
8
8
|
raised: false,
|
9
9
|
dup_method: :dup,
|
10
10
|
remap_method: nil,
|
11
|
-
includes:
|
12
|
-
excludes:
|
11
|
+
includes: {},
|
12
|
+
excludes: {},
|
13
13
|
clones: [],
|
14
14
|
customizations: [],
|
15
15
|
overrides: [],
|
@@ -40,7 +40,7 @@ module Amoeba
|
|
40
40
|
DEFAULTS.freeze
|
41
41
|
|
42
42
|
DEFAULTS.each do |key, value|
|
43
|
-
value.freeze
|
43
|
+
value.freeze if value.is_a?(Array) || value.is_a?(Hash)
|
44
44
|
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
45
45
|
def #{key} # def enabled
|
46
46
|
@config[:#{key}] # @config[:enabled]
|
@@ -69,15 +69,15 @@ module Amoeba
|
|
69
69
|
|
70
70
|
def propagate(style = :submissive)
|
71
71
|
@config[:parenting] ||= style
|
72
|
-
@config[:inherit]
|
72
|
+
@config[:inherit] = true
|
73
73
|
end
|
74
74
|
|
75
75
|
def push_value_to_array(value, key)
|
76
76
|
res = @config[key]
|
77
77
|
if value.is_a?(::Array)
|
78
78
|
res = value
|
79
|
-
|
80
|
-
res << value
|
79
|
+
elsif value
|
80
|
+
res << value
|
81
81
|
end
|
82
82
|
@config[key] = res.uniq
|
83
83
|
end
|
@@ -107,37 +107,45 @@ module Amoeba
|
|
107
107
|
@config[config_key][key] = val if val || (!val.nil? && config_key == :coercions)
|
108
108
|
end
|
109
109
|
|
110
|
-
def include_association(value = nil)
|
111
|
-
|
112
|
-
@config[:excludes] =
|
113
|
-
|
110
|
+
def include_association(value = nil, options = {})
|
111
|
+
enable
|
112
|
+
@config[:excludes] = {}
|
113
|
+
push_value_to_hash({ value => options }, :includes)
|
114
|
+
end
|
115
|
+
|
116
|
+
def include_associations(*values)
|
117
|
+
values.flatten.each { |v| include_association(v) }
|
114
118
|
end
|
115
119
|
|
116
|
-
# TODO remove this method in v3.0.0
|
120
|
+
# TODO: remove this method in v3.0.0
|
117
121
|
def include_field(value = nil)
|
118
|
-
warn
|
122
|
+
warn 'include_field is deprecated and will be removed in version 3.0.0; please use include_association instead'
|
119
123
|
include_association(value)
|
120
124
|
end
|
121
125
|
|
122
|
-
def exclude_association(value = nil)
|
123
|
-
|
124
|
-
@config[:includes] =
|
125
|
-
|
126
|
+
def exclude_association(value = nil, options = {})
|
127
|
+
enable
|
128
|
+
@config[:includes] = {}
|
129
|
+
push_value_to_hash({ value => options }, :excludes)
|
126
130
|
end
|
127
131
|
|
128
|
-
|
132
|
+
def exclude_associations(*values)
|
133
|
+
values.flatten.each { |v| exclude_association(v) }
|
134
|
+
end
|
135
|
+
|
136
|
+
# TODO: remove this method in v3.0.0
|
129
137
|
def exclude_field(value = nil)
|
130
|
-
warn
|
138
|
+
warn 'exclude_field is deprecated and will be removed in version 3.0.0; please use exclude_association instead'
|
131
139
|
exclude_association(value)
|
132
140
|
end
|
133
141
|
|
134
142
|
def clone(value = nil)
|
135
|
-
|
143
|
+
enable
|
136
144
|
push_value_to_array(value, :clones)
|
137
145
|
end
|
138
146
|
|
139
147
|
def recognize(value = nil)
|
140
|
-
|
148
|
+
enable
|
141
149
|
push_value_to_array(value, :known_macros)
|
142
150
|
end
|
143
151
|
|
data/lib/amoeba/version.rb
CHANGED
data/spec/lib/amoeba_spec.rb
CHANGED
@@ -5,6 +5,9 @@ describe 'amoeba' do
|
|
5
5
|
before :each do
|
6
6
|
require ::File.dirname(__FILE__) + '/../support/data.rb'
|
7
7
|
end
|
8
|
+
|
9
|
+
let(:first_product) { Product.find(1) }
|
10
|
+
|
8
11
|
it 'duplicates associated child records' do
|
9
12
|
# Posts {{{
|
10
13
|
old_post = ::Post.find(1)
|
@@ -29,18 +32,16 @@ describe 'amoeba' do
|
|
29
32
|
start_postconfig_count = PostConfig.all.count
|
30
33
|
start_postwidget_count = PostWidget.all.count
|
31
34
|
start_superkitten_count = Superkitten.all.count
|
32
|
-
|
33
|
-
|
34
|
-
rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS note_count FROM notes_posts')
|
35
|
-
start_postnote_count = rs['note_count']
|
35
|
+
start_posttag_count = Post.tag_count
|
36
|
+
start_postnote_count = Post.note_count
|
36
37
|
|
37
38
|
expect(new_post.save!).to be_truthy
|
38
39
|
expect(new_post.title).to eq("Copy of #{old_post.title}")
|
39
40
|
|
40
|
-
end_account_count = Account.
|
41
|
-
end_history_count = History.
|
42
|
-
end_cat_count = Category.
|
43
|
-
end_supercat_count = Supercat.
|
41
|
+
end_account_count = Account.count
|
42
|
+
end_history_count = History.count
|
43
|
+
end_cat_count = Category.count
|
44
|
+
end_supercat_count = Supercat.count
|
44
45
|
end_tag_count = Tag.all.count
|
45
46
|
end_note_count = Note.all.count
|
46
47
|
end_widget_count = Widget.all.count
|
@@ -50,10 +51,8 @@ describe 'amoeba' do
|
|
50
51
|
end_postconfig_count = PostConfig.all.count
|
51
52
|
end_postwidget_count = PostWidget.all.count
|
52
53
|
end_superkitten_count = Superkitten.all.count
|
53
|
-
|
54
|
-
|
55
|
-
rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS note_count FROM notes_posts')
|
56
|
-
end_postnote_count = rs['note_count']
|
54
|
+
end_posttag_count = Post.tag_count
|
55
|
+
end_postnote_count = Post.note_count
|
57
56
|
|
58
57
|
expect(end_tag_count).to eq(start_tag_count)
|
59
58
|
expect(end_cat_count).to eq(start_cat_count)
|
@@ -71,9 +70,9 @@ describe 'amoeba' do
|
|
71
70
|
expect(end_postnote_count).to eq(start_postnote_count * 2)
|
72
71
|
expect(end_superkitten_count).to eq(start_superkitten_count * 2)
|
73
72
|
|
74
|
-
expect(new_post.supercats.map(&:ramblings).include
|
73
|
+
expect(new_post.supercats.map(&:ramblings)).to include('Copy of zomg')
|
75
74
|
expect(new_post.supercats.map(&:other_ramblings).uniq.length).to eq(1)
|
76
|
-
expect(new_post.supercats.map(&:other_ramblings).uniq.include
|
75
|
+
expect(new_post.supercats.map(&:other_ramblings).uniq).to include('La la la')
|
77
76
|
expect(new_post.contents).to eq("Here's a copy: #{old_post.contents.gsub(/dog/, 'cat')} (copied version)")
|
78
77
|
expect(new_post.comments.length).to eq(5)
|
79
78
|
expect(new_post.comments.select { |c| c.nerf == 'ratatat' && c.contents.nil? }.length).to eq(1)
|
@@ -82,7 +81,7 @@ describe 'amoeba' do
|
|
82
81
|
expect(new_post.comments.select { |c| c.nerf == 'bonkers' && c.contents.nil? }.length).to eq(1)
|
83
82
|
|
84
83
|
new_post.widgets.map(&:id).each do |id|
|
85
|
-
expect(old_post.widgets.map(&:id).include
|
84
|
+
expect(old_post.widgets.map(&:id)).not_to include(id)
|
86
85
|
end
|
87
86
|
|
88
87
|
expect(new_post.custom_things.length).to eq(3)
|
@@ -94,7 +93,7 @@ describe 'amoeba' do
|
|
94
93
|
old_author = Author.find(1)
|
95
94
|
new_author = old_author.amoeba_dup
|
96
95
|
new_author.save!
|
97
|
-
expect(new_author.errors.messages
|
96
|
+
expect(new_author.errors.messages).to be_empty
|
98
97
|
expect(new_author.posts.first.custom_things.length).to eq(3)
|
99
98
|
expect(new_author.posts.first.custom_things.select { |ct| ct.value == [] }.length).to eq(1)
|
100
99
|
expect(new_author.posts.first.custom_things.select { |ct| ct.value == [1, 2] }.length).to eq(1)
|
@@ -102,24 +101,20 @@ describe 'amoeba' do
|
|
102
101
|
# }}}
|
103
102
|
# Products {{{
|
104
103
|
# Base Class {{{
|
105
|
-
old_product = Product.find(1)
|
106
104
|
|
107
|
-
start_image_count =
|
105
|
+
start_image_count = first_product.images.count
|
108
106
|
start_section_count = Section.all.length
|
109
|
-
|
110
|
-
start_prodsection_count = rs['section_count']
|
107
|
+
start_prodsection_count = first_product.section_count
|
111
108
|
|
112
|
-
new_product =
|
109
|
+
new_product = first_product.amoeba_dup
|
113
110
|
new_product.save
|
114
|
-
expect(new_product.errors.messages
|
111
|
+
expect(new_product.errors.messages).to be_empty
|
115
112
|
|
116
|
-
end_image_count =
|
117
|
-
end_newimage_count =
|
113
|
+
end_image_count = first_product.images.count
|
114
|
+
end_newimage_count = new_product.images.count
|
118
115
|
end_section_count = Section.all.length
|
119
|
-
|
120
|
-
|
121
|
-
rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', new_product.id)
|
122
|
-
end_newprodsection_count = rs['section_count']
|
116
|
+
end_prodsection_count = first_product.section_count
|
117
|
+
end_newprodsection_count = new_product.section_count
|
123
118
|
|
124
119
|
expect(end_image_count).to eq(start_image_count)
|
125
120
|
expect(end_newimage_count).to eq(start_image_count)
|
@@ -132,22 +127,19 @@ describe 'amoeba' do
|
|
132
127
|
# Shirt {{{
|
133
128
|
old_product = Shirt.find(2)
|
134
129
|
|
135
|
-
start_image_count =
|
136
|
-
start_section_count = Section.
|
137
|
-
|
138
|
-
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
|
139
133
|
|
140
134
|
new_product = old_product.amoeba_dup
|
141
135
|
new_product.save
|
142
|
-
expect(new_product.errors.messages
|
136
|
+
expect(new_product.errors.messages).to be_empty
|
143
137
|
|
144
|
-
end_image_count =
|
145
|
-
end_newimage_count =
|
146
|
-
end_section_count = Section.
|
147
|
-
|
148
|
-
|
149
|
-
rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', new_product.id)
|
150
|
-
end_newprodsection_count = rs['section_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
|
151
143
|
|
152
144
|
expect(end_image_count).to eq(start_image_count)
|
153
145
|
expect(end_newimage_count).to eq(start_image_count)
|
@@ -159,22 +151,19 @@ describe 'amoeba' do
|
|
159
151
|
# Necklace {{{
|
160
152
|
old_product = Necklace.find(3)
|
161
153
|
|
162
|
-
start_image_count =
|
163
|
-
start_section_count = Section.
|
164
|
-
|
165
|
-
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
|
166
157
|
|
167
158
|
new_product = old_product.amoeba_dup
|
168
159
|
new_product.save
|
169
|
-
expect(new_product.errors.messages
|
160
|
+
expect(new_product.errors.messages).to be_empty
|
170
161
|
|
171
|
-
end_image_count =
|
172
|
-
end_newimage_count =
|
173
|
-
end_section_count = Section.
|
174
|
-
|
175
|
-
|
176
|
-
rs = ActiveRecord::Base.connection.select_one('SELECT COUNT(*) AS section_count FROM products_sections WHERE product_id = ?', new_product.id)
|
177
|
-
end_newprodsection_count = rs['section_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
|
178
167
|
|
179
168
|
expect(end_image_count).to eq(start_image_count)
|
180
169
|
expect(end_newimage_count).to eq(start_image_count)
|
@@ -186,23 +175,60 @@ describe 'amoeba' do
|
|
186
175
|
# }}}
|
187
176
|
end
|
188
177
|
end
|
178
|
+
|
179
|
+
context 'use if condition in includes and excludes' 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 associations 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 associations with false 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 associations with 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 associations with false 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
|
+
end
|
216
|
+
|
189
217
|
context 'override' do
|
190
218
|
before :each do
|
191
219
|
::Image.fresh_amoeba
|
192
220
|
::Image.amoeba do
|
193
|
-
override ->(old, new) {
|
194
|
-
if old.filename == 'test.jpg'
|
195
|
-
new.product_id = 13
|
196
|
-
end
|
197
|
-
}
|
221
|
+
override ->(old, new) { new.product_id = 13 if old.filename == 'test.jpg' }
|
198
222
|
end
|
199
223
|
end
|
224
|
+
|
200
225
|
it 'should override fields' do
|
201
226
|
image = ::Image.create(filename: 'test.jpg', product_id: 12)
|
202
227
|
image_dup = image.amoeba_dup
|
203
228
|
expect(image_dup.save).to be_truthy
|
204
229
|
expect(image_dup.product_id).to eq(13)
|
205
230
|
end
|
231
|
+
|
206
232
|
it 'should not override fields' do
|
207
233
|
image = ::Image.create(filename: 'test2.jpg', product_id: 12)
|
208
234
|
image_dup = image.amoeba_dup
|
@@ -218,17 +244,19 @@ describe 'amoeba' do
|
|
218
244
|
nullify :product_id
|
219
245
|
end
|
220
246
|
end
|
247
|
+
|
248
|
+
let(:image) { ::Image.create(filename: 'test.jpg', product_id: 12) }
|
249
|
+
let(:image_dup) { image.amoeba_dup }
|
250
|
+
|
221
251
|
it 'should nullify fields' do
|
222
|
-
image = ::Image.create(filename: 'test.jpg', product_id: 12)
|
223
|
-
image_dup = image.amoeba_dup
|
224
252
|
expect(image_dup.save).to be_truthy
|
225
253
|
expect(image_dup.product_id).to be_nil
|
226
254
|
end
|
227
255
|
end
|
228
256
|
|
229
257
|
context 'strict propagate' do
|
230
|
-
it 'should call #
|
231
|
-
expect(::SuperBlackBox).to receive(:
|
258
|
+
it 'should call #reset_amoeba' do
|
259
|
+
expect(::SuperBlackBox).to receive(:reset_amoeba).and_call_original
|
232
260
|
box = ::SuperBlackBox.create(title: 'Super Black Box', price: 9.99, length: 1, metal: '1')
|
233
261
|
new_box = box.amoeba_dup
|
234
262
|
expect(new_box.save).to be_truthy
|
@@ -237,12 +265,14 @@ describe 'amoeba' do
|
|
237
265
|
|
238
266
|
context 'remapping and custom dup method' do
|
239
267
|
let(:prototype) { ObjectPrototype.new }
|
268
|
+
|
240
269
|
context 'through' do
|
241
270
|
it do
|
242
271
|
real_object = prototype.amoeba_dup
|
243
272
|
expect(real_object).to be_a(::RealObject)
|
244
273
|
end
|
245
274
|
end
|
275
|
+
|
246
276
|
context 'remapper' do
|
247
277
|
it do
|
248
278
|
prototype.subobject_prototypes << SubobjectPrototype.new
|
@@ -253,29 +283,94 @@ describe 'amoeba' do
|
|
253
283
|
end
|
254
284
|
|
255
285
|
context 'preprocessing fields' do
|
286
|
+
subject { super_admin.amoeba_dup }
|
287
|
+
let(:super_admin) { ::SuperAdmin.create!(email: 'user@example.com', active: true, password: 'password') }
|
288
|
+
|
256
289
|
it 'should accept "set" to set false to attribute' do
|
257
|
-
|
258
|
-
clone = super_admin.amoeba_dup
|
259
|
-
expect(clone.active).to eq(false)
|
290
|
+
expect(subject.active).to be false
|
260
291
|
end
|
292
|
+
|
261
293
|
it 'should skip "prepend" if it equal to false' do
|
262
|
-
|
263
|
-
clone = super_admin.amoeba_dup
|
264
|
-
expect(clone.password).to eq('password')
|
294
|
+
expect(subject.password).to eq('password')
|
265
295
|
end
|
266
296
|
end
|
267
297
|
|
268
298
|
context 'inheritance' do
|
299
|
+
let(:box) { Box.create }
|
300
|
+
|
269
301
|
it 'does not fail with a deep inheritance' do
|
270
|
-
box = Box.create
|
271
302
|
sub_sub_product = BoxSubSubProduct.create(title: 'Awesome shoes')
|
272
303
|
another_product = BoxAnotherProduct.create(title: 'Cleaning product')
|
273
|
-
sub_sub_product.box
|
274
|
-
sub_sub_product.another_product = another_product
|
275
|
-
sub_sub_product.save
|
304
|
+
sub_sub_product.update_attributes(box: box, another_product: another_product)
|
276
305
|
expect(box.sub_products.first.another_product.title).to eq('Cleaning product')
|
277
306
|
expect(box.amoeba_dup.sub_products.first.another_product.title).to eq('Cleaning product')
|
278
307
|
end
|
279
308
|
end
|
280
309
|
|
310
|
+
context 'inheritance extended' do
|
311
|
+
let(:stage) do
|
312
|
+
stage = CustomStage.new(title: 'My Stage', external_id: 213)
|
313
|
+
stage.listeners.build(name: 'John')
|
314
|
+
stage.listeners.build(name: 'Helen')
|
315
|
+
stage.specialists.build(name: 'Jack')
|
316
|
+
stage.custom_rules.build(description: 'Kill all humans')
|
317
|
+
stage.save!
|
318
|
+
stage
|
319
|
+
end
|
320
|
+
|
321
|
+
subject { stage.amoeba_dup }
|
322
|
+
|
323
|
+
it "contains parent association and own associations", :aggregate_failures do
|
324
|
+
subject
|
325
|
+
expect { subject.save! }.to change(Listener, :count).by(2).
|
326
|
+
and change(Specialist, :count).by(1).
|
327
|
+
and change(CustomRule, :count).by(1)
|
328
|
+
|
329
|
+
expect(subject.title).to eq 'My Stage'
|
330
|
+
expect(subject.external_id).to be_nil
|
331
|
+
expect(subject.listeners.find_by(name: 'John')).to_not be_nil
|
332
|
+
expect(subject.listeners.find_by(name: 'Helen')).to_not be_nil
|
333
|
+
expect(subject.specialists.find_by(name: 'Jack')).to_not be_nil
|
334
|
+
expect(subject.custom_rules.first.description).to eq 'Kill all humans'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context 'polymorphic' do
|
339
|
+
let(:company) { Company.find_by(name: 'ABC Industries') }
|
340
|
+
let(:new_company) { company.amoeba_dup }
|
341
|
+
|
342
|
+
it 'does not fail with a deep inheritance' do
|
343
|
+
# employee = company.employees.where(name:'Joe').first
|
344
|
+
start_company_count = Company.count
|
345
|
+
start_customer_count = Customer.count
|
346
|
+
start_employee_count = Employee.count
|
347
|
+
start_address_count = Address.count
|
348
|
+
start_photo_count = Photo.count
|
349
|
+
new_company.name = "Copy of #{new_company.name}"
|
350
|
+
new_company.save
|
351
|
+
expect(Company.count).to eq(start_company_count + 1)
|
352
|
+
expect(Customer.count).to eq(start_customer_count + 1)
|
353
|
+
expect(Employee.count).to eq(start_employee_count + 1)
|
354
|
+
expect(Address.count).to eq(start_address_count + 4)
|
355
|
+
expect(Photo.count).to eq(start_photo_count + 2)
|
356
|
+
|
357
|
+
new_company.reload # fully reload from database
|
358
|
+
new_company_employees = new_company.employees
|
359
|
+
expect(new_company_employees.count).to eq(1)
|
360
|
+
new_company_employee_joe = new_company_employees.find_by(name: 'Joe')
|
361
|
+
expect(new_company_employee_joe.photos.count).to eq(1)
|
362
|
+
expect(new_company_employee_joe.photos.first.size).to eq(12_345)
|
363
|
+
expect(new_company_employee_joe.addresses.count).to eq(2)
|
364
|
+
expect(new_company_employee_joe.addresses.where(street: '123 My Street').count).to eq(1)
|
365
|
+
expect(new_company_employee_joe.addresses.where(street: '124 My Street').count).to eq(1)
|
366
|
+
new_company_customers = new_company.customers
|
367
|
+
expect(new_company_customers.count).to eq(1)
|
368
|
+
new_company_customer_my = new_company_customers.where(email: 'my@email.address').first
|
369
|
+
expect(new_company_customer_my.photos.count).to eq(1)
|
370
|
+
expect(new_company_customer_my.photos.first.size).to eq(54_321)
|
371
|
+
expect(new_company_customer_my.addresses.count).to eq(2)
|
372
|
+
expect(new_company_customer_my.addresses.where(street: '321 My Street').count).to eq(1)
|
373
|
+
expect(new_company_customer_my.addresses.where(street: '321 My Drive').count).to eq(1)
|
374
|
+
end
|
375
|
+
end
|
281
376
|
end
|