amoeba 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a68fa1a80b0adb7f599e0a32be7bd689fa345e4
|
4
|
+
data.tar.gz: 3ce6f7bcce38f2c7fb1451c992527e9252acce8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e72bac7e454ab09712e28b2ee0651b25fb25cac04eba7ea276d43aa71bf6d427fa904a1525534fdc9b28dd48968d7f5fd1b2a983518e17840efe36cf61892ee
|
7
|
+
data.tar.gz: 4da78e7256ad15fe02bd2e3cf0249a114556f3e80cc63991167bd20d3011a7ce3aea3be03b1915a716a149c18ddb80c705e8391054dffa130595cabf8b9fdd69
|
data/.travis.yml
CHANGED
@@ -3,11 +3,11 @@ rvm:
|
|
3
3
|
- 2.0.0
|
4
4
|
- 2.1
|
5
5
|
- 2.2
|
6
|
+
- 2.3
|
6
7
|
- ruby-head
|
7
8
|
- jruby-19mode # JRuby in 1.9 mode
|
8
9
|
- rbx-2
|
9
10
|
gemfile:
|
10
|
-
- gemfiles/activerecord_3.2.gemfile
|
11
11
|
- gemfiles/activerecord_4.0.gemfile
|
12
12
|
- gemfiles/activerecord_4.1.gemfile
|
13
13
|
- gemfiles/activerecord_4.2.gemfile
|
@@ -17,3 +17,4 @@ matrix:
|
|
17
17
|
- rvm: ruby-head
|
18
18
|
- rvm: jruby-19mode
|
19
19
|
bundler_args: --without local_development
|
20
|
+
before_install: gem install bundler
|
data/Appraisals
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
Easy cloning of active_record objects including associations and several operations under associations and attributes.
|
4
4
|
|
5
5
|
[![Code Climate](https://codeclimate.com/github/rocksolidwebdesign/amoeba/badges/gpa.svg)](https://codeclimate.com/github/rocksolidwebdesign/amoeba)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/amoeba.svg)](http://badge.fury.io/rb/amoeba)
|
7
|
+
[![Build Status](https://travis-ci.org/amoeba-rb/amoeba.svg?branch=master)](https://travis-ci.org/amoeba-rb/amoeba)
|
6
8
|
|
7
9
|
## What?
|
8
10
|
|
@@ -44,26 +46,32 @@ Rails 3.2, 4.x compatible.
|
|
44
46
|
|
45
47
|
is hopefully as you would expect:
|
46
48
|
|
47
|
-
|
49
|
+
```sh
|
50
|
+
gem install amoeba
|
51
|
+
```
|
48
52
|
|
49
53
|
or just add it to your Gemfile:
|
50
54
|
|
51
|
-
|
55
|
+
```sh
|
56
|
+
gem 'amoeba'
|
57
|
+
```
|
52
58
|
|
53
59
|
Configure your models with one of the styles below and then just run the `amoeba_dup` method on your model where you would run the `dup` method normally:
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
```ruby
|
62
|
+
p = Post.create(:title => "Hello World!", :content => "Lorum ipsum dolor")
|
63
|
+
p.comments.create(:content => "I love it!")
|
64
|
+
p.comments.create(:content => "This sucks!")
|
58
65
|
|
59
|
-
|
66
|
+
puts Comment.all.count # should be 2
|
60
67
|
|
61
|
-
|
62
|
-
|
68
|
+
my_copy = p.amoeba_dup
|
69
|
+
my_copy.save
|
63
70
|
|
64
|
-
|
71
|
+
puts Comment.all.count # should be 4
|
72
|
+
```
|
65
73
|
|
66
|
-
By default, when enabled, amoeba will copy any and all associated child records automatically and
|
74
|
+
By default, when enabled, amoeba will copy any and all associated child records automatically and associate them with the new parent record.
|
67
75
|
|
68
76
|
You can configure the behavior to only include fields that you list or to only include fields that you don't exclude. Of the three, the most performant will be the indiscriminate style, followed by the inclusive style, and the exclusive style will be the slowest because of the need for an extra explicit check on each field. This performance difference is likely negligible enough that you can choose the style to use based on which is easiest to read and write, however, if your data tree is large enough and you need control over what fields get copied, inclusive style is probably a better choice than exclusive style.
|
69
77
|
|
@@ -77,27 +85,31 @@ This is the most basic usage case and will simply enable the copying of any know
|
|
77
85
|
|
78
86
|
If you have some models for a blog about like this:
|
79
87
|
|
80
|
-
|
81
|
-
|
82
|
-
|
88
|
+
```ruby
|
89
|
+
class Post < ActiveRecord::Base
|
90
|
+
has_many :comments
|
91
|
+
end
|
83
92
|
|
84
|
-
|
85
|
-
|
86
|
-
|
93
|
+
class Comment < ActiveRecord::Base
|
94
|
+
belongs_to :post
|
95
|
+
end
|
96
|
+
```
|
87
97
|
|
88
98
|
simply add the amoeba configuration block to your model and call the enable method to enable the copying of child records, like this:
|
89
99
|
|
90
|
-
|
91
|
-
|
100
|
+
```ruby
|
101
|
+
class Post < ActiveRecord::Base
|
102
|
+
has_many :comments
|
92
103
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
104
|
+
amoeba do
|
105
|
+
enable
|
106
|
+
end
|
107
|
+
end
|
97
108
|
|
98
|
-
|
99
|
-
|
100
|
-
|
109
|
+
class Comment < ActiveRecord::Base
|
110
|
+
belongs_to :post
|
111
|
+
end
|
112
|
+
```
|
101
113
|
|
102
114
|
Child records will be automatically copied when you run the `amoeba_dup` method.
|
103
115
|
|
@@ -105,110 +117,159 @@ Child records will be automatically copied when you run the `amoeba_dup` method.
|
|
105
117
|
|
106
118
|
If you only want some of the associations copied but not others, you may use the inclusive style:
|
107
119
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
120
|
+
```ruby
|
121
|
+
class Post < ActiveRecord::Base
|
122
|
+
has_many :comments
|
123
|
+
has_many :tags
|
124
|
+
has_many :authors
|
112
125
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
126
|
+
amoeba do
|
127
|
+
enable
|
128
|
+
include_association :tags
|
129
|
+
include_association :authors
|
130
|
+
end
|
131
|
+
end
|
119
132
|
|
120
|
-
|
121
|
-
|
122
|
-
|
133
|
+
class Comment < ActiveRecord::Base
|
134
|
+
belongs_to :post
|
135
|
+
end
|
136
|
+
```
|
123
137
|
|
124
138
|
Using the inclusive style within the amoeba block actually implies that you wish to enable amoeba, so there is no need to run the enable method, though it won't hurt either:
|
125
139
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
140
|
+
```ruby
|
141
|
+
class Post < ActiveRecord::Base
|
142
|
+
has_many :comments
|
143
|
+
has_many :tags
|
144
|
+
has_many :authors
|
130
145
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
146
|
+
amoeba do
|
147
|
+
include_association :tags
|
148
|
+
include_association :authors
|
149
|
+
end
|
150
|
+
end
|
136
151
|
|
137
|
-
|
138
|
-
|
139
|
-
|
152
|
+
class Comment < ActiveRecord::Base
|
153
|
+
belongs_to :post
|
154
|
+
end
|
155
|
+
```
|
140
156
|
|
141
157
|
You may also specify fields to be copied by passing an array. If you call the `include_association` with a single value, it will be appended to the list of already included fields. If you pass an array, your array will overwrite the original values.
|
142
158
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
159
|
+
```ruby
|
160
|
+
class Post < ActiveRecord::Base
|
161
|
+
has_many :comments
|
162
|
+
has_many :tags
|
163
|
+
has_many :authors
|
147
164
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
165
|
+
amoeba do
|
166
|
+
include_association [:tags, :authors]
|
167
|
+
end
|
168
|
+
end
|
152
169
|
|
153
|
-
|
154
|
-
|
155
|
-
|
170
|
+
class Comment < ActiveRecord::Base
|
171
|
+
belongs_to :post
|
172
|
+
end
|
173
|
+
```
|
156
174
|
|
157
175
|
These examples will copy the post's tags and authors but not its comments.
|
158
176
|
|
159
|
-
The inclusive style, when used, will automatically disable any
|
177
|
+
The inclusive style, when used, will automatically disable any other style that was previously selected.
|
160
178
|
|
161
179
|
#### Exclusive Style
|
162
180
|
|
163
181
|
If you have more fields to include than to exclude, you may wish to shorten the amount of typing and reading you need to do by using the exclusive style. All fields that are not explicitly excluded will be copied:
|
164
182
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
183
|
+
```ruby
|
184
|
+
class Post < ActiveRecord::Base
|
185
|
+
has_many :comments
|
186
|
+
has_many :tags
|
187
|
+
has_many :authors
|
169
188
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
189
|
+
amoeba do
|
190
|
+
exclude_association :comments
|
191
|
+
end
|
192
|
+
end
|
174
193
|
|
175
|
-
|
176
|
-
|
177
|
-
|
194
|
+
class Comment < ActiveRecord::Base
|
195
|
+
belongs_to :post
|
196
|
+
end
|
197
|
+
```
|
178
198
|
|
179
199
|
This example does the same thing as the inclusive style example, it will copy the post's tags and authors but not its comments. As with inclusive style, there is no need to explicitly enable amoeba when specifying fields to exclude.
|
180
200
|
|
181
|
-
The exclusive style, when used, will automatically disable any other style that was previously selected, so if you selected include fields, and then you choose some exclude fields, the `exclude_association` method will disable the previously
|
201
|
+
The exclusive style, when used, will automatically disable any other style that was previously selected, so if you selected include fields, and then you choose some exclude fields, the `exclude_association` method will disable the previously selected inclusive style and wipe out any corresponding include fields.
|
202
|
+
|
203
|
+
#### Conditions
|
204
|
+
|
205
|
+
Also if you need to path extra condition for include or exclude relationship you can path method name to `:if` option.
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
class Post < ActiveRecord::Base
|
209
|
+
has_many :comments
|
210
|
+
has_many :tags
|
211
|
+
|
212
|
+
amoeba do
|
213
|
+
include_association :comments, if: :popular?
|
214
|
+
end
|
215
|
+
|
216
|
+
def popular?
|
217
|
+
likes > 15
|
218
|
+
end
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
After call `Post.first.amoeba_dup` if `likes` is larger 15 than all comments will be duplicated too, but in another situation - no relations will be cloned. Same behavior will be for `exclude_association`.
|
223
|
+
|
224
|
+
**Be aware**! If you wrote:
|
225
|
+
```ruby
|
226
|
+
class Post < ActiveRecord::Base
|
227
|
+
has_many :comments
|
228
|
+
has_many :tags
|
229
|
+
|
230
|
+
amoeba do
|
231
|
+
exclude_association :tags
|
232
|
+
include_association :comments, if: :popular?
|
233
|
+
end
|
234
|
+
|
235
|
+
def popular?
|
236
|
+
likes > 15
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
inclusion strategy will be chosen regardless of the result of `popular?` method call (the same for reverse situation).
|
182
241
|
|
183
242
|
#### Cloning
|
184
243
|
|
185
244
|
If you are using a Many-to-Many relationship, you may tell amoeba to actually make duplicates of the original related records rather than merely maintaining association with the original records. Cloning is easy, merely tell amoeba which fields to clone in the same way you tell it which fields to include or exclude.
|
186
245
|
|
187
|
-
|
188
|
-
|
246
|
+
```ruby
|
247
|
+
class Post < ActiveRecord::Base
|
248
|
+
has_and_belongs_to_many :warnings
|
189
249
|
|
190
|
-
|
191
|
-
|
250
|
+
has_many :post_widgets
|
251
|
+
has_many :widgets, :through => :post_widgets
|
192
252
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
253
|
+
amoeba do
|
254
|
+
enable
|
255
|
+
clone [:widgets, :warnings]
|
256
|
+
end
|
257
|
+
end
|
198
258
|
|
199
|
-
|
200
|
-
|
201
|
-
|
259
|
+
class Warning < ActiveRecord::Base
|
260
|
+
has_and_belongs_to_many :posts
|
261
|
+
end
|
202
262
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
263
|
+
class PostWidget < ActiveRecord::Base
|
264
|
+
belongs_to :widget
|
265
|
+
belongs_to :post
|
266
|
+
end
|
207
267
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
268
|
+
class Widget < ActiveRecord::Base
|
269
|
+
has_many :post_widgets
|
270
|
+
has_many :posts, :through => :post_widgets
|
271
|
+
end
|
272
|
+
```
|
212
273
|
|
213
274
|
This example will actually duplicate the warnings and widgets in the database. If there were originally 3 warnings in the database then, upon duplicating a post, you will end up with 6 warnings in the database. This is in contrast to the default behavior where your new post would merely be re-associated with any previously existing warnings and those warnings themselves would not be duplicated.
|
214
275
|
|
@@ -222,23 +283,25 @@ By default, amoeba recognizes and attempts to copy any children of the following
|
|
222
283
|
|
223
284
|
You may control which association types amoeba applies itself to by using the `recognize` method within the amoeba configuration block.
|
224
285
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
286
|
+
```ruby
|
287
|
+
class Post < ActiveRecord::Base
|
288
|
+
has_one :config
|
289
|
+
has_many :comments
|
290
|
+
has_and_belongs_to_many :tags
|
229
291
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
292
|
+
amoeba do
|
293
|
+
recognize [:has_one, :has_and_belongs_to_many]
|
294
|
+
end
|
295
|
+
end
|
234
296
|
|
235
|
-
|
236
|
-
|
237
|
-
|
297
|
+
class Comment < ActiveRecord::Base
|
298
|
+
belongs_to :post
|
299
|
+
end
|
238
300
|
|
239
|
-
|
240
|
-
|
241
|
-
|
301
|
+
class Tag < ActiveRecord::Base
|
302
|
+
has_and_belongs_to_many :posts
|
303
|
+
end
|
304
|
+
```
|
242
305
|
|
243
306
|
This example will copy the post's configuration data and keep tags associated with the new post, but will not copy the post's comments because amoeba will only recognize and copy children of `has_one` and `has_and_belongs_to_many` associations and in this example, comments are not an `has_and_belongs_to_many` association.
|
244
307
|
|
@@ -248,24 +311,26 @@ This example will copy the post's configuration data and keep tags associated wi
|
|
248
311
|
|
249
312
|
If you wish to prevent a regular (non `has_*` association based) field from retaining it's value when copied, you may "zero out" or "nullify" the field, like this:
|
250
313
|
|
251
|
-
|
252
|
-
|
253
|
-
|
314
|
+
```ruby
|
315
|
+
class Topic < ActiveRecord::Base
|
316
|
+
has_many :posts
|
317
|
+
end
|
254
318
|
|
255
|
-
|
256
|
-
|
257
|
-
|
319
|
+
class Post < ActiveRecord::Base
|
320
|
+
belongs_to :topic
|
321
|
+
has_many :comments
|
258
322
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
323
|
+
amoeba do
|
324
|
+
enable
|
325
|
+
nullify :date_published
|
326
|
+
nullify :topic_id
|
327
|
+
end
|
328
|
+
end
|
265
329
|
|
266
|
-
|
267
|
-
|
268
|
-
|
330
|
+
class Comment < ActiveRecord::Base
|
331
|
+
belongs_to :post
|
332
|
+
end
|
333
|
+
```
|
269
334
|
|
270
335
|
This example will copy all of a post's comments. It will also nullify the publishing date and dissociate the post from its original topic.
|
271
336
|
|
@@ -273,13 +338,15 @@ Unlike inclusive and exclusive styles, specifying null fields will not automatic
|
|
273
338
|
|
274
339
|
#### Set
|
275
340
|
|
276
|
-
If you wish to just set a field to an
|
341
|
+
If you wish to just set a field to an arbitrary value on all duplicated objects you may use the `set` directive. For example, if you wanted to copy an object that has some kind of approval process associated with it, you likely may wish to set the new object's state to be open or "in progress" again.
|
277
342
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
343
|
+
```ruby
|
344
|
+
class Post < ActiveRecord::Base
|
345
|
+
amoeba do
|
346
|
+
set :state_tracker => "open_for_editing"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
```
|
283
350
|
|
284
351
|
In this example, when a post is duplicated, it's `state_tracker` field will always be given a value of `open_for_editing` to start.
|
285
352
|
|
@@ -287,34 +354,40 @@ In this example, when a post is duplicated, it's `state_tracker` field will alwa
|
|
287
354
|
|
288
355
|
You may add a string to the beginning of a copied object's field during the copy phase:
|
289
356
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
357
|
+
```ruby
|
358
|
+
class Post < ActiveRecord::Base
|
359
|
+
amoeba do
|
360
|
+
enable
|
361
|
+
prepend :title => "Copy of "
|
362
|
+
end
|
363
|
+
end
|
364
|
+
```
|
296
365
|
|
297
366
|
#### Append
|
298
367
|
|
299
368
|
You may add a string to the end of a copied object's field during the copy phase:
|
300
369
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
370
|
+
```ruby
|
371
|
+
class Post < ActiveRecord::Base
|
372
|
+
amoeba do
|
373
|
+
enable
|
374
|
+
append :title => "Copy of "
|
375
|
+
end
|
376
|
+
end
|
377
|
+
```
|
307
378
|
|
308
379
|
#### Regex
|
309
380
|
|
310
381
|
You may run a search and replace query on a copied object's field during the copy phase:
|
311
382
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
383
|
+
```ruby
|
384
|
+
class Post < ActiveRecord::Base
|
385
|
+
amoeba do
|
386
|
+
enable
|
387
|
+
regex :contents => {:replace => /dog/, :with => 'cat'}
|
388
|
+
end
|
389
|
+
end
|
390
|
+
```
|
318
391
|
|
319
392
|
#### Custom Methods
|
320
393
|
|
@@ -322,92 +395,104 @@ You may run a search and replace query on a copied object's field during the cop
|
|
322
395
|
|
323
396
|
You may run a custom method or methods to do basically anything you like, simply pass a lambda block, or an array of lambda blocks to the `customize` directive. Each block must have the same form, meaning that each block must accept two parameters, the original object and the newly copied object. You may then do whatever you wish, like this:
|
324
397
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
customize(lambda { |original_post,new_post|
|
330
|
-
if original_post.foo == "bar"
|
331
|
-
new_post.baz = "qux"
|
332
|
-
end
|
333
|
-
})
|
398
|
+
```ruby
|
399
|
+
class Post < ActiveRecord::Base
|
400
|
+
amoeba do
|
401
|
+
prepend :title => "Hello world! "
|
334
402
|
|
335
|
-
|
403
|
+
customize(lambda { |original_post,new_post|
|
404
|
+
if original_post.foo == "bar"
|
405
|
+
new_post.baz = "qux"
|
336
406
|
end
|
337
|
-
|
407
|
+
})
|
408
|
+
|
409
|
+
append :comments => "... know what I'm sayin?"
|
410
|
+
end
|
411
|
+
end
|
412
|
+
```
|
338
413
|
|
339
414
|
or this, using an array:
|
340
415
|
|
341
|
-
|
342
|
-
|
416
|
+
```ruby
|
417
|
+
class Post < ActiveRecord::Base
|
418
|
+
has_and_belongs_to_many :tags
|
343
419
|
|
344
|
-
|
345
|
-
|
420
|
+
amoeba do
|
421
|
+
include_association :tags
|
346
422
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
423
|
+
customize([
|
424
|
+
lambda do |orig_obj,copy_of_obj|
|
425
|
+
# good stuff goes here
|
426
|
+
end,
|
351
427
|
|
352
|
-
|
353
|
-
|
354
|
-
end
|
355
|
-
])
|
428
|
+
lambda do |orig_obj,copy_of_obj|
|
429
|
+
# more good stuff goes here
|
356
430
|
end
|
357
|
-
|
431
|
+
])
|
432
|
+
end
|
433
|
+
end
|
434
|
+
```
|
358
435
|
|
359
436
|
##### Override
|
360
437
|
|
361
438
|
Lambda blocks passed to customize run, by default, after all copying and field pre-processing. If you wish to run a method before any customization or field pre-processing, you may use `override` the cousin of `customize`. Usage is the same as above.
|
362
439
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
override(lambda { |original_post,new_post|
|
368
|
-
if original_post.foo == "bar"
|
369
|
-
new_post.baz = "qux"
|
370
|
-
end
|
371
|
-
})
|
440
|
+
```ruby
|
441
|
+
class Post < ActiveRecord::Base
|
442
|
+
amoeba do
|
443
|
+
prepend :title => "Hello world! "
|
372
444
|
|
373
|
-
|
445
|
+
override(lambda { |original_post,new_post|
|
446
|
+
if original_post.foo == "bar"
|
447
|
+
new_post.baz = "qux"
|
374
448
|
end
|
375
|
-
|
449
|
+
})
|
450
|
+
|
451
|
+
append :comments => "... know what I'm sayin?"
|
452
|
+
end
|
453
|
+
end
|
454
|
+
```
|
376
455
|
|
377
456
|
#### Chaining
|
378
457
|
|
379
458
|
You may apply a single preprocessor to multiple fields at once.
|
380
459
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
460
|
+
```ruby
|
461
|
+
class Post < ActiveRecord::Base
|
462
|
+
amoeba do
|
463
|
+
enable
|
464
|
+
prepend :title => "Copy of ", :contents => "Copied contents: "
|
465
|
+
end
|
466
|
+
end
|
467
|
+
```
|
387
468
|
|
388
469
|
#### Stacking
|
389
470
|
|
390
|
-
You may apply multiple
|
471
|
+
You may apply multiple preprocessing directives to a single model at once.
|
391
472
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
473
|
+
```ruby
|
474
|
+
class Post < ActiveRecord::Base
|
475
|
+
amoeba do
|
476
|
+
prepend :title => "Copy of ", :contents => "Original contents: "
|
477
|
+
append :contents => " (copied version)"
|
478
|
+
regex :contents => {:replace => /dog/, :with => 'cat'}
|
479
|
+
end
|
480
|
+
end
|
481
|
+
```
|
399
482
|
|
400
483
|
This example should result in something like this:
|
401
484
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
485
|
+
```ruby
|
486
|
+
post = Post.create(
|
487
|
+
:title => "Hello world",
|
488
|
+
:contents => "I like dogs, dogs are awesome."
|
489
|
+
)
|
406
490
|
|
407
|
-
|
491
|
+
new_post = post.amoeba_dup
|
408
492
|
|
409
|
-
|
410
|
-
|
493
|
+
new_post.title # "Copy of Hello world"
|
494
|
+
new_post.contents # "Original contents: I like cats, cats are awesome. (copied version)"
|
495
|
+
```
|
411
496
|
|
412
497
|
Like `nullify`, the preprocessing directives do not automatically enable the copying of associated child records. If only preprocessing directives are used and you do want to copy child records and no `include_association` or `exclude_association` list is provided, you must still explicitly enable the copying of child records by calling the enable method from within the amoeba block on your model.
|
413
498
|
|
@@ -417,38 +502,40 @@ You may use a combination of configuration methods within each model's amoeba bl
|
|
417
502
|
|
418
503
|
The following example syntax is perfectly valid, and will result in the usage of inclusive style. The order in which you call the configuration methods within the amoeba block does not matter:
|
419
504
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
505
|
+
```ruby
|
506
|
+
class Topic < ActiveRecord::Base
|
507
|
+
has_many :posts
|
508
|
+
end
|
509
|
+
|
510
|
+
class Post < ActiveRecord::Base
|
511
|
+
belongs_to :topic
|
512
|
+
has_many :comments
|
513
|
+
has_many :tags
|
514
|
+
has_many :authors
|
515
|
+
|
516
|
+
amoeba do
|
517
|
+
exclude_association :authors
|
518
|
+
include_association :tags
|
519
|
+
nullify :date_published
|
520
|
+
prepend :title => "Copy of "
|
521
|
+
append :contents => " (copied version)"
|
522
|
+
regex :contents => {:replace => /dog/, :with => 'cat'}
|
523
|
+
include_association :authors
|
524
|
+
enable
|
525
|
+
nullify :topic_id
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
class Comment < ActiveRecord::Base
|
530
|
+
belongs_to :post
|
531
|
+
end
|
532
|
+
```
|
446
533
|
|
447
534
|
This example will copy all of a post's tags and authors, but not its comments. It will also nullify the publishing date and dissociate the post from its original topic. It will also preprocess the post's fields as in the previous preprocessing example.
|
448
535
|
|
449
536
|
Note that, because of precedence, inclusive style is used and the list of exclude fields is never consulted. Additionally, the `enable` method is redundant because amoeba is automatically enabled when using `include_association`.
|
450
537
|
|
451
|
-
The preprocessing directives are run after child records are copied
|
538
|
+
The preprocessing directives are run after child records are copied and are run in this order.
|
452
539
|
|
453
540
|
1. Null fields
|
454
541
|
2. Prepends
|
@@ -461,26 +548,28 @@ Preprocessing directives do not affect inclusion and exclusion lists.
|
|
461
548
|
|
462
549
|
You may cause amoeba to keep copying down the chain as far as you like, simply add amoeba blocks to each model you wish to have copy its children. Amoeba will automatically recurse into any enabled grandchildren and copy them as well.
|
463
550
|
|
464
|
-
|
465
|
-
|
551
|
+
```ruby
|
552
|
+
class Post < ActiveRecord::Base
|
553
|
+
has_many :comments
|
466
554
|
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
555
|
+
amoeba do
|
556
|
+
enable
|
557
|
+
end
|
558
|
+
end
|
471
559
|
|
472
|
-
|
473
|
-
|
474
|
-
|
560
|
+
class Comment < ActiveRecord::Base
|
561
|
+
belongs_to :post
|
562
|
+
has_many :ratings
|
475
563
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
564
|
+
amoeba do
|
565
|
+
enable
|
566
|
+
end
|
567
|
+
end
|
480
568
|
|
481
|
-
|
482
|
-
|
483
|
-
|
569
|
+
class Rating < ActiveRecord::Base
|
570
|
+
belongs_to :comment
|
571
|
+
end
|
572
|
+
```
|
484
573
|
|
485
574
|
In this example, when a post is copied, amoeba will copy each all of a post's comments and will also copy each comment's ratings.
|
486
575
|
|
@@ -488,142 +577,150 @@ In this example, when a post is copied, amoeba will copy each all of a post's co
|
|
488
577
|
|
489
578
|
Using the `has_one :through` association is simple, just be sure to enable amoeba on the each model with a `has_one` association and amoeba will automatically and recursively drill down, like so:
|
490
579
|
|
491
|
-
|
492
|
-
|
493
|
-
|
580
|
+
```ruby
|
581
|
+
class Supplier < ActiveRecord::Base
|
582
|
+
has_one :account
|
583
|
+
has_one :history, :through => :account
|
494
584
|
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
585
|
+
amoeba do
|
586
|
+
enable
|
587
|
+
end
|
588
|
+
end
|
499
589
|
|
500
|
-
|
501
|
-
|
502
|
-
|
590
|
+
class Account < ActiveRecord::Base
|
591
|
+
belongs_to :supplier
|
592
|
+
has_one :history
|
503
593
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
594
|
+
amoeba do
|
595
|
+
enable
|
596
|
+
end
|
597
|
+
end
|
508
598
|
|
509
|
-
|
510
|
-
|
511
|
-
|
599
|
+
class History < ActiveRecord::Base
|
600
|
+
belongs_to :account
|
601
|
+
end
|
602
|
+
```
|
512
603
|
|
513
604
|
### Has Many Through
|
514
605
|
|
515
606
|
Copying of `has_many :through` associations works automatically. They perform the copy in the same way as the `has_and_belongs_to_many` association, meaning the actual child records are not copied, but rather the associations are simply maintained. You can add some field preprocessors to the middle model if you like but this is not strictly necessary:
|
516
607
|
|
517
|
-
|
518
|
-
|
519
|
-
|
608
|
+
```ruby
|
609
|
+
class Assembly < ActiveRecord::Base
|
610
|
+
has_many :manifests
|
611
|
+
has_many :parts, :through => :manifests
|
520
612
|
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
613
|
+
amoeba do
|
614
|
+
enable
|
615
|
+
end
|
616
|
+
end
|
525
617
|
|
526
|
-
|
527
|
-
|
528
|
-
|
618
|
+
class Manifest < ActiveRecord::Base
|
619
|
+
belongs_to :assembly
|
620
|
+
belongs_to :part
|
529
621
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
622
|
+
amoeba do
|
623
|
+
prepend :notes => "Copy of "
|
624
|
+
end
|
625
|
+
end
|
534
626
|
|
535
|
-
|
536
|
-
|
537
|
-
|
627
|
+
class Part < ActiveRecord::Base
|
628
|
+
has_many :manifests
|
629
|
+
has_many :assemblies, :through => :manifests
|
538
630
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
631
|
+
amoeba do
|
632
|
+
enable
|
633
|
+
end
|
634
|
+
end
|
635
|
+
```
|
543
636
|
|
544
637
|
### On The Fly Configuration
|
545
638
|
|
546
639
|
You may control how amoeba copies your object, on the fly, by passing a configuration block to the model's amoeba method. The configuration method is static but the configuration is applied on a per instance basis.
|
547
640
|
|
548
|
-
|
549
|
-
|
641
|
+
```ruby
|
642
|
+
class Post < ActiveRecord::Base
|
643
|
+
has_many :comments
|
550
644
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
645
|
+
amoeba do
|
646
|
+
enable
|
647
|
+
prepend :title => "Copy of "
|
648
|
+
end
|
649
|
+
end
|
556
650
|
|
557
|
-
|
558
|
-
|
559
|
-
|
651
|
+
class Comment < ActiveRecord::Base
|
652
|
+
belongs_to :post
|
653
|
+
end
|
560
654
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
655
|
+
class PostsController < ActionController
|
656
|
+
def duplicate_a_post
|
657
|
+
old_post = Post.create(
|
658
|
+
:title => "Hello world",
|
659
|
+
:contents => "Lorum ipsum"
|
660
|
+
)
|
567
661
|
|
568
|
-
|
569
|
-
|
570
|
-
|
662
|
+
old_post.class.amoeba do
|
663
|
+
prepend :contents => "Here's a copy: "
|
664
|
+
end
|
571
665
|
|
572
|
-
|
666
|
+
new_post = old_post.amoeba_dup
|
573
667
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
668
|
+
new_post.title # should be "Copy of Hello world"
|
669
|
+
new_post.contents # should be "Here's a copy: Lorum ipsum"
|
670
|
+
new_post.save
|
671
|
+
end
|
672
|
+
end
|
673
|
+
```
|
579
674
|
|
580
675
|
### Inheritance
|
581
676
|
|
582
677
|
If you are using the Single Table Inheritance provided by ActiveRecord, you may cause amoeba to automatically process child classes in the same way as their parents. All you need to do is call the `propagate` method within the amoeba block of the parent class and all child classes should copy in a similar manner.
|
583
678
|
|
584
|
-
|
585
|
-
|
679
|
+
```ruby
|
680
|
+
create_table :products, :force => true do |t|
|
681
|
+
t.string :type # this is the STI column
|
586
682
|
|
587
|
-
|
588
|
-
|
589
|
-
|
683
|
+
# these belong to all products
|
684
|
+
t.string :title
|
685
|
+
t.decimal :price
|
590
686
|
|
591
|
-
|
592
|
-
|
593
|
-
|
687
|
+
# these are for shirts only
|
688
|
+
t.decimal :sleeve_length
|
689
|
+
t.decimal :collar_size
|
594
690
|
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
691
|
+
# these are for computers only
|
692
|
+
t.integer :ram_size
|
693
|
+
t.integer :hard_drive_size
|
694
|
+
end
|
599
695
|
|
600
|
-
|
601
|
-
|
602
|
-
|
696
|
+
class Product < ActiveRecord::Base
|
697
|
+
has_many :images
|
698
|
+
has_and_belongs_to_many :categories
|
603
699
|
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
700
|
+
amoeba do
|
701
|
+
enable
|
702
|
+
propagate
|
703
|
+
end
|
704
|
+
end
|
609
705
|
|
610
|
-
|
611
|
-
|
706
|
+
class Shirt < Product
|
707
|
+
end
|
612
708
|
|
613
|
-
|
614
|
-
|
709
|
+
class Computer < Product
|
710
|
+
end
|
615
711
|
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
712
|
+
class ProductsController
|
713
|
+
def some_method
|
714
|
+
my_shirt = Shirt.find(1)
|
715
|
+
my_shirt.amoeba_dup
|
716
|
+
my_shirt.save
|
621
717
|
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
718
|
+
# this shirt should now:
|
719
|
+
# - have its own copy of all parent images
|
720
|
+
# - be in the same categories as the parent
|
721
|
+
end
|
722
|
+
end
|
723
|
+
```
|
627
724
|
|
628
725
|
This example should duplicate all the images and sections associated with this Shirt, which is a child of Product
|
629
726
|
|
@@ -637,21 +734,23 @@ You may change this behavior, the so called "parenting style", to give preferenc
|
|
637
734
|
|
638
735
|
The `:relaxed` parenting style will prefer parent settings.
|
639
736
|
|
640
|
-
|
641
|
-
|
642
|
-
|
737
|
+
```ruby
|
738
|
+
class Product < ActiveRecord::Base
|
739
|
+
has_many :images
|
740
|
+
has_and_belongs_to_many :sections
|
643
741
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
742
|
+
amoeba do
|
743
|
+
exclude_association :images
|
744
|
+
propagate :relaxed
|
745
|
+
end
|
746
|
+
end
|
649
747
|
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
748
|
+
class Shirt < Product
|
749
|
+
include_association :images
|
750
|
+
include_association :sections
|
751
|
+
prepend :title => "Copy of "
|
752
|
+
end
|
753
|
+
```
|
655
754
|
|
656
755
|
In this example, the conflicting `include_association` settings on the child will be ignored and the parent `exclude_association` setting will be used, while the `prepend` setting on the child will be honored because it doesn't conflict with the parent.
|
657
756
|
|
@@ -659,21 +758,23 @@ In this example, the conflicting `include_association` settings on the child wil
|
|
659
758
|
|
660
759
|
The `:strict` style will ignore child settings altogether and inherit any parent settings.
|
661
760
|
|
662
|
-
|
663
|
-
|
664
|
-
|
761
|
+
```ruby
|
762
|
+
class Product < ActiveRecord::Base
|
763
|
+
has_many :images
|
764
|
+
has_and_belongs_to_many :sections
|
665
765
|
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
766
|
+
amoeba do
|
767
|
+
exclude_association :images
|
768
|
+
propagate :strict
|
769
|
+
end
|
770
|
+
end
|
671
771
|
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
772
|
+
class Shirt < Product
|
773
|
+
include_association :images
|
774
|
+
include_association :sections
|
775
|
+
prepend :title => "Copy of "
|
776
|
+
end
|
777
|
+
```
|
677
778
|
|
678
779
|
In this example, the only processing that will happen when a Shirt is duplicated is whatever processing is allowed by the parent. So in this case the parent's `exclude_association` directive takes precedence over the child's `include_association` settings, and not only that, but none of the other settings for the child are used either. The `prepend` setting of the child is completely ignored.
|
679
780
|
|
@@ -695,69 +796,77 @@ This means that, for example:
|
|
695
796
|
|
696
797
|
This version will use both the parent and child settings, so both the images and sections will be copied.
|
697
798
|
|
698
|
-
|
699
|
-
|
700
|
-
|
799
|
+
```ruby
|
800
|
+
class Product < ActiveRecord::Base
|
801
|
+
has_many :images
|
802
|
+
has_and_belongs_to_many :sections
|
701
803
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
804
|
+
amoeba do
|
805
|
+
include_association :images
|
806
|
+
propagate
|
807
|
+
end
|
808
|
+
end
|
707
809
|
|
708
|
-
|
709
|
-
|
710
|
-
|
810
|
+
class Shirt < Product
|
811
|
+
include_association :sections
|
812
|
+
end
|
813
|
+
```
|
711
814
|
|
712
815
|
The next version will use only the child settings because passing an array will override any previous settings rather than adding to them and the child config takes precedence in the `submissive` parenting style. So in this case only the sections will be copied.
|
713
816
|
|
714
|
-
|
715
|
-
|
716
|
-
|
817
|
+
```ruby
|
818
|
+
class Product < ActiveRecord::Base
|
819
|
+
has_many :images
|
820
|
+
has_and_belongs_to_many :sections
|
717
821
|
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
822
|
+
amoeba do
|
823
|
+
include_association :images
|
824
|
+
propagate
|
825
|
+
end
|
826
|
+
end
|
723
827
|
|
724
|
-
|
725
|
-
|
726
|
-
|
828
|
+
class Shirt < Product
|
829
|
+
include_association [:sections]
|
830
|
+
end
|
831
|
+
```
|
727
832
|
|
728
833
|
##### A Relaxed Override Example
|
729
834
|
|
730
835
|
This version will use both the parent and child settings, so both the images and sections will be copied.
|
731
836
|
|
732
|
-
|
733
|
-
|
734
|
-
|
837
|
+
```ruby
|
838
|
+
class Product < ActiveRecord::Base
|
839
|
+
has_many :images
|
840
|
+
has_and_belongs_to_many :sections
|
735
841
|
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
842
|
+
amoeba do
|
843
|
+
include_association :images
|
844
|
+
propagate :relaxed
|
845
|
+
end
|
846
|
+
end
|
741
847
|
|
742
|
-
|
743
|
-
|
744
|
-
|
848
|
+
class Shirt < Product
|
849
|
+
include_association :sections
|
850
|
+
end
|
851
|
+
```
|
745
852
|
|
746
853
|
The next version will use only the parent settings because passing an array will override any previous settings rather than adding to them and the parent config takes precedence in the `relaxed` parenting style. So in this case only the images will be copied.
|
747
854
|
|
748
|
-
|
749
|
-
|
750
|
-
|
855
|
+
```ruby
|
856
|
+
class Product < ActiveRecord::Base
|
857
|
+
has_many :images
|
858
|
+
has_and_belongs_to_many :sections
|
751
859
|
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
860
|
+
amoeba do
|
861
|
+
include_association [:images]
|
862
|
+
propagate
|
863
|
+
end
|
864
|
+
end
|
757
865
|
|
758
|
-
|
759
|
-
|
760
|
-
|
866
|
+
class Shirt < Product
|
867
|
+
include_association :sections
|
868
|
+
end
|
869
|
+
```
|
761
870
|
|
762
871
|
### Validating Nested Attributes
|
763
872
|
|
@@ -765,81 +874,89 @@ If you end up with some validation issues when trying to validate the presence o
|
|
765
874
|
|
766
875
|
For example this will throw a validation error saying that your posts are invalid:
|
767
876
|
|
768
|
-
|
769
|
-
|
877
|
+
```ruby
|
878
|
+
class Author < ActiveRecord::Base
|
879
|
+
has_many :posts
|
770
880
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
881
|
+
amoeba do
|
882
|
+
enable
|
883
|
+
end
|
884
|
+
end
|
775
885
|
|
776
|
-
|
777
|
-
|
778
|
-
|
886
|
+
class Post < ActiveRecord::Base
|
887
|
+
belongs_to :author
|
888
|
+
validates_presence_of :author
|
779
889
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
890
|
+
amoeba do
|
891
|
+
enable
|
892
|
+
end
|
893
|
+
end
|
784
894
|
|
785
|
-
|
786
|
-
|
895
|
+
author = Author.find(1)
|
896
|
+
author.amoeba_dup
|
787
897
|
|
788
|
-
|
898
|
+
author.save # this will fail validation
|
899
|
+
```
|
789
900
|
|
790
901
|
Where this will work fine:
|
791
902
|
|
792
|
-
|
793
|
-
|
903
|
+
```ruby
|
904
|
+
class Author < ActiveRecord::Base
|
905
|
+
has_many :posts, :inverse_of => :author
|
794
906
|
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
907
|
+
amoeba do
|
908
|
+
enable
|
909
|
+
end
|
910
|
+
end
|
799
911
|
|
800
|
-
|
801
|
-
|
802
|
-
|
912
|
+
class Post < ActiveRecord::Base
|
913
|
+
belongs_to :author, :inverse_of => :posts
|
914
|
+
validates_presence_of :author
|
803
915
|
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
916
|
+
amoeba do
|
917
|
+
enable
|
918
|
+
end
|
919
|
+
end
|
808
920
|
|
809
|
-
|
810
|
-
|
921
|
+
author = Author.find(1)
|
922
|
+
author.amoeba_dup
|
811
923
|
|
812
|
-
|
924
|
+
author.save # this will pass validation
|
925
|
+
```
|
813
926
|
|
814
927
|
This issue is not amoeba specific and also occurs when creating new objects using `accepts_nested_attributes_for`, like this:
|
815
928
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
929
|
+
```ruby
|
930
|
+
class Author < ActiveRecord::Base
|
931
|
+
has_many :posts
|
932
|
+
accepts_nested_attributes_for :posts
|
933
|
+
end
|
820
934
|
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
935
|
+
class Post < ActiveRecord::Base
|
936
|
+
belongs_to :author
|
937
|
+
validates_presence_of :author
|
938
|
+
end
|
825
939
|
|
826
|
-
|
827
|
-
|
940
|
+
# this will fail validation
|
941
|
+
author = Author.create({:name => "Jim Smith", :posts => [{:title => "Hello World", :contents => "Lorum ipsum dolor}]})
|
942
|
+
```
|
828
943
|
|
829
944
|
This issue with `accepts_nested_attributes_for` can also be solved by using `:inverse_of`, like this:
|
830
945
|
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
946
|
+
```ruby
|
947
|
+
class Author < ActiveRecord::Base
|
948
|
+
has_many :posts, :inverse_of => :author
|
949
|
+
accepts_nested_attributes_for :posts
|
950
|
+
end
|
835
951
|
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
952
|
+
class Post < ActiveRecord::Base
|
953
|
+
belongs_to :author, :inverse_of => :posts
|
954
|
+
validates_presence_of :author
|
955
|
+
end
|
840
956
|
|
841
|
-
|
842
|
-
|
957
|
+
# this will pass validation
|
958
|
+
author = Author.create({:name => "Jim Smith", :posts => [{:title => "Hello World", :contents => "Lorum ipsum dolor}]})
|
959
|
+
```
|
843
960
|
|
844
961
|
The crux of the issue is that upon duplication, the new `Author` instance does not yet have an ID because it has not yet been persisted, so the `:posts` do not yet have an `:author_id` either, and thus no `:author` and thus they will fail validation. This issue may likely affect amoeba usage so if you get some validation failures, be sure to add `:inverse_of` to your models.
|
845
962
|
|
@@ -848,18 +965,20 @@ The crux of the issue is that upon duplication, the new `Author` instance does n
|
|
848
965
|
|
849
966
|
If you need to clone model with custom method you can use `through`:
|
850
967
|
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
968
|
+
```ruby
|
969
|
+
class ChildPrototype < ActiveRecord::Base
|
970
|
+
amoeba do
|
971
|
+
through :become_child
|
972
|
+
end
|
855
973
|
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
974
|
+
def become_child
|
975
|
+
self.dup.becomes(Child)
|
976
|
+
end
|
977
|
+
end
|
860
978
|
|
861
|
-
|
862
|
-
|
979
|
+
class Child < ChildPrototype
|
980
|
+
end
|
981
|
+
```
|
863
982
|
|
864
983
|
After cloning we will get instance of `Child` instead of `ChildPrototype`
|
865
984
|
|
@@ -867,47 +986,51 @@ After cloning we will get instance of `Child` instead of `ChildPrototype`
|
|
867
986
|
|
868
987
|
If you will need to do complex cloning with remapping associations name you can user `remapper`:
|
869
988
|
|
870
|
-
|
871
|
-
|
989
|
+
```ruby
|
990
|
+
class ObjectPrototype < ActiveRecord::Base
|
991
|
+
has_many :child_prototypes
|
872
992
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
993
|
+
amoeba do
|
994
|
+
method :become_real
|
995
|
+
remapper :remap_associations
|
996
|
+
end
|
877
997
|
|
878
|
-
|
879
|
-
|
880
|
-
|
998
|
+
def become_real
|
999
|
+
self.dup().becomes( RealObject )
|
1000
|
+
end
|
881
1001
|
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
1002
|
+
def remap_associations( name )
|
1003
|
+
:childs if name == :child_prototypes
|
1004
|
+
end
|
1005
|
+
end
|
886
1006
|
|
887
|
-
|
888
|
-
|
889
|
-
|
1007
|
+
class RealObject < ObjectPrototype
|
1008
|
+
has_many :childs
|
1009
|
+
end
|
890
1010
|
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
1011
|
+
class ChildPrototype < ActiveRecord::Base
|
1012
|
+
amoeba do
|
1013
|
+
method :become_child
|
1014
|
+
end
|
895
1015
|
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
1016
|
+
def become_child
|
1017
|
+
self.dup().becomes( Child )
|
1018
|
+
end
|
1019
|
+
end
|
900
1020
|
|
901
|
-
|
902
|
-
|
1021
|
+
class Child < ChildPrototype
|
1022
|
+
end
|
1023
|
+
```
|
903
1024
|
|
904
1025
|
In result we will get next:
|
905
1026
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
1027
|
+
```ruby
|
1028
|
+
prototype = ObjectPrototype.new
|
1029
|
+
prototype.child_prototypes << ChildPrototype.new
|
1030
|
+
object = prototype.amoeba_dup
|
1031
|
+
object.class # => RealObject
|
1032
|
+
object.childs.first.class #=> Child
|
1033
|
+
```
|
911
1034
|
|
912
1035
|
## Configuration Reference
|
913
1036
|
|
@@ -919,14 +1042,16 @@ Here is a static reference to the available configuration methods, usable within
|
|
919
1042
|
|
920
1043
|
for example:
|
921
1044
|
|
922
|
-
|
923
|
-
|
924
|
-
|
1045
|
+
```ruby
|
1046
|
+
amoeba do
|
1047
|
+
through :supper_pupper_dup
|
1048
|
+
end
|
925
1049
|
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
1050
|
+
def supper_pupper_dup
|
1051
|
+
puts "multiplied by budding"
|
1052
|
+
self.dup
|
1053
|
+
end
|
1054
|
+
```
|
930
1055
|
|
931
1056
|
### Controlling Associations
|
932
1057
|
|
@@ -954,9 +1079,11 @@ Here is a static reference to the available configuration methods, usable within
|
|
954
1079
|
|
955
1080
|
for example
|
956
1081
|
|
957
|
-
|
958
|
-
|
959
|
-
|
1082
|
+
```ruby
|
1083
|
+
amoeba do
|
1084
|
+
propagate :strict
|
1085
|
+
end
|
1086
|
+
```
|
960
1087
|
|
961
1088
|
will choose the strict parenting style of inherited settings.
|
962
1089
|
|
@@ -968,9 +1095,11 @@ Here is a static reference to the available configuration methods, usable within
|
|
968
1095
|
|
969
1096
|
for example:
|
970
1097
|
|
971
|
-
|
972
|
-
|
973
|
-
|
1098
|
+
```ruby
|
1099
|
+
amoeba do
|
1100
|
+
raised :relaxed
|
1101
|
+
end
|
1102
|
+
```
|
974
1103
|
|
975
1104
|
will choose the relaxed parenting style of inherited settings for this child. A parenting style set via the `raised` method takes precedence over the parenting style set using the `propagate` method.
|
976
1105
|
|
@@ -979,13 +1108,16 @@ Here is a static reference to the available configuration methods, usable within
|
|
979
1108
|
Set the method what will be used for remapping of association name. Method will have one argument - association name as Symbol. If method will return nil then association will not be remapped.
|
980
1109
|
|
981
1110
|
for example:
|
982
|
-
amoeba do
|
983
|
-
remapper :childs_to_parents
|
984
|
-
end
|
985
1111
|
|
986
|
-
|
987
|
-
|
988
|
-
|
1112
|
+
```ruby
|
1113
|
+
amoeba do
|
1114
|
+
remapper :childs_to_parents
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
def childs_to_parents(association_name)
|
1118
|
+
:parents if association_name == :childs
|
1119
|
+
end
|
1120
|
+
```
|
989
1121
|
|
990
1122
|
### Pre-Processing Fields
|
991
1123
|
|
@@ -1003,7 +1135,7 @@ Here is a static reference to the available configuration methods, usable within
|
|
1003
1135
|
|
1004
1136
|
#### set
|
1005
1137
|
|
1006
|
-
Set a field to a given value. This
|
1138
|
+
Set a field to a given value. This should work for almost any type of field. Accepts a hash of fields and the values you want them set to.. The keys are the field names and the values are the prefix strings. An example would be to add " (copied version)" to your description field. Don't forget to add a leading space if you want it. Passing a hash will add each key value pair to the list of append directives. If you wish to empty the list of directives, you may pass the hash inside of an array like this `[{:approval_state => "open_for_editing"}]`.
|
1007
1139
|
|
1008
1140
|
#### regex
|
1009
1141
|
|
@@ -1033,7 +1165,9 @@ The behavior when copying polymorphic `has_many` associations is also undefined.
|
|
1033
1165
|
|
1034
1166
|
You may run the rspec tests like this:
|
1035
1167
|
|
1036
|
-
|
1168
|
+
```sh
|
1169
|
+
bundle exec rspec spec
|
1170
|
+
```
|
1037
1171
|
|
1038
1172
|
### TODO
|
1039
1173
|
|