deep_cloneable 2.0.2 → 3.1.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 +5 -5
- data/Appraisals +29 -2
- data/CHANGELOG.md +328 -0
- data/Gemfile +7 -10
- data/Gemfile.lock +27 -63
- data/LICENSE +1 -1
- data/Rakefile +4 -18
- data/deep_cloneable.gemspec +18 -63
- data/init.rb +3 -1
- data/lib/deep_cloneable.rb +12 -130
- data/lib/deep_cloneable/association_not_found_exception.rb +6 -0
- data/lib/deep_cloneable/deep_clone.rb +207 -0
- data/lib/deep_cloneable/skip_validations.rb +10 -0
- data/lib/deep_cloneable/version.rb +3 -0
- data/readme.md +243 -0
- metadata +24 -38
- data/.document +0 -5
- data/.travis.yml +0 -28
- data/README.rdoc +0 -95
- data/VERSION +0 -1
- data/gemfiles/3.1.gemfile +0 -14
- data/gemfiles/3.1.gemfile.lock +0 -75
- data/gemfiles/3.2.gemfile +0 -14
- data/gemfiles/3.2.gemfile.lock +0 -75
- data/gemfiles/4.0.gemfile +0 -14
- data/gemfiles/4.0.gemfile.lock +0 -80
- data/gemfiles/4.1.gemfile +0 -14
- data/gemfiles/4.1.gemfile.lock +0 -79
- data/gemfiles/4.2.gemfile +0 -14
- data/gemfiles/4.2.gemfile.lock +0 -79
- data/test/database.yml +0 -6
- data/test/models.rb +0 -88
- data/test/schema.rb +0 -101
- data/test/test_deep_cloneable.rb +0 -342
- data/test/test_helper.rb +0 -42
data/LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -1,24 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rubygems'
|
2
4
|
require 'bundler/setup'
|
3
5
|
require 'appraisal'
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'jeweler'
|
7
|
-
Jeweler::Tasks.new do |gem|
|
8
|
-
gem.name = "deep_cloneable"
|
9
|
-
gem.summary = %Q{This gem gives every ActiveRecord::Base object the possibility to do a deep clone.}
|
10
|
-
gem.description = %Q{Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. }
|
11
|
-
gem.email = "r.j.delange@nedforce.nl"
|
12
|
-
gem.homepage = "http://github.com/moiristo/deep_cloneable"
|
13
|
-
gem.authors = ["Reinier de Lange"]
|
14
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
-
end
|
16
|
-
Jeweler::GemcutterTasks.new
|
17
|
-
rescue LoadError
|
18
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
-
end
|
20
|
-
|
21
6
|
require 'rake/testtask'
|
7
|
+
|
22
8
|
Rake::TestTask.new(:test) do |test|
|
23
9
|
test.libs << 'lib' << 'test'
|
24
10
|
test.pattern = 'test/**/test_*.rb'
|
@@ -29,7 +15,7 @@ task :default => :test
|
|
29
15
|
|
30
16
|
require 'rdoc/task'
|
31
17
|
Rake::RDocTask.new do |rdoc|
|
32
|
-
version = File.exist?('VERSION') ? File.read('VERSION') :
|
18
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ''
|
33
19
|
|
34
20
|
rdoc.rdoc_dir = 'rdoc'
|
35
21
|
rdoc.title = "deep_cloneable #{version}"
|
data/deep_cloneable.gemspec
CHANGED
@@ -1,66 +1,21 @@
|
|
1
|
-
#
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: deep_cloneable 2.0.2 ruby lib
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
s.version = "2.0.2"
|
10
|
-
|
11
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
-
s.require_paths = ["lib"]
|
13
|
-
s.authors = ["Reinier de Lange"]
|
14
|
-
s.date = "2014-12-17"
|
15
|
-
s.description = "Extends the functionality of ActiveRecord::Base#clone to perform a deep clone that includes user specified associations. "
|
16
|
-
s.email = "r.j.delange@nedforce.nl"
|
17
|
-
s.extra_rdoc_files = [
|
18
|
-
"LICENSE",
|
19
|
-
"README.rdoc"
|
20
|
-
]
|
21
|
-
s.files = [
|
22
|
-
".document",
|
23
|
-
".travis.yml",
|
24
|
-
"Appraisals",
|
25
|
-
"Gemfile",
|
26
|
-
"Gemfile.lock",
|
27
|
-
"LICENSE",
|
28
|
-
"README.rdoc",
|
29
|
-
"Rakefile",
|
30
|
-
"VERSION",
|
31
|
-
"deep_cloneable.gemspec",
|
32
|
-
"gemfiles/3.1.gemfile",
|
33
|
-
"gemfiles/3.1.gemfile.lock",
|
34
|
-
"gemfiles/3.2.gemfile",
|
35
|
-
"gemfiles/3.2.gemfile.lock",
|
36
|
-
"gemfiles/4.0.gemfile",
|
37
|
-
"gemfiles/4.0.gemfile.lock",
|
38
|
-
"gemfiles/4.1.gemfile",
|
39
|
-
"gemfiles/4.1.gemfile.lock",
|
40
|
-
"gemfiles/4.2.gemfile",
|
41
|
-
"gemfiles/4.2.gemfile.lock",
|
42
|
-
"init.rb",
|
43
|
-
"lib/deep_cloneable.rb",
|
44
|
-
"test/database.yml",
|
45
|
-
"test/models.rb",
|
46
|
-
"test/schema.rb",
|
47
|
-
"test/test_deep_cloneable.rb",
|
48
|
-
"test/test_helper.rb"
|
49
|
-
]
|
50
|
-
s.homepage = "http://github.com/moiristo/deep_cloneable"
|
51
|
-
s.rubygems_version = "2.4.2"
|
52
|
-
s.summary = "This gem gives every ActiveRecord::Base object the possibility to do a deep clone."
|
53
|
-
|
54
|
-
if s.respond_to? :specification_version then
|
55
|
-
s.specification_version = 4
|
3
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
4
|
+
require 'deep_cloneable/version'
|
56
5
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'deep_cloneable'
|
8
|
+
s.version = DeepCloneable::VERSION
|
9
|
+
s.authors = ['Reinier de Lange']
|
10
|
+
s.description = 'Extends the functionality of ActiveRecord::Base#dup to perform a deep clone that includes user specified associations. '
|
11
|
+
s.summary = 'This gem gives every ActiveRecord::Base object the possibility to do a deep clone.'
|
12
|
+
s.email = 'rjdelange@icloud.com'
|
13
|
+
s.extra_rdoc_files = ['LICENSE']
|
14
|
+
s.files = Dir.glob('{bin/*,lib/**/*,[A-Z]*}')
|
15
|
+
s.homepage = 'https://github.com/moiristo/deep_cloneable'
|
16
|
+
s.licenses = ['MIT']
|
17
|
+
s.platform = Gem::Platform::RUBY
|
18
|
+
s.required_ruby_version = '>= 1.9.3'
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
s.add_runtime_dependency('activerecord', ['>= 3.1.0', '< 7'])
|
65
21
|
end
|
66
|
-
|
data/init.rb
CHANGED
data/lib/deep_cloneable.rb
CHANGED
@@ -1,136 +1,18 @@
|
|
1
|
-
|
2
|
-
module DeepCloneable
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_support/lazy_load_hooks'
|
5
|
+
require 'active_support/core_ext/array/wrap'
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
require 'deep_cloneable/association_not_found_exception'
|
8
|
+
require 'deep_cloneable/skip_validations'
|
9
|
+
require 'deep_cloneable/deep_clone'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
else
|
14
|
-
tableized_class = self.class.name.tableize.to_sym
|
15
|
-
dict[tableized_class] ||= {}
|
16
|
-
dict[tableized_class][self] ||= dup()
|
17
|
-
end
|
18
|
-
|
19
|
-
block.call(self, kopy) if block
|
20
|
-
|
21
|
-
deep_exceptions = {}
|
22
|
-
if options[:except]
|
23
|
-
exceptions = options[:except].nil? ? [] : [options[:except]].flatten
|
24
|
-
exceptions.each do |attribute|
|
25
|
-
kopy.send(:write_attribute, attribute, self.class.column_defaults.dup[attribute.to_s]) unless attribute.kind_of?(Hash)
|
26
|
-
end
|
27
|
-
deep_exceptions = exceptions.select{|e| e.kind_of?(Hash) }.inject({}){|m,h| m.merge(h) }
|
28
|
-
end
|
29
|
-
|
30
|
-
deep_onlinesses = {}
|
31
|
-
if options[:only]
|
32
|
-
onlinesses = options[:only].nil? ? [] : [options[:only]].flatten
|
33
|
-
object_attrs = kopy.attributes.keys.collect{ |s| s.to_sym }
|
34
|
-
exceptions = object_attrs - onlinesses
|
35
|
-
exceptions.each do |attribute|
|
36
|
-
kopy.send(:write_attribute, attribute, self.class.column_defaults.dup[attribute.to_s]) unless attribute.kind_of?(Hash)
|
37
|
-
end
|
38
|
-
deep_onlinesses = onlinesses.select{|e| e.kind_of?(Hash) }.inject({}){|m,h| m.merge(h) }
|
39
|
-
end
|
40
|
-
|
41
|
-
if options[:include]
|
42
|
-
Array(options[:include]).each do |association, deep_associations|
|
43
|
-
if (association.kind_of? Hash)
|
44
|
-
deep_associations = association[association.keys.first]
|
45
|
-
association = association.keys.first
|
46
|
-
end
|
47
|
-
|
48
|
-
dup_options = deep_associations.blank? ? {} : {:include => deep_associations}
|
49
|
-
dup_options.merge!(:except => deep_exceptions[association]) if deep_exceptions[association]
|
50
|
-
dup_options.merge!(:only => deep_onlinesses[association]) if deep_onlinesses[association]
|
51
|
-
dup_options.merge!(:dictionary => dict) if dict
|
52
|
-
|
53
|
-
association_reflection = self.class.reflect_on_association(association)
|
54
|
-
raise AssociationNotFoundException.new("#{self.class}##{association}") if association_reflection.nil?
|
55
|
-
|
56
|
-
if options[:validate] == false
|
57
|
-
kopy.instance_eval do
|
58
|
-
# Force :validate => false on all saves.
|
59
|
-
def perform_validations(options={})
|
60
|
-
options[:validate] = false
|
61
|
-
super(options)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
association_type = association_reflection.macro
|
67
|
-
association_type = "#{association_type}_through" if association_reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
68
|
-
|
69
|
-
cloned_object = send(
|
70
|
-
"dup_#{association_type}_association",
|
71
|
-
{ :reflection => association_reflection, :association => association, :copy => kopy, :dup_options => dup_options },
|
72
|
-
&block
|
73
|
-
)
|
74
|
-
|
75
|
-
kopy.send("#{association}=", cloned_object)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
return kopy
|
80
|
-
end
|
81
|
-
|
82
|
-
private
|
83
|
-
|
84
|
-
def dup_belongs_to_association options, &block
|
85
|
-
self.send(options[:association]) && self.send(options[:association]).deep_clone(options[:dup_options], &block)
|
86
|
-
end
|
87
|
-
|
88
|
-
def dup_has_one_association options, &block
|
89
|
-
dup_belongs_to_association options, &block
|
90
|
-
end
|
91
|
-
|
92
|
-
def dup_has_many_association options, &block
|
93
|
-
primary_key_name = options[:reflection].foreign_key.to_s
|
94
|
-
|
95
|
-
reverse_association_name = options[:reflection].klass.reflect_on_all_associations.detect do |reflection|
|
96
|
-
reflection.foreign_key.to_s == primary_key_name && reflection != options[:reflection]
|
97
|
-
end.try(:name)
|
98
|
-
|
99
|
-
self.send(options[:association]).collect do |obj|
|
100
|
-
tmp = obj.deep_clone(options[:dup_options], &block)
|
101
|
-
tmp.send("#{primary_key_name}=", nil)
|
102
|
-
tmp.send("#{reverse_association_name.to_s}=", options[:copy]) if reverse_association_name
|
103
|
-
tmp
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def dup_has_many_through_association options, &block
|
108
|
-
dup_join_association(
|
109
|
-
options.merge(:macro => :has_many, :primary_key_name => options[:reflection].through_reflection.foreign_key.to_s),
|
110
|
-
&block)
|
111
|
-
end
|
112
|
-
|
113
|
-
def dup_has_and_belongs_to_many_association options, &block
|
114
|
-
dup_join_association(
|
115
|
-
options.merge(:macro => :has_and_belongs_to_many, :primary_key_name => options[:reflection].foreign_key.to_s),
|
116
|
-
&block)
|
117
|
-
end
|
118
|
-
|
119
|
-
def dup_join_association options, &block
|
120
|
-
reverse_association_name = options[:reflection].klass.reflect_on_all_associations.detect do |reflection|
|
121
|
-
(reflection.macro == options[:macro]) && (reflection.association_foreign_key.to_s == options[:primary_key_name])
|
122
|
-
end.try(:name)
|
123
|
-
|
124
|
-
self.send(options[:association]).collect do |obj|
|
125
|
-
obj.send(reverse_association_name).target << options[:copy] if reverse_association_name
|
126
|
-
obj
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
class AssociationNotFoundException < StandardError; end
|
11
|
+
module DeepCloneable
|
12
|
+
end
|
131
13
|
|
132
|
-
|
133
|
-
|
14
|
+
ActiveSupport.on_load :active_record do
|
15
|
+
protected :initialize_dup if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1
|
134
16
|
|
135
|
-
include DeepCloneable
|
17
|
+
include DeepCloneable::DeepClone
|
136
18
|
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCloneable
|
4
|
+
module DeepClone
|
5
|
+
# Deep dups an ActiveRecord model. See README.rdoc
|
6
|
+
def deep_clone(*args, &block)
|
7
|
+
options = args[0] || {}
|
8
|
+
|
9
|
+
dictionary = options[:dictionary]
|
10
|
+
dictionary ||= {} if options[:use_dictionary]
|
11
|
+
|
12
|
+
kopy = if dictionary
|
13
|
+
find_in_dictionary_or_dup(dictionary)
|
14
|
+
else
|
15
|
+
dup
|
16
|
+
end
|
17
|
+
|
18
|
+
options[:preprocessor].call(self, kopy) if options.key?(:preprocessor)
|
19
|
+
|
20
|
+
deep_exceptions = {}
|
21
|
+
if options[:except]
|
22
|
+
exceptions = Array.wrap(options[:except])
|
23
|
+
exceptions.each do |attribute|
|
24
|
+
dup_default_attribute_value_to(kopy, attribute, self) unless attribute.is_a?(Hash)
|
25
|
+
end
|
26
|
+
deep_exceptions = exceptions.select { |e| e.is_a?(Hash) }.inject({}) { |m, h| m.merge(h) }
|
27
|
+
end
|
28
|
+
|
29
|
+
deep_onlinesses = {}
|
30
|
+
if options[:only]
|
31
|
+
onlinesses = Array.wrap(options[:only])
|
32
|
+
object_attrs = kopy.attributes.keys.collect(&:to_sym)
|
33
|
+
exceptions = object_attrs - onlinesses
|
34
|
+
exceptions.each do |attribute|
|
35
|
+
dup_default_attribute_value_to(kopy, attribute, self) unless attribute.is_a?(Hash)
|
36
|
+
end
|
37
|
+
deep_onlinesses = onlinesses.select { |e| e.is_a?(Hash) }.inject({}) { |m, h| m.merge(h) }
|
38
|
+
end
|
39
|
+
|
40
|
+
kopy.instance_eval { extend ::DeepCloneable::SkipValidations } if options[:validate] == false
|
41
|
+
|
42
|
+
if options[:include]
|
43
|
+
normalized_includes_list(options[:include]).each do |association, conditions_or_deep_associations|
|
44
|
+
conditions = {}
|
45
|
+
|
46
|
+
if association.is_a? Hash
|
47
|
+
conditions_or_deep_associations = association[association.keys.first]
|
48
|
+
association = association.keys.first
|
49
|
+
end
|
50
|
+
|
51
|
+
case conditions_or_deep_associations
|
52
|
+
when Hash
|
53
|
+
conditions_or_deep_associations = conditions_or_deep_associations.dup
|
54
|
+
conditions[:if] = conditions_or_deep_associations.delete(:if) if conditions_or_deep_associations[:if]
|
55
|
+
conditions[:unless] = conditions_or_deep_associations.delete(:unless) if conditions_or_deep_associations[:unless]
|
56
|
+
when Array
|
57
|
+
conditions_or_deep_associations = conditions_or_deep_associations.map { |entry| entry.is_a?(Hash) ? entry.dup : entry }
|
58
|
+
conditions_or_deep_associations.each_with_index do |entry, index|
|
59
|
+
if entry.is_a?(Hash)
|
60
|
+
conditions[:if] = entry.delete(:if) if entry[:if]
|
61
|
+
conditions[:unless] = entry.delete(:unless) if entry[:unless]
|
62
|
+
end
|
63
|
+
|
64
|
+
conditions_or_deep_associations.delete_at(index) if entry.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
dup_options = {}
|
69
|
+
dup_options[:include] = conditions_or_deep_associations if conditions_or_deep_associations.present?
|
70
|
+
dup_options[:except] = deep_exceptions[association] if deep_exceptions[association]
|
71
|
+
dup_options[:only] = deep_onlinesses[association] if deep_onlinesses[association]
|
72
|
+
dup_options[:dictionary] = dictionary if dictionary
|
73
|
+
|
74
|
+
[:skip_missing_associations, :validate, :preprocessor, :postprocessor].each do |option|
|
75
|
+
dup_options[option] = options[option] if options.key?(option)
|
76
|
+
end
|
77
|
+
|
78
|
+
if (association_reflection = self.class.reflect_on_association(association))
|
79
|
+
association_type = association_reflection.macro
|
80
|
+
association_type = "#{association_type}_through" if association_reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
81
|
+
|
82
|
+
duped_object = send(
|
83
|
+
"dup_#{association_type}_association",
|
84
|
+
{ :reflection => association_reflection, :association => association, :copy => kopy, :conditions => conditions, :dup_options => dup_options },
|
85
|
+
&block
|
86
|
+
)
|
87
|
+
|
88
|
+
kopy.send("#{association}=", duped_object)
|
89
|
+
elsif !options[:skip_missing_associations]
|
90
|
+
raise ::DeepCloneable::AssociationNotFoundException, "#{self.class}##{association}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
yield(self, kopy) if block
|
96
|
+
options[:postprocessor].call(self, kopy) if options.key?(:postprocessor)
|
97
|
+
|
98
|
+
kopy
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
def find_in_dictionary_or_dup(dictionary, dup_on_miss = true)
|
104
|
+
tableized_class = self.class.name.tableize.to_sym
|
105
|
+
dictionary[tableized_class] ||= {}
|
106
|
+
dict_val = dictionary[tableized_class][self]
|
107
|
+
dict_val.nil? && dup_on_miss ? dictionary[tableized_class][self] = dup : dict_val
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def dup_belongs_to_association(options, &block)
|
113
|
+
object = deep_cloneable_object_for(options[:association], options[:conditions])
|
114
|
+
object && object.deep_clone(options[:dup_options], &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def dup_has_one_association(options, &block)
|
118
|
+
dup_belongs_to_association options, &block
|
119
|
+
end
|
120
|
+
|
121
|
+
def dup_has_many_association(options, &block)
|
122
|
+
foreign_key = options[:reflection].foreign_key.to_s
|
123
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :belongs_to)
|
124
|
+
objects = deep_cloneable_objects_for(options[:association], options[:conditions])
|
125
|
+
|
126
|
+
objects.map do |object|
|
127
|
+
object = object.deep_clone(options[:dup_options], &block)
|
128
|
+
object.send("#{foreign_key}=", nil)
|
129
|
+
object.send("#{reverse_association.name}=", options[:copy]) if reverse_association
|
130
|
+
object
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def dup_has_one_through_association(options, &block)
|
135
|
+
foreign_key = options[:reflection].through_reflection.foreign_key.to_s
|
136
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :has_one, :association_foreign_key)
|
137
|
+
|
138
|
+
object = deep_cloneable_object_for(options[:association], options[:conditions])
|
139
|
+
object && process_joined_object_for_deep_clone(object, options.merge(:reverse_association => reverse_association), &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
def dup_has_many_through_association(options, &block)
|
143
|
+
foreign_key = options[:reflection].through_reflection.foreign_key.to_s
|
144
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :has_many, :association_foreign_key)
|
145
|
+
|
146
|
+
objects = deep_cloneable_objects_for(options[:association], options[:conditions])
|
147
|
+
objects.map { |object| process_joined_object_for_deep_clone(object, options.merge(:reverse_association => reverse_association), &block) }
|
148
|
+
end
|
149
|
+
|
150
|
+
def dup_has_and_belongs_to_many_association(options, &block)
|
151
|
+
foreign_key = options[:reflection].foreign_key.to_s
|
152
|
+
reverse_association = find_reverse_association(options[:reflection], foreign_key, :has_and_belongs_to_many, :association_foreign_key)
|
153
|
+
|
154
|
+
objects = deep_cloneable_objects_for(options[:association], options[:conditions])
|
155
|
+
objects.map { |object| process_joined_object_for_deep_clone(object, options.merge(:reverse_association => reverse_association), &block) }
|
156
|
+
end
|
157
|
+
|
158
|
+
def find_reverse_association(source_reflection, primary_key_name, macro, matcher = :foreign_key)
|
159
|
+
if source_reflection.inverse_of.present?
|
160
|
+
source_reflection.inverse_of
|
161
|
+
else
|
162
|
+
source_reflection.klass.reflect_on_all_associations.detect do |reflection|
|
163
|
+
reflection != source_reflection && (macro.nil? || reflection.macro == macro) && (reflection.send(matcher).to_s == primary_key_name)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def deep_cloneable_object_for(single_association, conditions)
|
169
|
+
object = send(single_association)
|
170
|
+
evaluate_conditions(object, conditions) ? object : nil
|
171
|
+
end
|
172
|
+
|
173
|
+
def deep_cloneable_objects_for(many_association, conditions)
|
174
|
+
send(many_association).select { |object| evaluate_conditions(object, conditions) }
|
175
|
+
end
|
176
|
+
|
177
|
+
def process_joined_object_for_deep_clone(object, options, &block)
|
178
|
+
if (dictionary = options[:dup_options][:dictionary]) && object.find_in_dictionary_or_dup(dictionary, false)
|
179
|
+
object = object.deep_clone(options[:dup_options], &block)
|
180
|
+
elsif options[:reverse_association]
|
181
|
+
object.send(options[:reverse_association].name).target << options[:copy]
|
182
|
+
end
|
183
|
+
object
|
184
|
+
end
|
185
|
+
|
186
|
+
def evaluate_conditions(object, conditions)
|
187
|
+
conditions.none? || (conditions[:if] && conditions[:if].call(object)) || (conditions[:unless] && !conditions[:unless].call(object))
|
188
|
+
end
|
189
|
+
|
190
|
+
def dup_default_attribute_value_to(kopy, attribute, origin)
|
191
|
+
kopy[attribute] = origin.class.column_defaults.dup[attribute.to_s]
|
192
|
+
end
|
193
|
+
|
194
|
+
def normalized_includes_list(includes)
|
195
|
+
list = []
|
196
|
+
Array(includes).each do |item|
|
197
|
+
if item.is_a?(Hash) && item.size > 1
|
198
|
+
item.each { |key, value| list << { key => value } }
|
199
|
+
else
|
200
|
+
list << item
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
list
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|