deep_cloneable 2.3.2 → 2.4.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.
- 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
|