deep-cloning 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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