amoeba 1.2.1 → 3.2.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.
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/amoeba.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "amoeba/version"
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'amoeba/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "amoeba"
6
+ s.name = 'amoeba'
7
7
  s.version = Amoeba::VERSION
8
- s.authors = ["Vaughn Draughon"]
9
- s.email = "vaughn@rocksolidwebdesign.com"
10
- s.homepage = "http://github.com/rocksolidwebdesign/amoeba"
11
- s.license = "BSD"
12
- s.summary = %q{Easy copying of rails models and their child associations.}
8
+ s.authors = ['Vaughn Draughon', 'Oleksandr Simonov']
9
+ s.email = 'alex@simonov.me'
10
+ s.homepage = 'http://github.com/amoeba-rb/amoeba'
11
+ s.license = 'BSD'
12
+ s.summary = 'Easy copying of rails models and their child associations.'
13
13
 
14
14
  s.description = <<-EOF
15
15
  An extension to ActiveRecord to allow the duplication method to also copy associated children, with recursive support for nested of grandchildren. The behavior is controllable with a simple DSL both on your rails models and on the fly, i.e. per instance. Numerous configuration styles and preprocessing directives are included for power and flexibility. Supports preprocessing of field values to prepend strings such as "Copy of ", to nullify or process field values with regular expressions. Supports most association types including has_one :through and has_many :through.
@@ -17,18 +17,23 @@ An extension to ActiveRecord to allow the duplication method to also copy associ
17
17
  Tags: copy child associations, copy nested children, copy associated child records, nested copy, copy associations, copy relations, copy relationships, duplicate associations, duplicate associated records, duplicate child records, duplicate children, copy all, duplicate all, clone child associations, clone nested children, clone associated child records, nested clone, clone associations, clone relations, clone relationships, cloning child associations, cloning nested children, cloning associated child records, deep_cloning, nested cloning, cloning associations, cloning relations, cloning relationships, cloning child associations, cloning nested children, cloning associated child records, nested cloning, cloning associations, cloning relations, cloning relationships, cloning child associations, cloning nested children, cloning associated child records, deep_cloning, nested cloning, cloning associations, cloning relations, cloning relationships, duplicate child associations, duplicate nested children, duplicate associated child records, nested duplicate, duplicate associations, duplicate relations, duplicate relationships, duplicate child associations, duplicate nested children, duplicate associated child records, deep_duplicate, nested duplicate, duplicate associations, duplicate relations, duplicate relationships, deep_copy, deep_clone, deep_cloning, deep clone, deep cloning, has_one, has_many, has_and_belongs_to_many
18
18
  EOF
19
19
 
20
- s.rubyforge_project = "amoeba"
20
+ s.rubyforge_project = 'amoeba'
21
21
 
22
22
  s.files = `git ls-files`.split("\n")
23
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
- s.require_paths = ["lib"]
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
25
+ s.require_paths = ['lib']
26
26
 
27
27
  # specify any dependencies here; for example:
28
- s.add_development_dependency "bundler", ">= 1.0.0"
29
- s.add_development_dependency "rspec", "~> 2.3"
28
+ s.add_development_dependency 'bundler', '>= 1.6.0'
29
+ s.add_development_dependency 'rspec', '>= 3.0.0'
30
30
 
31
- s.add_development_dependency "sqlite3"
31
+ if RUBY_PLATFORM == 'java'
32
+ s.add_development_dependency 'activerecord-jdbc-adapter', '~> 61.0'
33
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 61.0'
34
+ else
35
+ s.add_development_dependency 'sqlite3', '>= 1.3'
36
+ end
32
37
 
33
- s.add_dependency "activerecord", ">= 3.0"
38
+ s.add_dependency 'activerecord', '>= 4.2.0'
34
39
  end
data/defaults.reek ADDED
@@ -0,0 +1,11 @@
1
+ ---
2
+ NestedIterators:
3
+ max_allowed_nesting: 2
4
+ UtilityFunction:
5
+ enabled: false
6
+ IrresponsibleModule:
7
+ enabled: false
8
+ DuplicateMethodCall:
9
+ max_calls: 5
10
+ FeatureEnvy:
11
+ enabled: false
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "sqlite3", "~> 1.3.0"
11
+ end
12
+
13
+ group :local_development do
14
+ gem "pry"
15
+ gem "appraisal"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.0.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "sqlite3", "~> 1.3.0"
11
+ end
12
+
13
+ group :local_development do
14
+ gem "pry"
15
+ gem "appraisal"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.1.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "sqlite3", "~> 1.3.0"
11
+ end
12
+
13
+ group :local_development do
14
+ gem "pry"
15
+ gem "appraisal"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.2.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "sqlite3", "~> 1.3.0"
11
+ end
12
+
13
+ group :local_development do
14
+ gem "pry"
15
+ gem "appraisal"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.0.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "sqlite3", "~> 1.4.0"
11
+ end
12
+
13
+ group :local_development do
14
+ gem "pry"
15
+ gem "appraisal"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.1.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "sqlite3", "~> 1.4.0"
11
+ end
12
+
13
+ group :local_development do
14
+ gem "pry"
15
+ gem "appraisal"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,24 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git "git://github.com/rails/arel.git" do
6
+ gem "arel"
7
+ end
8
+
9
+ git "git://github.com/rails/rails.git", branch: "main" do
10
+ gem "activerecord"
11
+ end
12
+
13
+ group :development, :test do
14
+ gem "rake"
15
+ gem "coveralls", require: false
16
+ gem "sqlite3", "~> 1.4.0"
17
+ end
18
+
19
+ group :local_development do
20
+ gem "pry"
21
+ gem "appraisal"
22
+ end
23
+
24
+ gemspec path: "../"
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.1.0"
6
+
7
+ group :development, :test do
8
+ gem "rake"
9
+ gem "coveralls", require: false
10
+ gem "activerecord-jdbc-adapter", "~> 61.0"
11
+ gem "activerecord-jdbcsqlite3-adapter", "~> 61.0"
12
+ end
13
+
14
+ group :local_development do
15
+ gem "pry"
16
+ gem "appraisal"
17
+ end
18
+
19
+ gemspec path: "../"
@@ -0,0 +1,28 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git "git://github.com/rails/arel.git" do
6
+ gem "arel"
7
+ end
8
+
9
+ git "git://github.com/rails/rails.git", branch: "main" do
10
+ gem "activerecord"
11
+ end
12
+
13
+ group :development, :test do
14
+ git "git://github.com/jruby/activerecord-jdbc-adapter" do
15
+ gem "activerecord-jdbc-adapter"
16
+ gem "activerecord-jdbcsqlite3-adapter", glob: "activerecord-jdbcsqlite3-adapter/activerecord-jdbcsqlite3-adapter.gemspec"
17
+ end
18
+
19
+ gem "rake"
20
+ gem "coveralls", require: false
21
+ end
22
+
23
+ group :local_development do
24
+ gem "pry"
25
+ gem "appraisal"
26
+ end
27
+
28
+ gemspec path: "../"
data/lib/amoeba.rb CHANGED
@@ -1,522 +1,18 @@
1
- require "active_record"
2
- require "amoeba/version"
1
+ require 'active_record'
2
+ require 'active_support/all'
3
+ require 'amoeba/version'
4
+ require 'amoeba/config'
5
+ require 'amoeba/macros'
6
+ require 'amoeba/macros/base'
7
+ require 'amoeba/macros/has_many'
8
+ require 'amoeba/macros/has_one'
9
+ require 'amoeba/macros/has_and_belongs_to_many'
10
+ require 'amoeba/cloner'
11
+ require 'amoeba/class_methods'
12
+ require 'amoeba/instance_methods'
3
13
 
4
14
  module Amoeba
5
- module Dsl # {{{
6
- class Config
7
- def initialize(parent)
8
- @parent = parent
9
- end
10
-
11
- # Getters {{{
12
- def upbringing
13
- @raised ||= false
14
- @raised
15
- end
16
-
17
- def enabled
18
- @enabled ||= false
19
- @enabled
20
- end
21
-
22
- def inherit
23
- @inherit ||= false
24
- @inherit
25
- end
26
-
27
- def do_preproc
28
- @do_preproc ||= false
29
- @do_preproc
30
- end
31
-
32
- def parenting
33
- @parenting ||= false
34
- @parenting
35
- end
36
-
37
- def known_macros
38
- @known_macros ||= [:has_one, :has_many, :has_and_belongs_to_many]
39
- @known_macros
40
- end
41
-
42
- def includes
43
- @includes ||= []
44
- @includes
45
- end
46
-
47
- def excludes
48
- @excludes ||= []
49
- @excludes
50
- end
51
-
52
- def clones
53
- @clones ||= []
54
- @clones
55
- end
56
-
57
- def customizations
58
- @customizations ||= []
59
- @customizations
60
- end
61
-
62
- def overrides
63
- @overrides ||= []
64
- @overrides
65
- end
66
-
67
- def null_fields
68
- @null_fields ||= []
69
- @null_fields
70
- end
71
-
72
- def coercions
73
- @coercions ||= {}
74
- @coercions
75
- end
76
-
77
- def prefixes
78
- @prefixes ||= {}
79
- @prefixes
80
- end
81
-
82
- def suffixes
83
- @suffixes ||= {}
84
- @suffixes
85
- end
86
-
87
- def regexes
88
- @regexes ||= {}
89
- @regexes
90
- end
91
- # }}}
92
-
93
- # Setters (Config DSL) {{{
94
- def enable
95
- @enabled = true
96
- end
97
-
98
- def disable
99
- @enabled = false
100
- end
101
-
102
- def raised(style=:submissive)
103
- @raised = style
104
- end
105
-
106
- def propagate(style=:submissive)
107
- @parenting ||= style
108
- @inherit = true
109
- end
110
-
111
- def include_field(value=nil)
112
- @enabled ||= true
113
- @excludes = []
114
- @includes ||= []
115
- if value.is_a?(Array)
116
- @includes = value
117
- else
118
- @includes << value if value
119
- end
120
- @includes
121
- end
122
-
123
- def exclude_field(value=nil)
124
- @enabled ||= true
125
- @includes = []
126
- @excludes ||= []
127
- if value.is_a?(Array)
128
- @excludes = value
129
- else
130
- @excludes << value if value
131
- end
132
- @excludes
133
- end
134
-
135
- def clone(value=nil)
136
- @enabled ||= true
137
- @clones ||= []
138
- if value.is_a?(Array)
139
- @clones = value
140
- else
141
- @clones << value if value
142
- end
143
- @clones
144
- end
145
-
146
- def override(value=nil)
147
- @do_preproc ||= true
148
- @overrides ||= []
149
- if value.is_a?(Array)
150
- @overrides = value
151
- else
152
- @overrides << value if value
153
- end
154
- @overrides
155
- end
156
-
157
- def customize(value=nil)
158
- @do_preproc ||= true
159
- @customizations ||= []
160
- if value.is_a?(Array)
161
- @customizations = value
162
- else
163
- @customizations << value if value
164
- end
165
- @customizations
166
- end
167
-
168
- def recognize(value=nil)
169
- @enabled ||= true
170
- @known_macros ||= []
171
- if value.is_a?(Array)
172
- @known_macros = value
173
- else
174
- @known_macros << value if value
175
- end
176
- @known_macros
177
- end
178
-
179
- def nullify(value=nil)
180
- @do_preproc ||= true
181
- @null_fields ||= []
182
- if value.is_a?(Array)
183
- @null_fields = value
184
- else
185
- @null_fields << value if value
186
- end
187
- @null_fields
188
- end
189
-
190
- def set(defs=nil)
191
- @do_preproc ||= true
192
- @coercions ||= {}
193
- if defs.is_a?(Array)
194
- @coercions = {}
195
-
196
- defs.each do |d|
197
- d.each do |k,v|
198
- @coercions[k] = v if v
199
- end
200
- end
201
- else
202
- defs.each do |k,v|
203
- @coercions[k] = v if v
204
- end
205
- end
206
- @coercions
207
- end
208
-
209
- def prepend(defs=nil)
210
- @do_preproc ||= true
211
- @prefixes ||= {}
212
- if defs.is_a?(Array)
213
- @prefixes = {}
214
-
215
- defs.each do |d|
216
- d.each do |k,v|
217
- @prefixes[k] = v if v
218
- end
219
- end
220
- else
221
- defs.each do |k,v|
222
- @prefixes[k] = v if v
223
- end
224
- end
225
- @prefixes
226
- end
227
-
228
- def append(defs=nil)
229
- @do_preproc ||= true
230
- @suffixes ||= {}
231
- if defs.is_a?(Array)
232
- @suffixes = {}
233
-
234
- defs.each do |d|
235
- d.each do |k,v|
236
- @suffixes[k] = v if v
237
- end
238
- end
239
- else
240
- defs.each do |k,v|
241
- @suffixes[k] = v if v
242
- end
243
- end
244
- @suffixes
245
- end
246
-
247
- def regex(defs=nil)
248
- @do_preproc ||= true
249
- @regexes ||= {}
250
- if defs.is_a?(Array)
251
- @regexes = {}
252
-
253
- defs.each do |d|
254
- d.each do |k,v|
255
- @regexes[k] = v if v
256
- end
257
- end
258
- else
259
- defs.each do |k,v|
260
- @regexes[k] = v if v
261
- end
262
- end
263
- @regexes
264
- end
265
- # }}}
266
- end
267
- end # }}}
268
-
269
- module ClassMethods
270
- def amoeba(&block)
271
- @config_block ||= block if block_given?
272
-
273
- @config ||= Amoeba::Dsl::Config.new(self)
274
- @config.instance_eval(&block) if block_given?
275
- @config
276
- end
277
-
278
- def fresh_amoeba(&block)
279
- @config_block = block if block_given?
280
-
281
- @config = Amoeba::Dsl::Config.new(self)
282
- @config.instance_eval(&block) if block_given?
283
- @config
284
- end
285
-
286
- def amoeba_block
287
- @config_block
288
- end
289
- end
290
-
291
- module InstanceMethods
292
- # Config Getters {{{
293
- def amoeba_conf
294
- self.class.amoeba
295
- end
296
-
297
- def has_parent_amoeba_conf?
298
- self.class.superclass.respond_to?(:amoeba)
299
- end
300
-
301
- def parent_amoeba_conf
302
- if has_parent_amoeba_conf?
303
- self.class.superclass.amoeba
304
- else
305
- false
306
- end
307
- end
308
-
309
- def amoeba_settings
310
- self.class.amoeba_block
311
- end
312
-
313
- def has_parent_amoeba_settings?
314
- self.class.superclass.respond_to?(:amoeba_block)
315
- end
316
-
317
- def parent_amoeba_settings
318
- if has_parent_amoeba_conf?
319
- self.class.superclass.amoeba_block
320
- else
321
- false
322
- end
323
- end
324
- # }}}
325
-
326
- def dup(options={})
327
- @result = super()
328
-
329
- # Inherit Parent Settings {{{
330
- if !amoeba_conf.enabled && parent_amoeba_conf.inherit
331
- if amoeba_conf.upbringing
332
- parenting_style = amoeba_conf.upbringing
333
- else
334
- parenting_style = parent_amoeba_conf.parenting
335
- end
336
-
337
- case parenting_style
338
- when :strict
339
- # parent settings only
340
- self.class.fresh_amoeba(&parent_amoeba_settings)
341
- when :relaxed
342
- # parent takes precedence
343
- self.class.amoeba(&parent_amoeba_settings)
344
- when :submissive
345
- # parent suggests things
346
- # child does what it wants to anyway
347
- child_settings = amoeba_settings
348
- self.class.fresh_amoeba(&parent_amoeba_settings)
349
- self.class.amoeba(&child_settings)
350
- end
351
- end
352
- # }}}
353
-
354
- # Run Amoeba {{{
355
- # pramoeba_conf.overridesepend any extra strings to indicate uniqueness of the new record(s)
356
- if amoeba_conf.overrides.length > 0
357
- amoeba_conf.overrides.each do |block|
358
- block.call(self, @result)
359
- end
360
- end
361
-
362
- if amoeba_conf.enabled
363
- # Deep Clone Settings {{{
364
- amoeba_conf.clones.each do |clone_field|
365
- r = self.class.reflect_on_association clone_field
366
-
367
- # if this is a has many through and we're gonna deep
368
- # copy the child records, exclude the regular join
369
- # table from copying so we don't end up with the new
370
- # and old children on the copy
371
- if r.macro == :has_many && r.is_a?(ActiveRecord::Reflection::ThroughReflection)
372
- amoeba_conf.exclude_field r.options[:through]
373
- end
374
- end
375
- # }}}
376
-
377
- # Inclusive Style {{{
378
- if amoeba_conf.includes.count > 0
379
- amoeba_conf.includes.each do |i|
380
- r = self.class.reflect_on_association i
381
- amo_process_association(i, r)
382
- end
383
- # }}}
384
- # Exclusive Style {{{
385
- elsif amoeba_conf.excludes.count > 0
386
- reflections.each do |r|
387
- if not amoeba_conf.excludes.include?(r[0])
388
- amo_process_association(r[0], r[1])
389
- end
390
- end
391
- # }}}
392
- # Indiscriminate Style {{{
393
- else
394
- reflections.each do |r|
395
- amo_process_association(r[0], r[1])
396
- end
397
- end
398
- # }}}
399
- end
400
-
401
- if amoeba_conf.do_preproc
402
- amo_preprocess_parent_copy
403
- end
404
- # }}}
405
-
406
- @result
407
- end
408
-
409
- private
410
- # Copy Children {{{
411
- def amo_process_association(relation_name, settings)
412
- if not amoeba_conf.known_macros.include?(settings.macro)
413
- return
414
- end
415
-
416
- case settings.macro
417
- when :has_one
418
- if settings.is_a?(ActiveRecord::Reflection::ThroughReflection)
419
- return
420
- end
421
-
422
- old_obj = self.send(relation_name)
423
-
424
- if not old_obj.nil?
425
- copy_of_obj = old_obj.dup
426
- copy_of_obj[:"#{settings.foreign_key}"] = nil
427
-
428
- @result.send(:"#{relation_name}=", copy_of_obj)
429
- end
430
- when :has_many
431
- clone = amoeba_conf.clones.include?(:"#{relation_name}")
432
-
433
- # this could be DRYed up for better readability by
434
- # duplicating the loop code, but I'm duplicating the
435
- # loops to avoid that extra check on each iteration
436
- if clone
437
- # This is a M:M "has many through" where we
438
- # actually copy and reassociate the new children
439
- # rather than only maintaining the associations
440
- self.send(relation_name).each do |old_obj|
441
-
442
- copy_of_obj = old_obj.dup
443
-
444
- # associate this new child to the new parent object
445
- @result.send(relation_name) << copy_of_obj
446
- end
447
- else
448
- # This is a regular 1:M "has many"
449
- #
450
- # copying the children of the regular has many will
451
- # effectively do what is desired anyway, the through
452
- # association is really just for convenience usage
453
- # on the model
454
- return if settings.is_a?(ActiveRecord::Reflection::ThroughReflection)
455
-
456
- self.send(relation_name).each do |old_obj|
457
- copy_of_obj = old_obj.dup
458
- copy_of_obj[:"#{settings.foreign_key}"] = nil
459
-
460
- # associate this new child to the new parent object
461
- @result.send(relation_name) << copy_of_obj
462
- end
463
- end
464
-
465
- when :has_and_belongs_to_many
466
- clone = amoeba_conf.clones.include?(relation_name)
467
-
468
- if clone
469
- self.send(relation_name).each do |old_obj|
470
- copy_of_obj = old_obj.dup
471
-
472
- # associate this new child to the new parent object
473
- @result.send(relation_name) << copy_of_obj
474
- end
475
- else
476
- self.send(relation_name).each do |old_obj|
477
- # associate this new child to the new parent object
478
- @result.send(relation_name) << old_obj
479
- end
480
- end
481
- end
482
- end
483
- # }}}
484
-
485
- # Field Preprocessor {{{
486
- def amo_preprocess_parent_copy
487
- # nullify any fields the user has configured
488
- amoeba_conf.null_fields.each do |n|
489
- @result[n] = nil
490
- end
491
-
492
- # prepend any extra strings to indicate uniqueness of the new record(s)
493
- amoeba_conf.coercions.each do |field,coercion|
494
- @result[field] = "#{coercion}"
495
- end
496
-
497
- # prepend any extra strings to indicate uniqueness of the new record(s)
498
- amoeba_conf.prefixes.each do |field,prefix|
499
- @result[field] = "#{prefix}#{@result[field]}"
500
- end
501
-
502
- # postpend any extra strings to indicate uniqueness of the new record(s)
503
- amoeba_conf.suffixes.each do |field,suffix|
504
- @result[field] = "#{@result[field]}#{suffix}"
505
- end
506
-
507
- # regex andy fields that need changing
508
- amoeba_conf.regexes.each do |field,action|
509
- @result[field].gsub!(action[:replace], action[:with])
510
- end
511
-
512
- # prepend any extra strings to indicate uniqueness of the new record(s)
513
- amoeba_conf.customizations.each do |block|
514
- block.call(self, @result)
515
- end
516
- end
517
- # }}}
518
- end
519
15
  end
520
16
 
521
- ActiveRecord::Base.send :extend, Amoeba::ClassMethods
17
+ ActiveRecord::Base.send :extend, Amoeba::ClassMethods
522
18
  ActiveRecord::Base.send :include, Amoeba::InstanceMethods