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