deep_cloneable 1.4.1 → 1.5.1
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/Gemfile +2 -2
- data/Gemfile.lock +16 -72
- data/LICENSE +1 -1
- data/README.rdoc +10 -3
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/deep_cloneable.gemspec +6 -6
- data/lib/deep_cloneable.rb +45 -37
- data/test/models.rb +19 -8
- data/test/schema.rb +25 -16
- data/test/test_deep_cloneable.rb +73 -17
- metadata +7 -7
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,94 +1,38 @@
|
|
|
1
1
|
GEM
|
|
2
2
|
remote: http://rubygems.org/
|
|
3
3
|
specs:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
mail (~> 2.4.0)
|
|
7
|
-
actionpack (3.2.2)
|
|
8
|
-
activemodel (= 3.2.2)
|
|
9
|
-
activesupport (= 3.2.2)
|
|
4
|
+
activemodel (3.2.12)
|
|
5
|
+
activesupport (= 3.2.12)
|
|
10
6
|
builder (~> 3.0.0)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
rack-cache (~> 1.1)
|
|
15
|
-
rack-test (~> 0.6.1)
|
|
16
|
-
sprockets (~> 2.1.2)
|
|
17
|
-
activemodel (3.2.2)
|
|
18
|
-
activesupport (= 3.2.2)
|
|
19
|
-
builder (~> 3.0.0)
|
|
20
|
-
activerecord (3.2.2)
|
|
21
|
-
activemodel (= 3.2.2)
|
|
22
|
-
activesupport (= 3.2.2)
|
|
7
|
+
activerecord (3.2.12)
|
|
8
|
+
activemodel (= 3.2.12)
|
|
9
|
+
activesupport (= 3.2.12)
|
|
23
10
|
arel (~> 3.0.2)
|
|
24
11
|
tzinfo (~> 0.3.29)
|
|
25
|
-
|
|
26
|
-
activemodel (= 3.2.2)
|
|
27
|
-
activesupport (= 3.2.2)
|
|
28
|
-
activesupport (3.2.2)
|
|
12
|
+
activesupport (3.2.12)
|
|
29
13
|
i18n (~> 0.6)
|
|
30
14
|
multi_json (~> 1.0)
|
|
31
15
|
arel (3.0.2)
|
|
32
|
-
builder (3.0.
|
|
33
|
-
erubis (2.7.0)
|
|
16
|
+
builder (3.0.4)
|
|
34
17
|
git (1.2.5)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
jeweler (1.8.3)
|
|
18
|
+
i18n (0.6.4)
|
|
19
|
+
jeweler (1.8.4)
|
|
38
20
|
bundler (~> 1.0)
|
|
39
21
|
git (>= 1.2.5)
|
|
40
22
|
rake
|
|
41
23
|
rdoc
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
mime-types (~> 1.16)
|
|
47
|
-
treetop (~> 1.4.8)
|
|
48
|
-
mime-types (1.18)
|
|
49
|
-
multi_json (1.2.0)
|
|
50
|
-
polyglot (0.3.3)
|
|
51
|
-
rack (1.4.1)
|
|
52
|
-
rack-cache (1.2)
|
|
53
|
-
rack (>= 0.4)
|
|
54
|
-
rack-ssl (1.3.2)
|
|
55
|
-
rack
|
|
56
|
-
rack-test (0.6.1)
|
|
57
|
-
rack (>= 1.0)
|
|
58
|
-
rails (3.2.2)
|
|
59
|
-
actionmailer (= 3.2.2)
|
|
60
|
-
actionpack (= 3.2.2)
|
|
61
|
-
activerecord (= 3.2.2)
|
|
62
|
-
activeresource (= 3.2.2)
|
|
63
|
-
activesupport (= 3.2.2)
|
|
64
|
-
bundler (~> 1.0)
|
|
65
|
-
railties (= 3.2.2)
|
|
66
|
-
railties (3.2.2)
|
|
67
|
-
actionpack (= 3.2.2)
|
|
68
|
-
activesupport (= 3.2.2)
|
|
69
|
-
rack-ssl (~> 1.3.2)
|
|
70
|
-
rake (>= 0.8.7)
|
|
71
|
-
rdoc (~> 3.4)
|
|
72
|
-
thor (~> 0.14.6)
|
|
73
|
-
rake (0.9.2.2)
|
|
74
|
-
rdoc (3.12)
|
|
24
|
+
json (1.7.7)
|
|
25
|
+
multi_json (1.6.1)
|
|
26
|
+
rake (10.0.3)
|
|
27
|
+
rdoc (4.0.0)
|
|
75
28
|
json (~> 1.4)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
rack (~> 1.0)
|
|
79
|
-
tilt (~> 1.1, != 1.3.0)
|
|
80
|
-
sqlite3 (1.3.5)
|
|
81
|
-
thor (0.14.6)
|
|
82
|
-
tilt (1.3.3)
|
|
83
|
-
treetop (1.4.10)
|
|
84
|
-
polyglot
|
|
85
|
-
polyglot (>= 0.3.1)
|
|
86
|
-
tzinfo (0.3.32)
|
|
29
|
+
sqlite3 (1.3.7)
|
|
30
|
+
tzinfo (0.3.36)
|
|
87
31
|
|
|
88
32
|
PLATFORMS
|
|
89
33
|
ruby
|
|
90
34
|
|
|
91
35
|
DEPENDENCIES
|
|
36
|
+
activerecord (>= 3.1)
|
|
92
37
|
jeweler
|
|
93
|
-
rails
|
|
94
38
|
sqlite3
|
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
|
@@ -4,6 +4,8 @@ This gem gives every ActiveRecord::Base object the possibility to do a deep clon
|
|
|
4
4
|
|
|
5
5
|
== Requirements
|
|
6
6
|
|
|
7
|
+
* Ruby 1.8.7, 1.9, 2.0.0
|
|
8
|
+
|
|
7
9
|
* Activerecord 3.1, 3.2
|
|
8
10
|
|
|
9
11
|
* Rails 2.x/3.0 users, please check out the 'rails2.x-3.0' branch.
|
|
@@ -12,7 +14,7 @@ This gem gives every ActiveRecord::Base object the possibility to do a deep clon
|
|
|
12
14
|
|
|
13
15
|
* In your Gemfile:
|
|
14
16
|
|
|
15
|
-
gem 'deep_cloneable', '~> 1.
|
|
17
|
+
gem 'deep_cloneable', '~> 1.5.0'
|
|
16
18
|
|
|
17
19
|
== Example
|
|
18
20
|
|
|
@@ -54,7 +56,10 @@ If this is not an option for you, it is also possible to populate the dictionary
|
|
|
54
56
|
pirate.dup :include => :parrot do |original, kopy|
|
|
55
57
|
kopy.cloned_from_id = original.id if kopy.respond_to?(:cloned_from_id)
|
|
56
58
|
end
|
|
57
|
-
|
|
59
|
+
|
|
60
|
+
=== Cloning without validations
|
|
61
|
+
pirate.dup :include => {:treasures => :gold_pieces}, :validate => false
|
|
62
|
+
|
|
58
63
|
== Contributors
|
|
59
64
|
|
|
60
65
|
* Michael He
|
|
@@ -64,6 +69,8 @@ If this is not an option for you, it is also possible to populate the dictionary
|
|
|
64
69
|
* Ilya Kuzmin
|
|
65
70
|
* zozi
|
|
66
71
|
* fractious
|
|
72
|
+
* Georges Gabereau
|
|
73
|
+
* Christophe Belpaire
|
|
67
74
|
|
|
68
75
|
== Note on Patches/Pull Requests
|
|
69
76
|
|
|
@@ -77,4 +84,4 @@ If this is not an option for you, it is also possible to populate the dictionary
|
|
|
77
84
|
|
|
78
85
|
== Copyright
|
|
79
86
|
|
|
80
|
-
Copyright (c)
|
|
87
|
+
Copyright (c) 2013 Reinier de Lange. See LICENSE for details.
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.5.1
|
data/deep_cloneable.gemspec
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = "deep_cloneable"
|
|
8
|
-
s.version = "1.
|
|
8
|
+
s.version = "1.5.1"
|
|
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 = "
|
|
12
|
+
s.date = "2013-03-06"
|
|
13
13
|
s.description = "Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. "
|
|
14
14
|
s.email = "r.j.delange@nedforce.nl"
|
|
15
15
|
s.extra_rdoc_files = [
|
|
@@ -35,21 +35,21 @@ Gem::Specification.new do |s|
|
|
|
35
35
|
]
|
|
36
36
|
s.homepage = "http://github.com/moiristo/deep_cloneable"
|
|
37
37
|
s.require_paths = ["lib"]
|
|
38
|
-
s.rubygems_version = "1.8.
|
|
38
|
+
s.rubygems_version = "1.8.24"
|
|
39
39
|
s.summary = "This gem gives every ActiveRecord::Base object the possibility to do a deep clone."
|
|
40
40
|
|
|
41
41
|
if s.respond_to? :specification_version then
|
|
42
42
|
s.specification_version = 3
|
|
43
43
|
|
|
44
44
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
45
|
-
s.add_runtime_dependency(%q<
|
|
45
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 3.1"])
|
|
46
46
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
|
47
47
|
else
|
|
48
|
-
s.add_dependency(%q<
|
|
48
|
+
s.add_dependency(%q<activerecord>, [">= 3.1"])
|
|
49
49
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
50
50
|
end
|
|
51
51
|
else
|
|
52
|
-
s.add_dependency(%q<
|
|
52
|
+
s.add_dependency(%q<activerecord>, [">= 3.1"])
|
|
53
53
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
54
54
|
end
|
|
55
55
|
end
|
data/lib/deep_cloneable.rb
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
class ActiveRecord::Base
|
|
2
2
|
module DeepCloneable
|
|
3
|
-
@@rails31 = ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR > 0
|
|
4
|
-
|
|
5
3
|
# ActiveRecord::Base has its own dup method for Ruby 1.8.7. We have to
|
|
6
4
|
# redefine it and put it in a module so that we can override it in a
|
|
7
5
|
# module and call the original with super().
|
|
8
|
-
if
|
|
6
|
+
if !Object.respond_to? :initialize_dup, true
|
|
9
7
|
ActiveRecord::Base.class_eval do
|
|
10
8
|
module Dup
|
|
11
9
|
def dup
|
|
@@ -14,57 +12,57 @@ class ActiveRecord::Base
|
|
|
14
12
|
copy
|
|
15
13
|
end
|
|
16
14
|
end
|
|
17
|
-
|
|
15
|
+
remove_possible_method :dup
|
|
18
16
|
include Dup
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
# clones an ActiveRecord model.
|
|
20
|
+
# clones an ActiveRecord model.
|
|
23
21
|
# if passed the :include option, it will deep clone the given associations
|
|
24
22
|
# if passed the :except option, it won't clone the given attributes
|
|
25
23
|
#
|
|
26
24
|
# === Usage:
|
|
27
|
-
#
|
|
25
|
+
#
|
|
28
26
|
# ==== Cloning one single association
|
|
29
27
|
# pirate.clone :include => :mateys
|
|
30
|
-
#
|
|
28
|
+
#
|
|
31
29
|
# ==== Cloning multiple associations
|
|
32
30
|
# pirate.clone :include => [:mateys, :treasures]
|
|
33
|
-
#
|
|
31
|
+
#
|
|
34
32
|
# ==== Cloning really deep
|
|
35
33
|
# pirate.clone :include => {:treasures => :gold_pieces}
|
|
36
|
-
#
|
|
34
|
+
#
|
|
37
35
|
# ==== Cloning really deep with multiple associations
|
|
38
36
|
# pirate.clone :include => [:mateys, {:treasures => :gold_pieces}]
|
|
39
37
|
#
|
|
40
38
|
# ==== Cloning really deep with multiple associations and a dictionary
|
|
41
39
|
#
|
|
42
|
-
# A dictionary ensures that models are not cloned multiple times when it is associated to nested models.
|
|
40
|
+
# A dictionary ensures that models are not cloned multiple times when it is associated to nested models.
|
|
43
41
|
# When using a dictionary, ensure recurring associations are cloned first:
|
|
44
42
|
#
|
|
45
|
-
# pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :use_dictionary => true }]
|
|
43
|
+
# pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :use_dictionary => true }]
|
|
46
44
|
#
|
|
47
45
|
# If this is not an option for you, it is also possible to populate the dictionary manually in advance:
|
|
48
46
|
#
|
|
49
47
|
# dict = { :mateys => {} }
|
|
50
48
|
# pirate.mateys.each{|m| dict[:mateys][m] = m.clone }
|
|
51
|
-
# pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :dictionary => dict }]
|
|
52
|
-
#
|
|
49
|
+
# pirate.clone :include => [:mateys, {:treasures => [:matey, :gold_pieces], :dictionary => dict }]
|
|
50
|
+
#
|
|
53
51
|
# ==== Cloning a model without an attribute
|
|
54
52
|
# pirate.clone :except => :name
|
|
55
|
-
#
|
|
53
|
+
#
|
|
56
54
|
# ==== Cloning a model without multiple attributes
|
|
57
55
|
# pirate.clone :except => [:name, :nick_name]
|
|
58
|
-
#
|
|
59
|
-
# ==== Cloning a model without an attribute or nested multiple attributes
|
|
56
|
+
#
|
|
57
|
+
# ==== Cloning a model without an attribute or nested multiple attributes
|
|
60
58
|
# pirate.clone :include => :parrot, :except => [:name, { :parrot => [:name] }]
|
|
61
|
-
#
|
|
62
|
-
define_method
|
|
59
|
+
#
|
|
60
|
+
define_method :dup do |*args, &block|
|
|
63
61
|
options = args[0] || {}
|
|
64
|
-
|
|
62
|
+
|
|
65
63
|
dict = options[:dictionary]
|
|
66
64
|
dict ||= {} if options.delete(:use_dictionary)
|
|
67
|
-
|
|
65
|
+
|
|
68
66
|
kopy = unless dict
|
|
69
67
|
super()
|
|
70
68
|
else
|
|
@@ -83,40 +81,50 @@ class ActiveRecord::Base
|
|
|
83
81
|
end
|
|
84
82
|
deep_exceptions = exceptions.select{|e| e.kind_of?(Hash) }.inject({}){|m,h| m.merge(h) }
|
|
85
83
|
end
|
|
86
|
-
|
|
84
|
+
|
|
87
85
|
if options[:include]
|
|
88
86
|
Array(options[:include]).each do |association, deep_associations|
|
|
89
87
|
if (association.kind_of? Hash)
|
|
90
88
|
deep_associations = association[association.keys.first]
|
|
91
89
|
association = association.keys.first
|
|
92
90
|
end
|
|
93
|
-
|
|
91
|
+
|
|
94
92
|
opts = deep_associations.blank? ? {} : {:include => deep_associations}
|
|
95
93
|
opts.merge!(:except => deep_exceptions[association]) if deep_exceptions[association]
|
|
96
94
|
opts.merge!(:dictionary => dict) if dict
|
|
97
|
-
|
|
95
|
+
|
|
98
96
|
association_reflection = self.class.reflect_on_association(association)
|
|
99
97
|
raise AssociationNotFoundException.new("#{self.class}##{association}") if association_reflection.nil?
|
|
100
|
-
|
|
98
|
+
|
|
99
|
+
if options[:validate] == false
|
|
100
|
+
kopy.instance_eval do
|
|
101
|
+
# Force :validate => false on all saves.
|
|
102
|
+
def perform_validations(options={})
|
|
103
|
+
options[:validate] = false
|
|
104
|
+
super(options)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
101
109
|
cloned_object = case association_reflection.macro
|
|
102
110
|
when :belongs_to, :has_one
|
|
103
111
|
self.send(association) && self.send(association).send(__method__, opts, &block)
|
|
104
112
|
when :has_many
|
|
105
|
-
primary_key_name =
|
|
106
|
-
|
|
113
|
+
primary_key_name = association_reflection.foreign_key.to_s
|
|
114
|
+
|
|
107
115
|
reverse_association_name = association_reflection.klass.reflect_on_all_associations.detect do |a|
|
|
108
|
-
|
|
116
|
+
a.foreign_key.to_s == primary_key_name
|
|
109
117
|
end.try(:name)
|
|
110
|
-
|
|
111
|
-
self.send(association).collect do |obj|
|
|
118
|
+
|
|
119
|
+
self.send(association).collect do |obj|
|
|
112
120
|
tmp = obj.send(__method__, opts, &block)
|
|
113
|
-
tmp.send("#{primary_key_name}=", nil)
|
|
121
|
+
tmp.send("#{primary_key_name}=", nil)
|
|
114
122
|
tmp.send("#{reverse_association_name.to_s}=", kopy) if reverse_association_name
|
|
115
123
|
tmp
|
|
116
124
|
end
|
|
117
125
|
when :has_and_belongs_to_many
|
|
118
|
-
primary_key_name =
|
|
119
|
-
|
|
126
|
+
primary_key_name = association_reflection.foreign_key.to_s
|
|
127
|
+
|
|
120
128
|
reverse_association_name = association_reflection.klass.reflect_on_all_associations.detect do |a|
|
|
121
129
|
(a.macro == :has_and_belongs_to_many) && (a.association_foreign_key.to_s == primary_key_name)
|
|
122
130
|
end.try(:name)
|
|
@@ -126,16 +134,16 @@ class ActiveRecord::Base
|
|
|
126
134
|
obj
|
|
127
135
|
end
|
|
128
136
|
end
|
|
129
|
-
|
|
137
|
+
|
|
130
138
|
kopy.send("#{association}=", cloned_object)
|
|
131
139
|
end
|
|
132
140
|
end
|
|
133
141
|
|
|
134
142
|
return kopy
|
|
135
143
|
end
|
|
136
|
-
|
|
137
|
-
class AssociationNotFoundException < StandardError; end
|
|
144
|
+
|
|
145
|
+
class AssociationNotFoundException < StandardError; end
|
|
138
146
|
end
|
|
139
|
-
|
|
147
|
+
|
|
140
148
|
include DeepCloneable
|
|
141
|
-
end
|
|
149
|
+
end
|
data/test/models.rb
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
module Animal
|
|
2
2
|
class Human < ActiveRecord::Base
|
|
3
3
|
has_many :pigs
|
|
4
|
-
|
|
5
|
-
has_many :ownerships
|
|
4
|
+
|
|
5
|
+
has_many :ownerships
|
|
6
6
|
has_many :chickens, :through => :ownerships
|
|
7
7
|
end
|
|
8
8
|
class Pig < ActiveRecord::Base
|
|
9
9
|
belongs_to :human
|
|
10
10
|
end
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
class Chicken < ActiveRecord::Base
|
|
13
13
|
has_many :ownerships
|
|
14
14
|
has_many :humans, :through => :ownerships
|
|
15
|
-
end
|
|
16
|
-
|
|
15
|
+
end
|
|
16
|
+
|
|
17
17
|
class Ownership < ActiveRecord::Base
|
|
18
18
|
belongs_to :human
|
|
19
19
|
belongs_to :chicken
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
validates_uniqueness_of :chicken_id, :scope => :human_id
|
|
22
|
-
end
|
|
22
|
+
end
|
|
23
|
+
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
class GoldPiece < ActiveRecord::Base; belongs_to :treasure end
|
|
@@ -50,4 +51,14 @@ end
|
|
|
50
51
|
|
|
51
52
|
class Car < ActiveRecord::Base
|
|
52
53
|
has_and_belongs_to_many :people
|
|
53
|
-
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class ChildWithValidation < ActiveRecord::Base
|
|
57
|
+
belongs_to :parent, :class_name => 'ParentWithValidation'
|
|
58
|
+
validates :name, :presence => true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class ParentWithValidation < ActiveRecord::Base
|
|
62
|
+
has_many :children, :class_name => 'ChildWithValidation'
|
|
63
|
+
validates :name, :presence => true
|
|
64
|
+
end
|
data/test/schema.rb
CHANGED
|
@@ -6,49 +6,49 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
|
6
6
|
t.column :ship_id, :integer
|
|
7
7
|
t.column :ship_type, :string
|
|
8
8
|
end
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
create_table :parrots, :force => true do |t|
|
|
11
11
|
t.column :name, :string
|
|
12
12
|
t.column :pirate_id, :integer
|
|
13
13
|
end
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
create_table :mateys, :force => true do |t|
|
|
16
16
|
t.column :name, :string
|
|
17
17
|
t.column :pirate_id, :integer
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
create_table :treasures, :force => true do |t|
|
|
21
21
|
t.column :found_at, :string
|
|
22
22
|
t.column :owner, :integer
|
|
23
|
-
t.column :matey_id, :integer
|
|
23
|
+
t.column :matey_id, :integer
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
create_table :gold_pieces, :force => true do |t|
|
|
27
27
|
t.column :treasure_id, :integer
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
create_table :battle_ships, :force => true do |t|
|
|
31
31
|
t.column :name, :string
|
|
32
|
-
end
|
|
33
|
-
|
|
32
|
+
end
|
|
33
|
+
|
|
34
34
|
create_table :pigs, :force => true do |t|
|
|
35
35
|
t.column :name, :string
|
|
36
36
|
t.column :human_id, :integer
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
create_table :humen, :force => true do |t|
|
|
40
40
|
t.column :name, :string
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
create_table :chickens, :force => true do |t|
|
|
44
44
|
t.column :name, :string
|
|
45
|
-
end
|
|
46
|
-
|
|
45
|
+
end
|
|
46
|
+
|
|
47
47
|
create_table :ownerships, :force => true do |t|
|
|
48
48
|
t.column :human_id, :integer
|
|
49
|
-
t.column :chicken_id, :integer
|
|
49
|
+
t.column :chicken_id, :integer
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
create_table :cars, :force => true do |t|
|
|
53
53
|
t.column :name, :string
|
|
54
54
|
end
|
|
@@ -60,6 +60,15 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
|
60
60
|
create_table :cars_people, :id => false, :force => true do |t|
|
|
61
61
|
t.column :car_id, :integer
|
|
62
62
|
t.column :person_id, :integer
|
|
63
|
-
end
|
|
64
|
-
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
create_table :parent_with_validations, :force => true do |t|
|
|
66
|
+
t.column :name, :string
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
create_table :child_with_validations, :force => true do |t|
|
|
70
|
+
t.column :name, :string
|
|
71
|
+
t.column :parent_with_validation_id, :integer
|
|
72
|
+
end
|
|
73
|
+
|
|
65
74
|
end
|
data/test/test_deep_cloneable.rb
CHANGED
|
@@ -2,10 +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
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
@@clone_method = ActiveRecord::VERSION::MAJOR >= 3 && ActiveRecord::VERSION::MINOR > 0 ? :dup : :clone
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
9
|
@jack = Pirate.create(:name => 'Jack Sparrow', :nick_name => 'Captain Jack', :age => 30)
|
|
10
10
|
@polly = Parrot.create(:name => 'Polly', :pirate => @jack)
|
|
11
11
|
@john = Matey.create(:name => 'John', :pirate => @jack)
|
|
@@ -143,30 +143,30 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
|
143
143
|
assert dup_human.new_record?
|
|
144
144
|
assert dup_human.save
|
|
145
145
|
assert_equal 1, dup_human.pigs.count
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
@human2 = Animal::Human.create :name => "John"
|
|
148
148
|
@pig2 = @human2.pigs.create :name => 'small pig'
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
dup_human_2 = @human.send(@@clone_method, :include => [:pigs])
|
|
151
151
|
assert dup_human_2.new_record?
|
|
152
152
|
assert dup_human_2.save
|
|
153
153
|
assert_equal 1, dup_human_2.pigs.count
|
|
154
|
-
end
|
|
155
|
-
|
|
154
|
+
end
|
|
155
|
+
|
|
156
156
|
def test_should_dup_many_to_many_associations
|
|
157
|
-
@human = Animal::Human.create :name => "Michael"
|
|
158
|
-
@human2 = Animal::Human.create :name => "Jack"
|
|
157
|
+
@human = Animal::Human.create :name => "Michael"
|
|
158
|
+
@human2 = Animal::Human.create :name => "Jack"
|
|
159
159
|
@chicken1 = Animal::Chicken.create :name => 'Chick1'
|
|
160
|
-
@chicken2 = Animal::Chicken.create :name => 'Chick2'
|
|
160
|
+
@chicken2 = Animal::Chicken.create :name => 'Chick2'
|
|
161
161
|
@human.chickens << [@chicken1, @chicken2]
|
|
162
|
-
@human2.chickens << [@chicken1, @chicken2]
|
|
163
|
-
|
|
162
|
+
@human2.chickens << [@chicken1, @chicken2]
|
|
163
|
+
|
|
164
164
|
dup_human = @human.send(@@clone_method, :include => :ownerships)
|
|
165
165
|
assert dup_human.new_record?
|
|
166
166
|
assert dup_human.save
|
|
167
|
-
assert_equal 2, dup_human.chickens.count
|
|
168
|
-
end
|
|
169
|
-
|
|
167
|
+
assert_equal 2, dup_human.chickens.count
|
|
168
|
+
end
|
|
169
|
+
|
|
170
170
|
def test_should_dup_with_block
|
|
171
171
|
dup = @jack.send(@@clone_method, :include => :parrot) do |original, kopy|
|
|
172
172
|
kopy.cloned_from_id = original.id
|
|
@@ -177,7 +177,7 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
|
177
177
|
assert_equal @jack.id, dup.cloned_from_id
|
|
178
178
|
assert_equal @jack.parrot.id, dup.parrot.cloned_from_id
|
|
179
179
|
end
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
def test_should_dup_habtm_associations
|
|
182
182
|
@person1 = Person.create :name => "Bill"
|
|
183
183
|
@person2 = Person.create :name => "Ted"
|
|
@@ -201,5 +201,61 @@ class TestDeepCloneable < Test::Unit::TestCase
|
|
|
201
201
|
assert_equal @person1.cars, dup_person.cars
|
|
202
202
|
assert_equal 2, dup_person.cars.count
|
|
203
203
|
end
|
|
204
|
-
|
|
204
|
+
|
|
205
|
+
def test_parent_validations_run_on_save_after_clone
|
|
206
|
+
child = ChildWithValidation.create :name => 'Jimmy'
|
|
207
|
+
parent = ParentWithValidation.new :children => [child]
|
|
208
|
+
parent.save :validate => false
|
|
209
|
+
|
|
210
|
+
dup_parent = parent.dup :include => :children
|
|
211
|
+
|
|
212
|
+
assert !dup_parent.save
|
|
213
|
+
assert dup_parent.new_record?
|
|
214
|
+
assert !dup_parent.valid?
|
|
215
|
+
assert dup_parent.children.first.valid?
|
|
216
|
+
assert_equal dup_parent.errors.messages, :name => ["can't be blank"]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def test_parent_validations_dont_run_on_save_after_clone
|
|
220
|
+
child = ChildWithValidation.create :name => 'Jimmy'
|
|
221
|
+
parent = ParentWithValidation.new :children => [child]
|
|
222
|
+
parent.save :validate => false
|
|
223
|
+
|
|
224
|
+
dup_parent = parent.dup :include => :children, :validate => false
|
|
225
|
+
|
|
226
|
+
assert dup_parent.save
|
|
227
|
+
assert !dup_parent.new_record?
|
|
228
|
+
assert !dup_parent.valid?
|
|
229
|
+
assert dup_parent.children.first.valid?
|
|
230
|
+
assert_equal dup_parent.errors.messages, :name => ["can't be blank"]
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def test_child_validations_run_on_save_after_clone
|
|
234
|
+
child = ChildWithValidation.new
|
|
235
|
+
child.save :validate => false
|
|
236
|
+
parent = ParentWithValidation.create :name => 'John', :children => [child]
|
|
237
|
+
|
|
238
|
+
dup_parent = parent.dup :include => :children
|
|
239
|
+
|
|
240
|
+
assert !dup_parent.save
|
|
241
|
+
assert dup_parent.new_record?
|
|
242
|
+
assert !dup_parent.valid?
|
|
243
|
+
assert !dup_parent.children.first.valid?
|
|
244
|
+
assert_equal dup_parent.errors.messages, :children => ["is invalid"]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def test_child_validations_run_on_save_after_clone
|
|
248
|
+
child = ChildWithValidation.new
|
|
249
|
+
child.save :validate => false
|
|
250
|
+
parent = ParentWithValidation.create :name => 'John', :children => [child]
|
|
251
|
+
|
|
252
|
+
dup_parent = parent.dup :include => :children, :validate => false
|
|
253
|
+
|
|
254
|
+
assert dup_parent.save
|
|
255
|
+
assert !dup_parent.new_record?
|
|
256
|
+
assert !dup_parent.valid?
|
|
257
|
+
assert !dup_parent.children.first.valid?
|
|
258
|
+
assert_equal dup_parent.errors.messages, :children => ["is invalid"]
|
|
259
|
+
end
|
|
260
|
+
|
|
205
261
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: deep_cloneable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.1
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,16 +9,16 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2013-03-06 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
|
-
name:
|
|
15
|
+
name: activerecord
|
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ! '>='
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: '
|
|
21
|
+
version: '3.1'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -26,7 +26,7 @@ dependencies:
|
|
|
26
26
|
requirements:
|
|
27
27
|
- - ! '>='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: '
|
|
29
|
+
version: '3.1'
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
31
|
name: jeweler
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -81,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
81
81
|
version: '0'
|
|
82
82
|
segments:
|
|
83
83
|
- 0
|
|
84
|
-
hash:
|
|
84
|
+
hash: 250465939979745511
|
|
85
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
86
|
none: false
|
|
87
87
|
requirements:
|
|
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
90
90
|
version: '0'
|
|
91
91
|
requirements: []
|
|
92
92
|
rubyforge_project:
|
|
93
|
-
rubygems_version: 1.8.
|
|
93
|
+
rubygems_version: 1.8.24
|
|
94
94
|
signing_key:
|
|
95
95
|
specification_version: 3
|
|
96
96
|
summary: This gem gives every ActiveRecord::Base object the possibility to do a deep
|