deep_cloneable 1.2.4 → 1.3.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/README.rdoc +8 -2
- data/VERSION +1 -1
- data/deep_cloneable.gemspec +2 -2
- data/lib/deep_cloneable.rb +12 -6
- data/test/test_deep_cloneable.rb +70 -68
- metadata +5 -5
data/README.rdoc
CHANGED
@@ -4,7 +4,7 @@ This gem gives every ActiveRecord::Base object the possibility to do a deep clon
|
|
4
4
|
|
5
5
|
== Requirements
|
6
6
|
|
7
|
-
* Activerecord, tested with
|
7
|
+
* Activerecord, tested with AR 3 & 3.1.
|
8
8
|
|
9
9
|
== Installation
|
10
10
|
|
@@ -60,6 +60,12 @@ If this is not an option for you, it is also possible to populate the dictionary
|
|
60
60
|
=== Cloning a model without an attribute or nested multiple attributes
|
61
61
|
pirate.clone :include => :parrot, :except => [:name, { :parrot => [:name] }]
|
62
62
|
|
63
|
+
== Contributors
|
64
|
+
|
65
|
+
* Michael He
|
66
|
+
* Indrek Juhkam
|
67
|
+
* Mart Karu
|
68
|
+
|
63
69
|
== Note on Patches/Pull Requests
|
64
70
|
|
65
71
|
* Fork the project.
|
@@ -72,4 +78,4 @@ If this is not an option for you, it is also possible to populate the dictionary
|
|
72
78
|
|
73
79
|
== Copyright
|
74
80
|
|
75
|
-
Copyright (c)
|
81
|
+
Copyright (c) 2011 Reinier de Lange. See LICENSE for details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.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.3.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{2011-
|
12
|
+
s.date = %q{2011-07-11}
|
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
@@ -1,5 +1,7 @@
|
|
1
1
|
class ActiveRecord::Base
|
2
2
|
module DeepCloneable
|
3
|
+
@@rails31 = ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR > 0
|
4
|
+
|
3
5
|
# clones an ActiveRecord model.
|
4
6
|
# if passed the :include option, it will deep clone the given associations
|
5
7
|
# if passed the :except option, it won't clone the given attributes
|
@@ -40,7 +42,9 @@ class ActiveRecord::Base
|
|
40
42
|
# ==== Cloning a model without an attribute or nested multiple attributes
|
41
43
|
# pirate.clone :include => :parrot, :except => [:name, { :parrot => [:name] }]
|
42
44
|
#
|
43
|
-
|
45
|
+
define_method (@@rails31 ? :dup : :clone) do |*args|
|
46
|
+
options = args[0] || {}
|
47
|
+
|
44
48
|
dict = options[:dictionary]
|
45
49
|
dict ||= {} if options.delete(:use_dictionary)
|
46
50
|
|
@@ -77,15 +81,17 @@ class ActiveRecord::Base
|
|
77
81
|
|
78
82
|
cloned_object = case association_reflection.macro
|
79
83
|
when :belongs_to, :has_one
|
80
|
-
self.send(association) && self.send(association).
|
84
|
+
self.send(association) && self.send(association).send(__method__, opts)
|
81
85
|
when :has_many, :has_and_belongs_to_many
|
82
|
-
|
83
|
-
|
86
|
+
primary_key_name = (@@rails31 ? association_reflection.foreign_key : association_reflection.primary_key_name).to_s
|
87
|
+
|
88
|
+
reverse_association_name = association_reflection.klass.reflect_on_all_associations.detect do |a|
|
89
|
+
a.send(@@rails31 ? :foreign_key : :primary_key_name).to_s == primary_key_name
|
84
90
|
end.try(:name)
|
85
91
|
|
86
92
|
self.send(association).collect do |obj|
|
87
|
-
tmp = obj.
|
88
|
-
tmp.send("#{
|
93
|
+
tmp = obj.send(__method__, opts)
|
94
|
+
tmp.send("#{primary_key_name}=", nil)
|
89
95
|
tmp.send("#{reverse_association_name.to_s}=", kopy) if reverse_association_name
|
90
96
|
tmp
|
91
97
|
end
|
data/test/test_deep_cloneable.rb
CHANGED
@@ -2,8 +2,10 @@ require File.dirname(__FILE__) + '/test_helper'
|
|
2
2
|
|
3
3
|
class TestDeepCloneable < Test::Unit::TestCase
|
4
4
|
load_schema
|
5
|
+
|
6
|
+
@@clone_method = ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR > 0 ? :dup : :clone
|
5
7
|
|
6
|
-
def setup
|
8
|
+
def setup
|
7
9
|
@jack = Pirate.create(:name => 'Jack Sparrow', :nick_name => 'Captain Jack', :age => 30)
|
8
10
|
@polly = Parrot.create(:name => 'Polly', :pirate => @jack)
|
9
11
|
@john = Matey.create(:name => 'John', :pirate => @jack)
|
@@ -12,109 +14,109 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
12
14
|
@ship = BattleShip.create(:name => 'Black Pearl', :pirates => [@jack])
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
16
|
-
|
17
|
-
assert
|
18
|
-
assert_equal @jack.name, @jack.
|
19
|
-
assert_nil
|
20
|
-
assert_equal @jack.nick_name,
|
17
|
+
def test_single_dup_exception
|
18
|
+
dup = @jack.send(@@clone_method, :except => :name)
|
19
|
+
assert dup.save
|
20
|
+
assert_equal @jack.name, @jack.send(@@clone_method).name # Old behaviour
|
21
|
+
assert_nil dup.name
|
22
|
+
assert_equal @jack.nick_name, dup.nick_name
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
|
25
|
-
assert
|
26
|
-
assert_nil
|
27
|
-
assert_equal 'no nickname',
|
28
|
-
assert_equal @jack.age,
|
25
|
+
def test_multiple_dup_exception
|
26
|
+
dup = @jack.send(@@clone_method, :except => [:name, :nick_name])
|
27
|
+
assert dup.save
|
28
|
+
assert_nil dup.name
|
29
|
+
assert_equal 'no nickname', dup.nick_name
|
30
|
+
assert_equal @jack.age, dup.age
|
29
31
|
end
|
30
32
|
|
31
33
|
def test_single_include_association
|
32
|
-
|
33
|
-
assert
|
34
|
-
assert_equal 1,
|
34
|
+
dup = @jack.send(@@clone_method, :include => :mateys)
|
35
|
+
assert dup.save
|
36
|
+
assert_equal 1, dup.mateys.size
|
35
37
|
end
|
36
38
|
|
37
39
|
def test_single_include_belongs_to_polymorphic_association
|
38
|
-
|
39
|
-
assert
|
40
|
-
assert_not_nil
|
41
|
-
assert_not_equal @jack.ship,
|
40
|
+
dup = @jack.send(@@clone_method, :include => :ship)
|
41
|
+
assert dup.save
|
42
|
+
assert_not_nil dup.ship
|
43
|
+
assert_not_equal @jack.ship, dup.ship
|
42
44
|
end
|
43
45
|
|
44
46
|
def test_single_include_has_many_polymorphic_association
|
45
|
-
|
46
|
-
assert
|
47
|
-
assert
|
47
|
+
dup = @ship.send(@@clone_method, :include => :pirates)
|
48
|
+
assert dup.save
|
49
|
+
assert dup.pirates.any?
|
48
50
|
end
|
49
51
|
|
50
52
|
def test_multiple_include_association
|
51
|
-
|
52
|
-
assert
|
53
|
-
assert_equal 1,
|
54
|
-
assert_equal 1,
|
53
|
+
dup = @jack.send(@@clone_method, :include => [:mateys, :treasures])
|
54
|
+
assert dup.save
|
55
|
+
assert_equal 1, dup.mateys.size
|
56
|
+
assert_equal 1, dup.treasures.size
|
55
57
|
end
|
56
58
|
|
57
59
|
def test_deep_include_association
|
58
|
-
|
59
|
-
assert
|
60
|
-
assert_equal 1,
|
61
|
-
assert_equal 1,
|
60
|
+
dup = @jack.send(@@clone_method, :include => {:treasures => :gold_pieces})
|
61
|
+
assert dup.save
|
62
|
+
assert_equal 1, dup.treasures.size
|
63
|
+
assert_equal 1, dup.gold_pieces.size
|
62
64
|
end
|
63
65
|
|
64
66
|
def test_include_association_assignments
|
65
|
-
|
67
|
+
dup = @jack.send(@@clone_method, :include => :treasures)
|
66
68
|
|
67
|
-
|
68
|
-
assert_equal
|
69
|
+
dup.treasures.each do |treasure|
|
70
|
+
assert_equal dup, treasure.pirate
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
74
|
def test_multiple_and_deep_include_association
|
73
|
-
|
74
|
-
assert
|
75
|
-
assert_equal 1,
|
76
|
-
assert_equal 1,
|
77
|
-
assert_equal 1,
|
75
|
+
dup = @jack.send(@@clone_method, :include => {:treasures => :gold_pieces, :mateys => {}})
|
76
|
+
assert dup.save
|
77
|
+
assert_equal 1, dup.treasures.size
|
78
|
+
assert_equal 1, dup.gold_pieces.size
|
79
|
+
assert_equal 1, dup.mateys.size
|
78
80
|
end
|
79
81
|
|
80
82
|
def test_multiple_and_deep_include_association_with_array
|
81
|
-
|
82
|
-
assert
|
83
|
-
assert_equal 1,
|
84
|
-
assert_equal 1,
|
85
|
-
assert_equal 1,
|
83
|
+
dup = @jack.send(@@clone_method, :include => [{:treasures => :gold_pieces}, :mateys])
|
84
|
+
assert dup.save
|
85
|
+
assert_equal 1, dup.treasures.size
|
86
|
+
assert_equal 1, dup.gold_pieces.size
|
87
|
+
assert_equal 1, dup.mateys.size
|
86
88
|
end
|
87
89
|
|
88
90
|
def test_with_belongs_to_relation
|
89
|
-
|
90
|
-
assert
|
91
|
-
assert_not_equal
|
91
|
+
dup = @jack.send(@@clone_method, :include => :parrot)
|
92
|
+
assert dup.save
|
93
|
+
assert_not_equal dup.parrot, @jack.parrot
|
92
94
|
end
|
93
95
|
|
94
96
|
def test_should_pass_nested_exceptions
|
95
|
-
|
96
|
-
assert
|
97
|
-
assert_not_equal
|
97
|
+
dup = @jack.send(@@clone_method, :include => :parrot, :except => [:name, { :parrot => [:name] }])
|
98
|
+
assert dup.save
|
99
|
+
assert_not_equal dup.parrot, @jack.parrot
|
98
100
|
assert_not_nil @jack.parrot.name
|
99
|
-
assert_nil
|
101
|
+
assert_nil dup.parrot.name
|
100
102
|
end
|
101
103
|
|
102
|
-
def
|
104
|
+
def test_should_not_double_dup_when_using_dictionary
|
103
105
|
current_matey_count = Matey.count
|
104
|
-
|
105
|
-
|
106
|
+
dup = @jack.send(@@clone_method, :include => [:mateys, { :treasures => :matey }], :use_dictionary => true)
|
107
|
+
dup.save!
|
106
108
|
|
107
109
|
assert_equal current_matey_count + 1, Matey.count
|
108
110
|
end
|
109
111
|
|
110
|
-
def
|
112
|
+
def test_should_not_double_dup_when_using_manual_dictionary
|
111
113
|
current_matey_count = Matey.count
|
112
114
|
|
113
115
|
dict = { :mateys => {} }
|
114
|
-
@jack.mateys.each{|m| dict[:mateys][m] = m.
|
116
|
+
@jack.mateys.each{|m| dict[:mateys][m] = m.send(@@clone_method) }
|
115
117
|
|
116
|
-
|
117
|
-
|
118
|
+
dup = @jack.send(@@clone_method, :include => [:mateys, { :treasures => :matey }], :dictionary => dict)
|
119
|
+
dup.save!
|
118
120
|
|
119
121
|
assert_equal current_matey_count + 1, Matey.count
|
120
122
|
end
|
@@ -123,19 +125,19 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
123
125
|
@human = Animal::Human.create :name => "Michael"
|
124
126
|
@pig = Animal::Pig.create :human => @human, :name => 'big pig'
|
125
127
|
|
126
|
-
|
127
|
-
assert
|
128
|
-
assert_equal 1,
|
128
|
+
dup_human = @human.send(@@clone_method, :include => [:pigs])
|
129
|
+
assert dup_human.save
|
130
|
+
assert_equal 1, dup_human.pigs.count
|
129
131
|
|
130
132
|
@human2 = Animal::Human.create :name => "John"
|
131
133
|
@pig2 = @human2.pigs.create :name => 'small pig'
|
132
134
|
|
133
|
-
|
134
|
-
assert
|
135
|
-
assert_equal 1,
|
135
|
+
dup_human_2 = @human.send(@@clone_method, :include => [:pigs])
|
136
|
+
assert dup_human_2.save
|
137
|
+
assert_equal 1, dup_human_2.pigs.count
|
136
138
|
end
|
137
139
|
|
138
|
-
def
|
140
|
+
def test_should_dup_many_to_many_associations
|
139
141
|
@human = Animal::Human.create :name => "Michael"
|
140
142
|
@human2 = Animal::Human.create :name => "Jack"
|
141
143
|
@chicken1 = Animal::Chicken.create :name => 'Chick1'
|
@@ -143,9 +145,9 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
143
145
|
@human.chickens << [@chicken1, @chicken2]
|
144
146
|
@human2.chickens << [@chicken1, @chicken2]
|
145
147
|
|
146
|
-
|
147
|
-
assert
|
148
|
-
assert_equal 2,
|
148
|
+
dup_human = @human.send(@@clone_method, :include => :ownerships)
|
149
|
+
assert dup_human.save
|
150
|
+
assert_equal 2, dup_human.chickens.count
|
149
151
|
end
|
150
152
|
|
151
153
|
end
|
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: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 1.3.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: 2011-
|
18
|
+
date: 2011-07-11 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|