deep_cloneable 1.1.0 → 1.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/.gitignore +2 -0
- data/README.rdoc +12 -0
- data/VERSION +1 -1
- data/deep_cloneable.gemspec +2 -2
- data/lib/deep_cloneable.rb +25 -2
- data/test/schema.rb +1 -0
- data/test/test_deep_cloneable.rb +21 -1
- data/test/test_helper.rb +4 -3
- metadata +4 -4
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -37,6 +37,18 @@ And include the gem in your apps config
|
|
37
37
|
|
38
38
|
=== Cloning really deep with multiple associations
|
39
39
|
pirate.clone :include => [:mateys, {:treasures => :gold_pieces}]
|
40
|
+
|
41
|
+
=== Cloning really deep with multiple associations and a dictionary
|
42
|
+
|
43
|
+
A dictionary ensures that models are not cloned multiple times when it is associated to nested models.
|
44
|
+
When using a dictionary, ensure recurring associations are cloned first:
|
45
|
+
|
46
|
+
pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :use_dictionary => true }]
|
47
|
+
|
48
|
+
If this is not an option for you, it is also possible to populate the dictionary manually in advance:
|
49
|
+
|
50
|
+
dict = { :mateys => {} }
|
51
|
+
pirate.mateys.each{|m| dict[:mateys][m] = m.clone }
|
40
52
|
|
41
53
|
=== Cloning a model without an attribute
|
42
54
|
pirate.clone :except => :name
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/deep_cloneable.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{deep_cloneable}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Reinier de Lange"]
|
12
|
-
s.date = %q{2010-10-
|
12
|
+
s.date = %q{2010-10-20}
|
13
13
|
s.description = %q{Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. }
|
14
14
|
s.email = %q{r.j.delange@nedforce.nl}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/deep_cloneable.rb
CHANGED
@@ -17,7 +17,20 @@ class ActiveRecord::Base
|
|
17
17
|
#
|
18
18
|
# ==== Cloning really deep with multiple associations
|
19
19
|
# pirate.clone :include => [:mateys, {:treasures => :gold_pieces}]
|
20
|
-
#
|
20
|
+
#
|
21
|
+
# ==== Cloning really deep with multiple associations and a dictionary
|
22
|
+
#
|
23
|
+
# A dictionary ensures that models are not cloned multiple times when it is associated to nested models.
|
24
|
+
# When using a dictionary, ensure recurring associations are cloned first:
|
25
|
+
#
|
26
|
+
# pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :use_dictionary => true }]
|
27
|
+
#
|
28
|
+
# If this is not an option for you, it is also possible to populate the dictionary manually in advance:
|
29
|
+
#
|
30
|
+
# dict = { :mateys => {} }
|
31
|
+
# pirate.mateys.each{|m| dict[:mateys][m] = m.clone }
|
32
|
+
# pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :dictionary => dict }]
|
33
|
+
#
|
21
34
|
# ==== Cloning a model without an attribute
|
22
35
|
# pirate.clone :except => :name
|
23
36
|
#
|
@@ -28,7 +41,16 @@ class ActiveRecord::Base
|
|
28
41
|
# pirate.clone :include => :parrot, :except => [:name, { :parrot => [:name] }]
|
29
42
|
#
|
30
43
|
def clone(options = {})
|
31
|
-
|
44
|
+
dict = options[:dictionary]
|
45
|
+
dict ||= {} if options.delete(:use_dictionary)
|
46
|
+
|
47
|
+
kopy = unless dict
|
48
|
+
super()
|
49
|
+
else
|
50
|
+
tableized_class = self.class.name.tableize.to_sym
|
51
|
+
dict[tableized_class] ||= {}
|
52
|
+
dict[tableized_class][self] ||= super()
|
53
|
+
end
|
32
54
|
|
33
55
|
deep_exceptions = {}
|
34
56
|
if options[:except]
|
@@ -48,6 +70,7 @@ class ActiveRecord::Base
|
|
48
70
|
|
49
71
|
opts = deep_associations.blank? ? {} : {:include => deep_associations}
|
50
72
|
opts.merge!(:except => deep_exceptions[association]) if deep_exceptions[association]
|
73
|
+
opts.merge!(:dictionary => dict) if dict
|
51
74
|
|
52
75
|
association_reflection = self.class.reflect_on_association(association)
|
53
76
|
cloned_object = case association_reflection.macro
|
data/test/schema.rb
CHANGED
data/test/test_deep_cloneable.rb
CHANGED
@@ -7,7 +7,7 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
7
7
|
@jack = Pirate.create(:name => 'Jack Sparrow', :nick_name => 'Captain Jack', :age => 30)
|
8
8
|
@polly = Parrot.create(:name => 'Polly', :pirate => @jack)
|
9
9
|
@john = Matey.create(:name => 'John', :pirate => @jack)
|
10
|
-
@treasure = Treasure.create(:found_at => 'Isla del Muerte', :pirate => @jack)
|
10
|
+
@treasure = Treasure.create(:found_at => 'Isla del Muerte', :pirate => @jack, :matey => @john)
|
11
11
|
@gold_piece = GoldPiece.create(:treasure => @treasure)
|
12
12
|
end
|
13
13
|
|
@@ -76,4 +76,24 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
76
76
|
assert_not_nil @jack.parrot.name
|
77
77
|
assert_nil clone.parrot.name
|
78
78
|
end
|
79
|
+
|
80
|
+
def test_should_not_double_clone_when_using_dictionary
|
81
|
+
current_matey_count = Matey.count
|
82
|
+
clone = @jack.clone(:include => [:mateys, { :treasures => :matey }], :use_dictionary => true)
|
83
|
+
clone.save!
|
84
|
+
|
85
|
+
assert_equal current_matey_count + 1, Matey.count
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_not_double_clone_when_using_manual_dictionary
|
89
|
+
current_matey_count = Matey.count
|
90
|
+
|
91
|
+
dict = { :mateys => {} }
|
92
|
+
@jack.mateys.each{|m| dict[:mateys][m] = m.clone }
|
93
|
+
|
94
|
+
clone = @jack.clone(:include => [:mateys, { :treasures => :matey }], :dictionary => dict)
|
95
|
+
clone.save!
|
96
|
+
|
97
|
+
assert_equal current_matey_count + 1, Matey.count
|
98
|
+
end
|
79
99
|
end
|
data/test/test_helper.rb
CHANGED
@@ -10,9 +10,9 @@ require 'active_record'
|
|
10
10
|
require 'active_record/fixtures'
|
11
11
|
require File.dirname(__FILE__) + '/../init.rb'
|
12
12
|
|
13
|
-
class GoldPiece < ActiveRecord::Base; belongs_to :treasure
|
14
|
-
class Matey < ActiveRecord::Base;
|
15
|
-
class Parrot < ActiveRecord::Base;
|
13
|
+
class GoldPiece < ActiveRecord::Base; belongs_to :treasure end
|
14
|
+
class Matey < ActiveRecord::Base; belongs_to :pirate end
|
15
|
+
class Parrot < ActiveRecord::Base; belongs_to :pirate end
|
16
16
|
|
17
17
|
class Pirate < ActiveRecord::Base
|
18
18
|
has_many :mateys
|
@@ -23,6 +23,7 @@ end
|
|
23
23
|
|
24
24
|
class Treasure < ActiveRecord::Base
|
25
25
|
belongs_to :pirate
|
26
|
+
belongs_to :matey
|
26
27
|
has_many :gold_pieces
|
27
28
|
end
|
28
29
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deep_cloneable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Reinier de Lange
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-20 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|