deep-cloning 0.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 +7 -0
- data/lib/deep_cloning.rb +117 -0
- data/test/test_deep_cloning.rb +10 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3372e16d0e185165e80d378bd4beea49c8c9fad8851efbd151262f089a07e309
|
4
|
+
data.tar.gz: 89ce525b272e1abb127365474e36a22a6ca9819ddbe47c9cb936b4e074df3bd0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '06974397d62faee5f8f8e37357b278ce0707e1bfa5f4cf0c94b81732499482f24098b31e3daa041a6f4ef5ca489bf5d8eaea8ef0182ee6d6533e88140c32e6d9'
|
7
|
+
data.tar.gz: 73460882ab61231d5979435c174ffe21f646cda6660335f5653e203a7501c94564099b8062bfb7e75049456e150a36e4a6e153ced7e5eb60017d9cc6211327b6
|
data/lib/deep_cloning.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module DeepCloning
|
4
|
+
# This is the main class responsible to evaluate the equations
|
5
|
+
class Clone
|
6
|
+
VERSION = '0.1.0'.freeze
|
7
|
+
def initialize(root, opts = { except: [], save_root: true })
|
8
|
+
@root = root
|
9
|
+
@opts = opts
|
10
|
+
end
|
11
|
+
|
12
|
+
def replicate
|
13
|
+
ActiveRecord::Base.transaction do
|
14
|
+
# the block can be used to change the fields and fix eventual validation problems
|
15
|
+
leafs_statuses = { "#{@root.class.name}_#{@root.id}" => true }
|
16
|
+
if @opts[:save_root]
|
17
|
+
clone = @root.dup
|
18
|
+
yield(@root, clone, :before_save) if block_given?
|
19
|
+
clone.save if clone.new_record? # avoid save again if saved on block
|
20
|
+
yield(@root, clone, :after_save) if block_given?
|
21
|
+
raise clone.errors.full_messages.join(', ') if clone.errors.any?
|
22
|
+
@opts[clone.class.name] = { @root.id => clone }
|
23
|
+
end
|
24
|
+
@opts[:source] = leafs(@root)
|
25
|
+
|
26
|
+
while @opts[:source].any?
|
27
|
+
@cell = @opts[:source].detect do |n|
|
28
|
+
n = yield(n, n, :prepare) if block_given?
|
29
|
+
walk?(n)
|
30
|
+
end
|
31
|
+
unless @cell
|
32
|
+
ap @opts[:source].map { |s| "#{s.id} - #{s.class.name}" }
|
33
|
+
raise 'cannot duplicate the hierarchy'
|
34
|
+
end
|
35
|
+
@opts[:source] -= [@cell]
|
36
|
+
|
37
|
+
@opts[@cell.class.name] = {} unless @opts[@cell.class.name]
|
38
|
+
next if @opts[@cell.class.name][@cell.id] # already cloned?
|
39
|
+
skip = false
|
40
|
+
skip = yield(@cell, clone, :skip?) if block_given?
|
41
|
+
|
42
|
+
if not @cell.class.name.in?(@opts[:except]) and not skip
|
43
|
+
clone = @cell.dup
|
44
|
+
parents(clone.class).each do |belongs_to|
|
45
|
+
old_id = clone.send("#{belongs_to.name}_id")
|
46
|
+
next unless old_id
|
47
|
+
|
48
|
+
if belongs_to.options[:polymorphic]
|
49
|
+
class_name = clone.send("#{belongs_to.name}").class.name
|
50
|
+
if @opts[class_name] and @opts[class_name][old_id]
|
51
|
+
clone.send("#{belongs_to.name}=", @opts[class_name][old_id])
|
52
|
+
end
|
53
|
+
else
|
54
|
+
if @opts[belongs_to.class_name] and @opts[belongs_to.class_name][old_id]
|
55
|
+
clone.send("#{belongs_to.name}=", @opts[belongs_to.class_name][old_id])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
yield(@cell, clone, :before_save) if block_given?
|
60
|
+
clone.save if clone.new_record? # avoid save again if saved on block
|
61
|
+
yield(@cell, clone, :after_save) if block_given?
|
62
|
+
raise "#{clone.class} - #{clone.errors.full_messages.join(', ')}" if clone.errors.any?
|
63
|
+
@opts[clone.class.name][@cell.id] = clone
|
64
|
+
end
|
65
|
+
@opts[:source] += leafs(@cell)
|
66
|
+
leafs_statuses["#{@cell.class.name}_#{@cell.id}"] = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
rescue => e
|
70
|
+
puts e.backtrace
|
71
|
+
puts e.message
|
72
|
+
end
|
73
|
+
|
74
|
+
# Need to check the relations instead the models only
|
75
|
+
def walk?(cell)
|
76
|
+
parents(cell.class).map do |p|
|
77
|
+
if p.options[:polymorphic]
|
78
|
+
if cell.respond_to?("#{p.name}_id".to_sym) and cell.send("#{p.name}_id")
|
79
|
+
class_name = cell.send("#{p.name}").class.name
|
80
|
+
@opts[class_name] = {} unless @opts[class_name]
|
81
|
+
@opts[class_name][cell.send("#{p.name}_id")] or class_name.in? @opts[:except] # replicated parent?
|
82
|
+
else
|
83
|
+
true
|
84
|
+
end
|
85
|
+
else
|
86
|
+
@opts[p.class_name] = {} unless @opts[p.class_name]
|
87
|
+
!cell.respond_to?("#{p.name}_id".to_sym) or cell.send("#{p.name}_id").nil? or @opts[p.class_name][cell.send("#{p.name}_id")] or p.class_name.in? @opts[:except] # replicated parent?
|
88
|
+
end
|
89
|
+
end.reduce(true) { |result, value| result and value }
|
90
|
+
end
|
91
|
+
|
92
|
+
def leafs(cell)
|
93
|
+
node = cell.class
|
94
|
+
arr = []
|
95
|
+
node.reflect_on_all_associations(:has_one).each do |c|
|
96
|
+
unless c.class_name.in? @opts[:except]
|
97
|
+
leaf = cell.send(c.name)
|
98
|
+
arr << leaf if leaf&.persisted? and (@opts[:source].nil? or (not leaf.in? @opts[:source]))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
node.reflect_on_all_associations(:has_many).each do |c|
|
102
|
+
unless c.class_name.in? @opts[:except]
|
103
|
+
cell.send(c.name).find_in_batches.each do |leafs|
|
104
|
+
leafs.each do |leaf|
|
105
|
+
arr << leaf if leaf&.persisted? and (@opts[:source].nil? or (not leaf.in? @opts[:source]))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
arr
|
111
|
+
end
|
112
|
+
|
113
|
+
def parents(node)
|
114
|
+
parents = node.reflect_on_all_associations(:belongs_to) # name and class_name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'deep_cloning'
|
3
|
+
|
4
|
+
# This class test all possible equations for this gem
|
5
|
+
class TestParsec < Minitest::Test
|
6
|
+
def test_defined
|
7
|
+
assert defined?(DeepCloning::DeepCloning)
|
8
|
+
assert defined?(DeepCloning::DeepCloning::VERSION)
|
9
|
+
end
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deep-cloning
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nilton Vasques
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activerecord
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.2'
|
55
|
+
description: DeepCloning is a gem that is able to replicate a set of records in depth
|
56
|
+
email:
|
57
|
+
- nilton.vasques@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- lib/deep_cloning.rb
|
63
|
+
- test/test_deep_cloning.rb
|
64
|
+
homepage: https://github.com/niltonvasques/deep-cloning
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 2.7.6
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: DeepCloning is a gem that is able to replicate a set of records in depth
|
88
|
+
test_files:
|
89
|
+
- test/test_deep_cloning.rb
|