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 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
@@ -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