deep_cloneable 2.3.2 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +20 -0
- data/.travis.yml +2 -2
- data/CHANGELOG.md +13 -1
- data/Gemfile +6 -6
- data/LICENSE +1 -1
- data/VERSION +1 -1
- data/deep_cloneable.gemspec +4 -3
- data/lib/deep_cloneable.rb +103 -89
- data/readme.md +34 -3
- data/test/models.rb +12 -3
- data/test/schema.rb +11 -1
- data/test/test_deep_cloneable.rb +75 -48
- data/test/test_helper.rb +9 -9
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9bc79180e45ac09676614be579f12550f6e9e4e123a320bb42ce7675dfe95eb
|
4
|
+
data.tar.gz: 39db7d4d43ee2dee821d16dce99feb1fcd70b20878d6bb37b9283b51213ed9b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4908868fbfd325c6953c8c68fbce7ce935349c9eedf206579b3f6433bfa1073f1f2c2c10cff81ce2ccd212c6db921d0be904784062318d98d1fd69d03eb21f4
|
7
|
+
data.tar.gz: f8fd74ad2f5c95d9cecbd5df6450b9bf551a0aba708096b6cfbbee777d3ba43e6b1f64447c8cac03c1fdacf8867880a7ac8f2f2297fe17471b9881ea88097afb
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Metrics:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Security/YAMLLoad:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Style/ClassAndModuleChildren:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/Documentation:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/HashSyntax:
|
14
|
+
EnforcedStyle: hash_rockets
|
15
|
+
|
16
|
+
Style/Lambda:
|
17
|
+
EnforcedStyle: lambda
|
18
|
+
|
19
|
+
Style/SymbolArray:
|
20
|
+
EnforcedStyle: brackets
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
|
+
|
9
|
+
## [2.4.0] - 2019-01-10
|
10
|
+
### Added
|
11
|
+
- Support `has_one` and `has_one - through`
|
12
|
+
### Changed
|
13
|
+
- Lint code via Rubocop
|
14
|
+
- Refactor code to improve readability
|
15
|
+
- Use HTTPS in the gemspec website URL
|
16
|
+
|
17
|
+
## [2.3.2] - 2018-04-11
|
8
18
|
### Added
|
9
19
|
- Support for Ruby 2.4 and 2.5
|
10
20
|
- Support for Rails 5.1 and 5.2
|
@@ -156,7 +166,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
156
166
|
### Fixed
|
157
167
|
- Convert existing code to a gem
|
158
168
|
|
159
|
-
[Unreleased]: https://github.com/moiristo/deep_cloneable/compare/v2.
|
169
|
+
[Unreleased]: https://github.com/moiristo/deep_cloneable/compare/v2.4.0...HEAD
|
170
|
+
[2.4.0]: https://github.com/moiristo/deep_cloneable/compare/v2.3.2...v2.4.0
|
171
|
+
[2.3.2]: https://github.com/moiristo/deep_cloneable/compare/v2.3.1...v2.3.2
|
160
172
|
[2.3.1]: https://github.com/moiristo/deep_cloneable/compare/v2.3.0...v2.3.1
|
161
173
|
[2.3.0]: https://github.com/moiristo/deep_cloneable/compare/v2.2.2...v2.3.0
|
162
174
|
[2.2.2]: https://github.com/moiristo/deep_cloneable/compare/v2.2.1...v2.2.2
|
data/Gemfile
CHANGED
@@ -2,14 +2,14 @@ source 'http://rubygems.org'
|
|
2
2
|
|
3
3
|
gem 'activerecord', '>= 3.1.0', '< 6'
|
4
4
|
|
5
|
+
gem 'git', '~> 1.2.9', :group => :test
|
5
6
|
gem 'highline', '~> 1.6.0', :group => :test
|
6
|
-
gem 'rake', '~> 10.4', :group => :test
|
7
7
|
gem 'rack', '~> 1.6', :group => :test
|
8
|
-
gem '
|
8
|
+
gem 'rake', '~> 10.4', :group => :test
|
9
9
|
|
10
|
-
gem 'minitest', :group => :test
|
11
10
|
gem 'appraisal', :group => :test
|
12
|
-
gem 'sqlite3', :group => :test
|
13
|
-
gem 'rdoc', '>= 2.4.2', '< 6', :group => :test
|
14
|
-
gem 'nokogiri', '~> 1.5.0', :group => :test
|
15
11
|
gem 'jeweler', :group => :test
|
12
|
+
gem 'minitest', :group => :test
|
13
|
+
gem 'nokogiri', '~> 1.5.0', :group => :test
|
14
|
+
gem 'rdoc', '>= 2.4.2', '< 6', :group => :test
|
15
|
+
gem 'sqlite3', :group => :test
|
data/LICENSE
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.0
|
data/deep_cloneable.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: deep_cloneable 2.
|
5
|
+
# stub: deep_cloneable 2.4.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "deep_cloneable".freeze
|
9
|
-
s.version = "2.
|
9
|
+
s.version = "2.4.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Reinier de Lange".freeze]
|
14
|
-
s.date = "
|
14
|
+
s.date = "2019-01-10"
|
15
15
|
s.description = "Extends the functionality of ActiveRecord::Base#dup to perform a deep clone that includes user specified associations. ".freeze
|
16
16
|
s.email = "rjdelange@icloud.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".document",
|
22
|
+
".rubocop.yml",
|
22
23
|
".travis.yml",
|
23
24
|
"Appraisals",
|
24
25
|
"CHANGELOG.md",
|
data/lib/deep_cloneable.rb
CHANGED
@@ -1,73 +1,72 @@
|
|
1
|
-
require
|
1
|
+
require 'active_record'
|
2
2
|
|
3
3
|
class ActiveRecord::Base
|
4
4
|
module DeepCloneable
|
5
|
-
|
6
5
|
# Deep dups an ActiveRecord model. See README.rdoc
|
7
|
-
def deep_clone
|
6
|
+
def deep_clone(*args, &block)
|
8
7
|
options = args[0] || {}
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
dictionary = options[:dictionary]
|
10
|
+
dictionary ||= {} if options.delete(:use_dictionary)
|
12
11
|
|
13
|
-
kopy =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
kopy = if dictionary
|
13
|
+
find_in_dictionary_or_dup(dictionary)
|
14
|
+
else
|
15
|
+
dup
|
16
|
+
end
|
18
17
|
|
19
|
-
|
18
|
+
yield(self, kopy) if block
|
20
19
|
|
21
20
|
deep_exceptions = {}
|
22
21
|
if options[:except]
|
23
|
-
exceptions = options[:except]
|
22
|
+
exceptions = array_wrap(options[:except])
|
24
23
|
exceptions.each do |attribute|
|
25
|
-
dup_default_attribute_value_to(kopy, attribute, self) unless attribute.
|
24
|
+
dup_default_attribute_value_to(kopy, attribute, self) unless attribute.is_a?(Hash)
|
26
25
|
end
|
27
|
-
deep_exceptions = exceptions.select{|e| e.
|
26
|
+
deep_exceptions = exceptions.select { |e| e.is_a?(Hash) }.inject({}) { |m, h| m.merge(h) }
|
28
27
|
end
|
29
28
|
|
30
29
|
deep_onlinesses = {}
|
31
30
|
if options[:only]
|
32
|
-
onlinesses = options[:only]
|
33
|
-
object_attrs = kopy.attributes.keys.collect
|
31
|
+
onlinesses = array_wrap(options[:only])
|
32
|
+
object_attrs = kopy.attributes.keys.collect(&:to_sym)
|
34
33
|
exceptions = object_attrs - onlinesses
|
35
34
|
exceptions.each do |attribute|
|
36
|
-
dup_default_attribute_value_to(kopy, attribute, self) unless attribute.
|
35
|
+
dup_default_attribute_value_to(kopy, attribute, self) unless attribute.is_a?(Hash)
|
37
36
|
end
|
38
|
-
deep_onlinesses = onlinesses.select{|e| e.
|
37
|
+
deep_onlinesses = onlinesses.select { |e| e.is_a?(Hash) }.inject({}) { |m, h| m.merge(h) }
|
39
38
|
end
|
40
39
|
|
41
40
|
if options[:include]
|
42
41
|
normalized_includes_list(options[:include]).each do |association, conditions_or_deep_associations|
|
43
42
|
conditions = {}
|
44
43
|
|
45
|
-
if association.
|
44
|
+
if association.is_a? Hash
|
46
45
|
conditions_or_deep_associations = association[association.keys.first]
|
47
46
|
association = association.keys.first
|
48
47
|
end
|
49
48
|
|
50
|
-
if conditions_or_deep_associations.
|
49
|
+
if conditions_or_deep_associations.is_a?(Hash)
|
51
50
|
conditions_or_deep_associations = conditions_or_deep_associations.dup
|
52
51
|
conditions[:if] = conditions_or_deep_associations.delete(:if) if conditions_or_deep_associations[:if]
|
53
52
|
conditions[:unless] = conditions_or_deep_associations.delete(:unless) if conditions_or_deep_associations[:unless]
|
54
|
-
elsif conditions_or_deep_associations.
|
53
|
+
elsif conditions_or_deep_associations.is_a?(Array)
|
55
54
|
conditions_or_deep_associations = conditions_or_deep_associations.dup
|
56
|
-
conditions_or_deep_associations.delete_if {|entry| conditions.merge!(entry) if entry.is_a?(Hash) && (entry.key?(:if) || entry.key?(:unless)) }
|
55
|
+
conditions_or_deep_associations.delete_if { |entry| conditions.merge!(entry) if entry.is_a?(Hash) && (entry.key?(:if) || entry.key?(:unless)) }
|
57
56
|
end
|
58
57
|
|
59
58
|
dup_options = {}
|
60
|
-
dup_options
|
61
|
-
dup_options
|
62
|
-
dup_options
|
63
|
-
dup_options
|
64
|
-
dup_options
|
59
|
+
dup_options[:include] = conditions_or_deep_associations if conditions_or_deep_associations.present?
|
60
|
+
dup_options[:except] = deep_exceptions[association] if deep_exceptions[association]
|
61
|
+
dup_options[:only] = deep_onlinesses[association] if deep_onlinesses[association]
|
62
|
+
dup_options[:dictionary] = dictionary if dictionary
|
63
|
+
dup_options[:skip_missing_associations] = options[:skip_missing_associations] if options[:skip_missing_associations]
|
65
64
|
|
66
|
-
if association_reflection = self.class.reflect_on_association(association)
|
65
|
+
if (association_reflection = self.class.reflect_on_association(association))
|
67
66
|
if options[:validate] == false
|
68
67
|
kopy.instance_eval do
|
69
68
|
# Force :validate => false on all saves.
|
70
|
-
def perform_validations(options={})
|
69
|
+
def perform_validations(options = {})
|
71
70
|
options[:validate] = false
|
72
71
|
super(options)
|
73
72
|
end
|
@@ -85,105 +84,112 @@ class ActiveRecord::Base
|
|
85
84
|
|
86
85
|
kopy.send("#{association}=", duped_object)
|
87
86
|
elsif !options[:skip_missing_associations]
|
88
|
-
raise AssociationNotFoundException
|
87
|
+
raise AssociationNotFoundException, "#{self.class}##{association}"
|
89
88
|
end
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
93
|
-
|
92
|
+
kopy
|
94
93
|
end
|
95
94
|
|
96
|
-
|
95
|
+
protected
|
97
96
|
|
98
|
-
def
|
97
|
+
def find_in_dictionary_or_dup(dictionary, dup_on_miss = true)
|
99
98
|
tableized_class = self.class.name.tableize.to_sym
|
100
|
-
|
101
|
-
dict_val =
|
102
|
-
dict_val.nil? && dup_on_miss ?
|
99
|
+
dictionary[tableized_class] ||= {}
|
100
|
+
dict_val = dictionary[tableized_class][self]
|
101
|
+
dict_val.nil? && dup_on_miss ? dictionary[tableized_class][self] = dup : dict_val
|
103
102
|
end
|
104
103
|
|
105
|
-
|
106
|
-
|
107
|
-
def dup_default_attribute_value_to(kopy, attribute, origin)
|
108
|
-
kopy[attribute] = origin.class.column_defaults.dup[attribute.to_s]
|
109
|
-
end
|
104
|
+
private
|
110
105
|
|
111
|
-
def dup_belongs_to_association
|
112
|
-
object =
|
113
|
-
object = nil if options[:conditions].any? && evaluate_conditions(object, options[:conditions])
|
106
|
+
def dup_belongs_to_association(options, &block)
|
107
|
+
object = deep_cloneable_object_for(options[:association], options[:conditions])
|
114
108
|
object && object.deep_clone(options[:dup_options], &block)
|
115
109
|
end
|
116
110
|
|
117
|
-
def dup_has_one_association
|
111
|
+
def dup_has_one_association(options, &block)
|
118
112
|
dup_belongs_to_association options, &block
|
119
113
|
end
|
120
114
|
|
121
|
-
def dup_has_many_association
|
122
|
-
|
115
|
+
def dup_has_many_association(options, &block)
|
116
|
+
foreign_key = options[:reflection].foreign_key.to_s
|
117
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :belongs_to)
|
118
|
+
objects = deep_cloneable_objects_for(options[:association], options[:conditions])
|
123
119
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
end.try(:name)
|
120
|
+
objects.map do |object|
|
121
|
+
object = object.deep_clone(options[:dup_options], &block)
|
122
|
+
object.send("#{foreign_key}=", nil)
|
123
|
+
object.send("#{reverse_association.name}=", options[:copy]) if reverse_association
|
124
|
+
object
|
130
125
|
end
|
126
|
+
end
|
131
127
|
|
132
|
-
|
133
|
-
|
128
|
+
def dup_has_one_through_association(options, &block)
|
129
|
+
foreign_key = options[:reflection].through_reflection.foreign_key.to_s
|
130
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :has_one, :association_foreign_key)
|
134
131
|
|
135
|
-
|
136
|
-
|
137
|
-
tmp.send("#{primary_key_name}=", nil)
|
138
|
-
tmp.send("#{reverse_association_name.to_s}=", options[:copy]) if reverse_association_name
|
139
|
-
tmp
|
140
|
-
end
|
132
|
+
object = deep_cloneable_object_for(options[:association], options[:conditions])
|
133
|
+
object && process_joined_object_for_deep_clone(object, options.merge(:reverse_association => reverse_association), &block)
|
141
134
|
end
|
142
135
|
|
143
|
-
def dup_has_many_through_association
|
144
|
-
|
145
|
-
|
146
|
-
|
136
|
+
def dup_has_many_through_association(options, &block)
|
137
|
+
foreign_key = options[:reflection].through_reflection.foreign_key.to_s
|
138
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :has_many, :association_foreign_key)
|
139
|
+
|
140
|
+
objects = deep_cloneable_objects_for(options[:association], options[:conditions])
|
141
|
+
objects.map { |object| process_joined_object_for_deep_clone(object, options.merge(:reverse_association => reverse_association), &block) }
|
147
142
|
end
|
148
143
|
|
149
|
-
def dup_has_and_belongs_to_many_association
|
150
|
-
|
151
|
-
|
152
|
-
|
144
|
+
def dup_has_and_belongs_to_many_association(options, &block)
|
145
|
+
foreign_key = options[:reflection].foreign_key.to_s
|
146
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :has_and_belongs_to_many, :association_foreign_key)
|
147
|
+
|
148
|
+
objects = deep_cloneable_objects_for(options[:association], options[:conditions])
|
149
|
+
objects.map { |object| process_joined_object_for_deep_clone(object, options.merge(:reverse_association => reverse_association), &block) }
|
153
150
|
end
|
154
151
|
|
155
|
-
def
|
156
|
-
if
|
157
|
-
|
152
|
+
def find_reverse_association(source_reflection, primary_key_name, macro, matcher = :foreign_key)
|
153
|
+
if source_reflection.inverse_of.present?
|
154
|
+
source_reflection.inverse_of
|
158
155
|
else
|
159
|
-
|
160
|
-
(reflection.macro ==
|
161
|
-
end
|
156
|
+
source_reflection.klass.reflect_on_all_associations.detect do |reflection|
|
157
|
+
reflection != source_reflection && (macro.nil? || reflection.macro == macro) && (reflection.send(matcher).to_s == primary_key_name)
|
158
|
+
end
|
162
159
|
end
|
160
|
+
end
|
163
161
|
|
164
|
-
|
165
|
-
|
162
|
+
def deep_cloneable_object_for(single_association, conditions)
|
163
|
+
object = send(single_association)
|
164
|
+
evaluate_conditions(object, conditions) && object
|
165
|
+
end
|
166
166
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
167
|
+
def deep_cloneable_objects_for(many_association, conditions)
|
168
|
+
send(many_association).select { |object| evaluate_conditions(object, conditions) }
|
169
|
+
end
|
170
|
+
|
171
|
+
def process_joined_object_for_deep_clone(object, options, &block)
|
172
|
+
if (dictionary = options[:dup_options][:dictionary]) && object.find_in_dictionary_or_dup(dictionary, false)
|
173
|
+
object = object.deep_clone(options[:dup_options], &block)
|
174
|
+
elsif options[:reverse_association]
|
175
|
+
object.send(options[:reverse_association].name).target << options[:copy]
|
175
176
|
end
|
177
|
+
object
|
178
|
+
end
|
179
|
+
|
180
|
+
def evaluate_conditions(object, conditions)
|
181
|
+
conditions.none? || (conditions[:if] && conditions[:if].call(object)) || (conditions[:unless] && !conditions[:unless].call(object))
|
176
182
|
end
|
177
183
|
|
178
|
-
def
|
179
|
-
|
184
|
+
def dup_default_attribute_value_to(kopy, attribute, origin)
|
185
|
+
kopy[attribute] = origin.class.column_defaults.dup[attribute.to_s]
|
180
186
|
end
|
181
187
|
|
182
|
-
def normalized_includes_list
|
188
|
+
def normalized_includes_list(includes)
|
183
189
|
list = []
|
184
190
|
Array(includes).each do |item|
|
185
191
|
if item.is_a?(Hash) && item.size > 1
|
186
|
-
item.each{|key, value| list << { key => value } }
|
192
|
+
item.each { |key, value| list << { key => value } }
|
187
193
|
else
|
188
194
|
list << item
|
189
195
|
end
|
@@ -192,6 +198,14 @@ class ActiveRecord::Base
|
|
192
198
|
list
|
193
199
|
end
|
194
200
|
|
201
|
+
def array_wrap(object)
|
202
|
+
if object.respond_to?(:to_ary)
|
203
|
+
object.to_ary || [object]
|
204
|
+
else
|
205
|
+
[object]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
195
209
|
class AssociationNotFoundException < StandardError; end
|
196
210
|
|
197
211
|
ActiveRecord::Base.class_eval { protected :initialize_dup } if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1
|
data/readme.md
CHANGED
@@ -15,7 +15,7 @@ This gem gives every ActiveRecord::Base object the possibility to do a deep clon
|
|
15
15
|
* Add deep_cloneable to your Gemfile:
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
gem 'deep_cloneable', '~> 2.
|
18
|
+
gem 'deep_cloneable', '~> 2.4.0'
|
19
19
|
```
|
20
20
|
|
21
21
|
## Upgrading from v1
|
@@ -114,7 +114,9 @@ end
|
|
114
114
|
|
115
115
|
*Note*: Using `deep_clone` with a block will also pass the associated objects that are being cloned to the block, so be sure to check whether the object actually responds to your method of choice.
|
116
116
|
|
117
|
-
### Cloning models with files
|
117
|
+
### Cloning models with files
|
118
|
+
|
119
|
+
#### Carrierwave
|
118
120
|
|
119
121
|
If you are cloning models that have associated files through Carrierwave these will not get transferred automatically. To overcome the issue you need to explicitly set the file attribute.
|
120
122
|
|
@@ -125,6 +127,35 @@ pirate.deep_clone include: :parrot do |original, kopy|
|
|
125
127
|
end
|
126
128
|
```
|
127
129
|
|
130
|
+
#### ActiveStorage
|
131
|
+
|
132
|
+
For ActiveStorage, you have two options: you can either make a full copy, or share data blobs between two records.
|
133
|
+
|
134
|
+
##### Full copy example
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
pirate.deep_clone include: :parrot do |original, kopy|
|
138
|
+
if kopy.is_a?(Pirate) && original.avatar.attached?
|
139
|
+
ActiveStorage::Downloader.new(original.avatar).download_blob_to_tempfile do |tempfile|
|
140
|
+
kopy.avatar.attach({
|
141
|
+
io: tempfile,
|
142
|
+
filename: original.avatar.blob.filename,
|
143
|
+
content_type: original.avatar.blob.content_type
|
144
|
+
})
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
|
151
|
+
##### Shallow copy example
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
pirate.deep_clone include: :parrot do |original, kopy|
|
155
|
+
kopy.avatar.attach(original.avatar.blob) if kopy.is_a?(Pirate) && original.avatar.attached?
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
128
159
|
### Skipping missing associations
|
129
160
|
|
130
161
|
By default, deep_cloneable will throw a `ActiveRecord::Base::DeepCloneable::AssociationNotFoundException` error when an association cannot be found. You can also skip missing associations by specifying `skip_missing_associations` if needed, for example when you have associations on some (but not all) subclasses of an STI model:
|
@@ -145,4 +176,4 @@ pirate.deep_clone include: [:parrot, :rum], skip_missing_associations: true
|
|
145
176
|
|
146
177
|
### Copyright
|
147
178
|
|
148
|
-
Copyright ©
|
179
|
+
Copyright © 2019 Reinier de Lange. See LICENSE for details.
|
data/test/models.rb
CHANGED
@@ -26,14 +26,12 @@ module Animal
|
|
26
26
|
has_many :birds
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
29
|
class Ownership < ActiveRecord::Base
|
31
30
|
belongs_to :human
|
32
31
|
belongs_to :chicken
|
33
32
|
|
34
33
|
validates_uniqueness_of :chicken_id, :scope => :human_id
|
35
34
|
end
|
36
|
-
|
37
35
|
end
|
38
36
|
|
39
37
|
class GoldPiece < ActiveRecord::Base; belongs_to :treasure end
|
@@ -89,7 +87,7 @@ class ChildWithValidation < ActiveRecord::Base
|
|
89
87
|
end
|
90
88
|
|
91
89
|
class ParentWithValidation < ActiveRecord::Base
|
92
|
-
has_many :children, :class_name => 'ChildWithValidation'
|
90
|
+
has_many :children, :class_name => 'ChildWithValidation', :foreign_key => 'parent_id'
|
93
91
|
validates :name, :presence => true
|
94
92
|
end
|
95
93
|
|
@@ -126,6 +124,7 @@ end
|
|
126
124
|
class Contractor < ActiveRecord::Base
|
127
125
|
belongs_to :building
|
128
126
|
has_and_belongs_to_many :apartments
|
127
|
+
has_one :contract
|
129
128
|
end
|
130
129
|
|
131
130
|
class User < ActiveRecord::Base
|
@@ -140,3 +139,13 @@ end
|
|
140
139
|
class Product < ActiveRecord::Base
|
141
140
|
belongs_to :order
|
142
141
|
end
|
142
|
+
|
143
|
+
class Contract < ActiveRecord::Base
|
144
|
+
belongs_to :contractor
|
145
|
+
belongs_to :organization
|
146
|
+
end
|
147
|
+
|
148
|
+
class Organization < ActiveRecord::Base
|
149
|
+
has_one :contract
|
150
|
+
has_one :contractor, :through => :contract
|
151
|
+
end
|
data/test/schema.rb
CHANGED
@@ -90,7 +90,7 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
90
90
|
|
91
91
|
create_table :child_with_validations, :force => true do |t|
|
92
92
|
t.column :name, :string
|
93
|
-
t.column :
|
93
|
+
t.column :parent_id, :integer
|
94
94
|
end
|
95
95
|
|
96
96
|
create_table :parts, :force => true do |t|
|
@@ -143,4 +143,14 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
143
143
|
t.column :name, :string
|
144
144
|
t.column :order_id, :integer
|
145
145
|
end
|
146
|
+
|
147
|
+
create_table :organizations, :force => true do |t|
|
148
|
+
t.column :name, :string
|
149
|
+
end
|
150
|
+
|
151
|
+
create_table :contracts, :force => true do |t|
|
152
|
+
t.column :number, :string
|
153
|
+
t.column :contractor_id, :integer
|
154
|
+
t.column :organization_id, :integer
|
155
|
+
end
|
146
156
|
end
|
data/test/test_deep_cloneable.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class TestDeepCloneable < MiniTest::Unit::TestCase
|
4
|
-
|
5
4
|
def setup
|
6
5
|
@jack = Pirate.create(:name => 'Jack Sparrow', :nick_name => 'Captain Jack', :age => 30)
|
7
6
|
@polly = Parrot.create(:name => 'Polly', :age => 2, :pirate => @jack)
|
@@ -84,7 +83,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
84
83
|
end
|
85
84
|
|
86
85
|
def test_deep_include_association
|
87
|
-
deep_clone = @jack.deep_clone(:include => {:treasures => :gold_pieces})
|
86
|
+
deep_clone = @jack.deep_clone(:include => { :treasures => :gold_pieces })
|
88
87
|
assert deep_clone.new_record?
|
89
88
|
assert deep_clone.save
|
90
89
|
assert_equal 1, deep_clone.treasures.size
|
@@ -110,7 +109,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
110
109
|
end
|
111
110
|
|
112
111
|
def test_multiple_and_deep_include_association
|
113
|
-
deep_clone = @jack.deep_clone(:include => {:treasures => :gold_pieces, :mateys => {}})
|
112
|
+
deep_clone = @jack.deep_clone(:include => { :treasures => :gold_pieces, :mateys => {} })
|
114
113
|
assert deep_clone.new_record?
|
115
114
|
assert deep_clone.save
|
116
115
|
assert_equal 1, deep_clone.treasures.size
|
@@ -119,7 +118,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
119
118
|
end
|
120
119
|
|
121
120
|
def test_multiple_and_deep_include_association_with_array
|
122
|
-
deep_clone = @jack.deep_clone(:include => [{:treasures => :gold_pieces}, :mateys])
|
121
|
+
deep_clone = @jack.deep_clone(:include => [{ :treasures => :gold_pieces }, :mateys])
|
123
122
|
assert deep_clone.new_record?
|
124
123
|
assert deep_clone.save
|
125
124
|
assert_equal 1, deep_clone.treasures.size
|
@@ -128,7 +127,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
128
127
|
end
|
129
128
|
|
130
129
|
def test_multiple_and_deep_include_association_with_array_and_multikey_hash
|
131
|
-
deep_clone = @jack.deep_clone(:include => [:parrot, {:treasures => :gold_pieces, :mateys => {}}])
|
130
|
+
deep_clone = @jack.deep_clone(:include => [:parrot, { :treasures => :gold_pieces, :mateys => {} }])
|
132
131
|
assert deep_clone.new_record?
|
133
132
|
assert deep_clone.save
|
134
133
|
assert_equal 1, deep_clone.treasures.size
|
@@ -153,6 +152,16 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
153
152
|
assert_nil deep_clone.parrot.name
|
154
153
|
end
|
155
154
|
|
155
|
+
def test_should_pass_nested_exception_with_hash_value
|
156
|
+
deep_clone = @jack.deep_clone(:include => :parrot, :except => { :parrot => [:name] })
|
157
|
+
assert deep_clone.new_record?
|
158
|
+
assert deep_clone.save
|
159
|
+
refute_equal deep_clone.parrot, @jack.parrot
|
160
|
+
assert_equal deep_clone.parrot.age, @jack.parrot.age
|
161
|
+
refute_nil @jack.parrot.name
|
162
|
+
assert_nil deep_clone.parrot.name
|
163
|
+
end
|
164
|
+
|
156
165
|
def test_should_pass_nested_onlinesses
|
157
166
|
deep_clone = @jack.deep_clone(:include => :parrot, :only => [:name, { :parrot => [:name] }])
|
158
167
|
assert deep_clone.new_record?
|
@@ -163,6 +172,16 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
163
172
|
assert_nil deep_clone.parrot.age
|
164
173
|
end
|
165
174
|
|
175
|
+
def test_should_pass_nested_onliness_with_hash_value
|
176
|
+
deep_clone = @jack.deep_clone(:include => :parrot, :only => { :parrot => [:name] })
|
177
|
+
assert deep_clone.new_record?
|
178
|
+
assert deep_clone.piastres, []
|
179
|
+
assert deep_clone.save
|
180
|
+
refute_equal deep_clone.parrot, @jack.parrot
|
181
|
+
assert_equal deep_clone.parrot.name, @jack.parrot.name
|
182
|
+
assert_nil deep_clone.parrot.age
|
183
|
+
end
|
184
|
+
|
166
185
|
def test_should_not_double_deep_clone_when_using_dictionary
|
167
186
|
current_matey_count = Matey.count
|
168
187
|
deep_clone = @jack.deep_clone(:include => [:mateys, { :treasures => :matey }], :use_dictionary => true)
|
@@ -176,7 +195,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
176
195
|
current_matey_count = Matey.count
|
177
196
|
|
178
197
|
dict = { :mateys => {} }
|
179
|
-
@jack.mateys.each{|m| dict[:mateys][m] = m.deep_clone }
|
198
|
+
@jack.mateys.each { |m| dict[:mateys][m] = m.deep_clone }
|
180
199
|
|
181
200
|
deep_clone = @jack.deep_clone(:include => [:mateys, { :treasures => :matey }], :dictionary => dict)
|
182
201
|
assert deep_clone.new_record?
|
@@ -186,7 +205,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
186
205
|
end
|
187
206
|
|
188
207
|
def test_should_support_ar_class_under_module
|
189
|
-
@human = Animal::Human.create :name =>
|
208
|
+
@human = Animal::Human.create :name => 'Michael'
|
190
209
|
@pig = Animal::Pig.create :human => @human, :name => 'big pig'
|
191
210
|
|
192
211
|
deep_clone_human = @human.deep_clone(:include => [:pigs])
|
@@ -194,18 +213,18 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
194
213
|
assert deep_clone_human.save
|
195
214
|
assert_equal 1, deep_clone_human.pigs.count
|
196
215
|
|
197
|
-
@human2 = Animal::Human.create :name =>
|
216
|
+
@human2 = Animal::Human.create :name => 'John'
|
198
217
|
@pig2 = @human2.pigs.create :name => 'small pig'
|
199
218
|
|
200
|
-
|
201
|
-
assert
|
202
|
-
assert
|
203
|
-
assert_equal 1,
|
219
|
+
deep_clone_human2 = @human.deep_clone(:include => [:pigs])
|
220
|
+
assert deep_clone_human2.new_record?
|
221
|
+
assert deep_clone_human2.save
|
222
|
+
assert_equal 1, deep_clone_human2.pigs.count
|
204
223
|
end
|
205
224
|
|
206
225
|
def test_should_deep_clone_many_to_many_associations
|
207
|
-
@human = Animal::Human.create :name =>
|
208
|
-
@human2 = Animal::Human.create :name =>
|
226
|
+
@human = Animal::Human.create :name => 'Michael'
|
227
|
+
@human2 = Animal::Human.create :name => 'Jack'
|
209
228
|
@chicken1 = Animal::Chicken.create :name => 'Chick1'
|
210
229
|
@chicken2 = Animal::Chicken.create :name => 'Chick2'
|
211
230
|
@human.chickens << [@chicken1, @chicken2]
|
@@ -219,8 +238,8 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
219
238
|
|
220
239
|
def test_should_skip_missing_associations
|
221
240
|
@earth = Animal::Planet.create :name => 'Earth'
|
222
|
-
@human = Animal::Human.create :name =>
|
223
|
-
@chicken = Animal::Chicken.create :name => 'Chick', :humans => [@human], :planet => @earth
|
241
|
+
@human = Animal::Human.create :name => 'Michael'
|
242
|
+
@chicken = Animal::Chicken.create :name => 'Chick', :humans => [@human], :planet => @earth
|
224
243
|
@dove = Animal::Dove.create :name => 'Dovey', :planet => @earth
|
225
244
|
|
226
245
|
assert_raises ActiveRecord::Base::DeepCloneable::AssociationNotFoundException do
|
@@ -229,7 +248,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
229
248
|
|
230
249
|
deep_clone_earth = @earth.deep_clone(:include => { :birds => :ownerships }, :skip_missing_associations => true)
|
231
250
|
assert_equal 2, deep_clone_earth.birds.size
|
232
|
-
assert deep_clone_earth.birds.detect{|bird| bird.is_a?(Animal::Chicken) }.ownerships.any?
|
251
|
+
assert deep_clone_earth.birds.detect { |bird| bird.is_a?(Animal::Chicken) }.ownerships.any?
|
233
252
|
end
|
234
253
|
|
235
254
|
def test_should_deep_clone_with_block
|
@@ -244,8 +263,8 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
244
263
|
end
|
245
264
|
|
246
265
|
def test_should_deep_clone_habtm_associations
|
247
|
-
@person1 = Person.create :name =>
|
248
|
-
@person2 = Person.create :name =>
|
266
|
+
@person1 = Person.create :name => 'Bill'
|
267
|
+
@person2 = Person.create :name => 'Ted'
|
249
268
|
@car1 = Car.create :name => 'Mustang'
|
250
269
|
@car2 = Car.create :name => 'Camaro'
|
251
270
|
@person1.cars << [@car1, @car2]
|
@@ -269,7 +288,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
269
288
|
|
270
289
|
def test_should_deep_clone_habtm_associations_with_missing_reverse_association
|
271
290
|
@coin = Coin.create :value => 1
|
272
|
-
@person = Person.create :name =>
|
291
|
+
@person = Person.create :name => 'Bill'
|
273
292
|
@coin.people << @person
|
274
293
|
|
275
294
|
deep_clone = @coin.deep_clone :include => :people
|
@@ -286,7 +305,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
286
305
|
deep_clone = student.deep_clone :include => { :student_assignments => :subject }
|
287
306
|
deep_clone.save # Subjects will have been set after save
|
288
307
|
assert_equal 2, deep_clone.subjects.size
|
289
|
-
[subject1, subject2].each{|subject| assert !deep_clone.subjects.include?(subject) }
|
308
|
+
[subject1, subject2].each { |subject| assert !deep_clone.subjects.include?(subject) }
|
290
309
|
end
|
291
310
|
|
292
311
|
def test_parent_validations_run_on_save_after_clone
|
@@ -328,7 +347,7 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
328
347
|
assert deep_clone_parent.new_record?
|
329
348
|
assert !deep_clone_parent.valid?
|
330
349
|
assert !deep_clone_parent.children.first.valid?
|
331
|
-
assert_equal deep_clone_parent.errors.messages, :children => [
|
350
|
+
assert_equal deep_clone_parent.errors.messages, :children => ['is invalid']
|
332
351
|
end
|
333
352
|
|
334
353
|
def test_child_validations_run_on_save_after_clone_without_validation
|
@@ -342,13 +361,13 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
342
361
|
assert !deep_clone_parent.new_record?
|
343
362
|
assert !deep_clone_parent.valid?
|
344
363
|
assert !deep_clone_parent.children.first.valid?
|
345
|
-
assert_equal deep_clone_parent.errors.messages, :children => [
|
364
|
+
assert_equal deep_clone_parent.errors.messages, :children => ['is invalid']
|
346
365
|
end
|
347
366
|
|
348
367
|
def test_self_join_has_many
|
349
368
|
parent_part = Part.create(:name => 'Parent')
|
350
|
-
|
351
|
-
|
369
|
+
Part.create(:name => 'Child 1', :parent_part_id => parent_part.id)
|
370
|
+
Part.create(:name => 'Child 2', :parent_part_id => parent_part.id)
|
352
371
|
|
353
372
|
deep_clone_part = parent_part.deep_clone :include => :child_parts
|
354
373
|
assert deep_clone_part.save
|
@@ -362,7 +381,16 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
362
381
|
|
363
382
|
deep_clone = student.deep_clone :include => :subjects
|
364
383
|
assert_equal 2, deep_clone.subjects.size
|
365
|
-
assert_equal [[student, deep_clone],[student, deep_clone]], deep_clone.subjects.map
|
384
|
+
assert_equal [[student, deep_clone], [student, deep_clone]], deep_clone.subjects.map(&:students)
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_should_include_has_one_through_associations
|
388
|
+
organization = Organization.create(:name => 'organization')
|
389
|
+
contractor = Contractor.create(:name => 'contractor')
|
390
|
+
Contract.create(:number => 12_345, :contractor => contractor, :organization => organization)
|
391
|
+
|
392
|
+
deep_clone = organization.deep_clone(:include => :contractor)
|
393
|
+
assert_equal organization.contractor, deep_clone.contractor
|
366
394
|
end
|
367
395
|
|
368
396
|
def test_should_deep_clone_unsaved_objects
|
@@ -380,14 +408,14 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
380
408
|
subject2 = Subject.create(:name => 'subject 2')
|
381
409
|
student = Student.create(:name => 'Parent', :subjects => [subject1, subject2])
|
382
410
|
|
383
|
-
deep_clone = student.deep_clone :include => { :subjects => { :if => lambda{|subject| subject.name == 'subject 2' } } }
|
411
|
+
deep_clone = student.deep_clone :include => { :subjects => { :if => lambda { |subject| subject.name == 'subject 2' } } }
|
384
412
|
assert_equal 1, deep_clone.subjects.size
|
385
413
|
assert_equal 'subject 2', deep_clone.subjects.first.name
|
386
414
|
|
387
415
|
deep_clone = @jack.deep_clone(:include => {
|
388
|
-
|
389
|
-
|
390
|
-
|
416
|
+
:treasures => { :gold_pieces => { :unless => lambda { |piece| piece.is_a?(Parrot) } } },
|
417
|
+
:mateys => { :if => lambda { |matey| matey.is_a?(GoldPiece) } }
|
418
|
+
})
|
391
419
|
|
392
420
|
assert deep_clone.new_record?
|
393
421
|
assert deep_clone.save
|
@@ -401,21 +429,21 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
401
429
|
subject2 = Subject.create(:name => 'subject 2')
|
402
430
|
student = Student.create(:name => 'Parent', :subjects => [subject1, subject2])
|
403
431
|
|
404
|
-
deep_clone = student.deep_clone(:include => [:subjects => [:if => lambda{|
|
432
|
+
deep_clone = student.deep_clone(:include => [:subjects => [:if => lambda { |_subject| false }]])
|
405
433
|
assert deep_clone.subjects.none?
|
406
434
|
|
407
|
-
deep_clone = student.deep_clone(:include => [:subjects => [:if => lambda{|
|
435
|
+
deep_clone = student.deep_clone(:include => [:subjects => [:if => lambda { |_subject| true }]])
|
408
436
|
assert_equal 2, deep_clone.subjects.size
|
409
437
|
end
|
410
438
|
|
411
439
|
def test_should_reject_copies_if_conditionals_are_passed_with_associations
|
412
|
-
deep_clone = @ship.deep_clone(:include => [:pirates => [:treasures, :mateys, { :unless => lambda {|pirate| pirate.name == 'Jack Sparrow'} }]])
|
440
|
+
deep_clone = @ship.deep_clone(:include => [:pirates => [:treasures, :mateys, { :unless => lambda { |pirate| pirate.name == 'Jack Sparrow' } }]])
|
413
441
|
|
414
442
|
assert deep_clone.new_record?
|
415
443
|
assert deep_clone.save
|
416
444
|
assert_equal 0, deep_clone.pirates.size
|
417
445
|
|
418
|
-
deep_clone = @ship.deep_clone(:include => [:pirates => [:treasures, :mateys, { :if => lambda {|pirate| pirate.name == 'Jack Sparrow'} }]])
|
446
|
+
deep_clone = @ship.deep_clone(:include => [:pirates => [:treasures, :mateys, { :if => lambda { |pirate| pirate.name == 'Jack Sparrow' } }]])
|
419
447
|
assert deep_clone.new_record?
|
420
448
|
assert deep_clone.save
|
421
449
|
assert_equal 1, deep_clone.pirates.size
|
@@ -430,36 +458,36 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
430
458
|
@order2.products << [@product1, @product2]
|
431
459
|
@user.orders << [@order1, @order2]
|
432
460
|
|
433
|
-
deep_clone = @user.deep_clone(:include => [:orders => [:products => [{ :unless => lambda {|product| product.name == 'Ink' }}]]])
|
461
|
+
deep_clone = @user.deep_clone(:include => [:orders => [:products => [{ :unless => lambda { |product| product.name == 'Ink' } }]]])
|
434
462
|
|
435
463
|
assert deep_clone.new_record?
|
436
464
|
assert deep_clone.save
|
437
465
|
assert_equal 1, deep_clone.orders.second.products.size
|
438
466
|
|
439
|
-
deep_clone = @user.deep_clone(:include => [:orders => [:products => [{ :if => lambda {|product| product.name == 'Ink'}}]]])
|
467
|
+
deep_clone = @user.deep_clone(:include => [:orders => [:products => [{ :if => lambda { |product| product.name == 'Ink' } }]]])
|
440
468
|
assert deep_clone.new_record?
|
441
469
|
assert deep_clone.save
|
442
470
|
assert_equal 1, deep_clone.orders.second.products.size
|
443
471
|
end
|
444
472
|
|
445
473
|
def test_should_find_in_dict_for_habtm
|
446
|
-
apt = Apartment.create(:number =>
|
447
|
-
contractor = Contractor.create(:name =>
|
474
|
+
apt = Apartment.create(:number => '101')
|
475
|
+
contractor = Contractor.create(:name => 'contractor', :apartments => [apt])
|
448
476
|
|
449
477
|
apt.contractors = [contractor]
|
450
478
|
apt.save!
|
451
479
|
|
452
|
-
building = Building.create(:name =>
|
480
|
+
building = Building.create(:name => 'Tall Building', :contractors => [contractor], :apartments => [apt])
|
453
481
|
|
454
482
|
deep_clone = building.deep_clone(:include => [
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
483
|
+
:apartments,
|
484
|
+
{
|
485
|
+
:contractors => [
|
486
|
+
:apartments
|
487
|
+
]
|
488
|
+
}
|
489
|
+
],
|
490
|
+
:use_dictionary => true)
|
463
491
|
|
464
492
|
deep_clone.save!
|
465
493
|
|
@@ -472,5 +500,4 @@ class TestDeepCloneable < MiniTest::Unit::TestCase
|
|
472
500
|
assert_nil deep_clone.name
|
473
501
|
refute deep_clone.name_changed?
|
474
502
|
end
|
475
|
-
|
476
503
|
end
|
data/test/test_helper.rb
CHANGED
@@ -13,24 +13,24 @@ require 'active_record'
|
|
13
13
|
I18n.enforce_available_locales = true
|
14
14
|
|
15
15
|
def load_schema
|
16
|
-
config = YAML
|
16
|
+
config = YAML.load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
17
17
|
|
18
|
-
if defined?(ActiveSupport::BufferedLogger)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
ActiveRecord::Base.logger = if defined?(ActiveSupport::BufferedLogger)
|
19
|
+
ActiveSupport::BufferedLogger.new(File.dirname(__FILE__) + '/debug.log')
|
20
|
+
else
|
21
|
+
ActiveSupport::Logger.new(File.dirname(__FILE__) + '/debug.log')
|
22
|
+
end
|
23
23
|
|
24
24
|
db_adapter = ENV['DB']
|
25
25
|
db_adapter ||= 'sqlite3'
|
26
26
|
|
27
27
|
if db_adapter.nil?
|
28
|
-
raise
|
28
|
+
raise 'No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3.'
|
29
29
|
end
|
30
30
|
ActiveRecord::Base.establish_connection(config[db_adapter])
|
31
|
-
load(File.dirname(__FILE__) +
|
31
|
+
load(File.dirname(__FILE__) + '/schema.rb')
|
32
32
|
end
|
33
33
|
|
34
34
|
load_schema
|
35
35
|
require File.dirname(__FILE__) + '/../init.rb'
|
36
|
-
require 'models'
|
36
|
+
require 'models'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deep_cloneable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reinier de Lange
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -39,6 +39,7 @@ extra_rdoc_files:
|
|
39
39
|
- LICENSE
|
40
40
|
files:
|
41
41
|
- ".document"
|
42
|
+
- ".rubocop.yml"
|
42
43
|
- ".travis.yml"
|
43
44
|
- Appraisals
|
44
45
|
- CHANGELOG.md
|