deep_cloneable 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|