separatum 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b772e3f0272e01367a9e4e8ab1be143964046207677841d6b0532262d6e8a7c1
4
- data.tar.gz: 824578f149e375bbc57bb28cf6b44155610a53c7448c5e27b4e09646d625b951
3
+ metadata.gz: 8e83b3b6b180d0cd4bef68738072cfd4c3ba180e5433bee78c2399c51a2e1894
4
+ data.tar.gz: fdce513260bcd76e4fd58edbe7ea9676206fa065b10e054124b2fd6eca9763b9
5
5
  SHA512:
6
- metadata.gz: 92051fcf37e11b151718a21281a95cc23a6cda366a4891623182ba2f3e183e720ebff0758d48960169ff74c0a2e682c1381d581a49f7c3a5561deaa7a2d32325
7
- data.tar.gz: 457a164b48dcf7b0b6f30bb5d29e55e24784b7257f2d3d448f611f59a2ce16674f84481bda7d93d4e6d4baadeb3a790a7802937e880fd8af7a4bf6e6afdf1ba4
6
+ metadata.gz: 69aebdd7d04d72853c639205f0f130a5664a7f953d284917d23808a5861221c39f5d09615eefff13cc95c0ae7b71a7a8bbf74944ea75f2c6980c3f50c96d1432
7
+ data.tar.gz: 10a9dba816fbb93879e671d783b6a2d3df11e6ce2ce1536046e0960955af61bdfa2f1b330148ff84c622d215142380702ff10dc1ab84abcd4acaa46bee87cf0b
data/.gitignore CHANGED
@@ -12,4 +12,3 @@
12
12
 
13
13
  # rspec failure tracking
14
14
  .rspec_status
15
-
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- separatum (0.1.0)
4
+ separatum (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -4,7 +4,7 @@ Extract and transfer linked objects from one database into another.
4
4
 
5
5
  ## How you can use it
6
6
 
7
- - Making seeds.rb as copy of production data for testing purposes.
7
+ - Making seeds.rb as copy of production data for testing purposes
8
8
  - Making separate database for AB-testing (performance or marketing purposes)
9
9
  - Checking your data logical structure (it will raise on broken or unexisting links)
10
10
 
@@ -30,8 +30,7 @@ Build new exporter
30
30
 
31
31
  ```ruby
32
32
  exporter = Separatum.build do
33
- use Separatum::Importers::ActiveRecord # We are going to crawl ActiveRecord objects
34
- use Separatum::Converters::Object2Hash # Most of the modules in Separatum is working with Hash-form of objects
33
+ use Separatum::Importers::ActiveRecord # We are going to crawl ActiveRecord objects
35
34
  use Separatum::Processors::UuidChanger # Hide production's UUIDs with no broken links
36
35
  use Separatum::Exporters::JsonFile, file_name: 'separate.json' # Export object to json file
37
36
  end
@@ -50,8 +49,7 @@ Build new importer
50
49
 
51
50
  ```ruby
52
51
  importer = Separatum.build do
53
- use Separatum::Importers::JsonFile, file_name: 'separate.json' # We are going to import hashed objects from separate.json
54
- use Separatum::Converters::Hash2Object # Convert back to real objects (not persisted)
52
+ use Separatum::Importers::JsonFile, file_name: 'separate.json' # We are going to import hashed objects from separate.json
55
53
  use Separatum::Exporters::ActiveRecord # Save them (in one transaction for all objects in set)
56
54
  end
57
55
  ```
@@ -67,9 +65,7 @@ importer.() # It returns set of persisted objects
67
65
  ```ruby
68
66
  seeds_generator = Separatum.build do
69
67
  use Separatum::Importers::ActiveRecord
70
- use Separatum::Converters::Object2Hash
71
- use Separatum::Processors::UuidChanger
72
- use Separatum::Converters::Hash2Object
68
+ use Separatum::Processors::UuidChanger
73
69
  use Separatum::Exporters::ActiveRecordCode
74
70
  end
75
71
 
@@ -82,9 +78,53 @@ start_object = User.find('any_uuid_you_want_to_start_from')
82
78
  puts seeds_generator.(start_object)
83
79
  ```
84
80
 
81
+ ## Building parts
82
+
83
+ ### Separatum::Importers::ActiveRecord
84
+
85
+ Parameters:
86
+
87
+ - max_depth (default: 3)
88
+ - edge_classes
89
+ - denied_classes
90
+ - denied_class_transitions
91
+ - svg_file_name
92
+ - dot_file_name
93
+
94
+ ### Separatum::Importers::JsonFile
95
+
96
+ Parameters:
97
+
98
+ - file_name
99
+
100
+ ### Separatum::Processors::FieldChanger
101
+
102
+ Parameters:
103
+
104
+ - 1st/2nd - class and field to change
105
+ - 3rd/4th - class and method that will make transformation
106
+ - 3rd - Proc or Block
107
+
108
+ ### Separatum::Exporters::ActiveRecord
109
+
110
+ ### Separatum::Exporters::ActiveRecordCode
111
+
112
+ Parameters:
113
+
114
+ - file_name
115
+ - ignore_not_unique_classes
116
+
117
+
118
+ ### Separatum::Exporters::JsonFile
119
+
120
+ Parameters:
121
+
122
+ - file_name
123
+ - pretty_print
124
+
85
125
  ## TODO
86
126
 
87
- - Data obfuscation (respecting to private data)
127
+ - Timemachine for DateTime fields
88
128
 
89
129
  ## Development
90
130
 
@@ -2,14 +2,15 @@ module Separatum
2
2
  module Converters
3
3
  class Hash2Object
4
4
  def call(*hashes)
5
- hashes.map do |hash|
5
+ hashes.flatten.map do |hash|
6
6
  hash_copy = hash.symbolize_keys
7
- _klass = hash_copy.delete(:_klass).constantize
8
- instance = _klass.new
9
- hash_copy.symbolize_keys.each do |k, v|
10
- instance.send("#{k}=", v)
7
+ klass = Object.const_get(hash_copy.delete(:_klass))
8
+ hash_copy.keys.map(&:to_s).select { |k| '_' == k[0] }.each { |k| hash_copy.delete(k.to_sym) }
9
+ object = klass.new
10
+ hash_copy.each do |k, v|
11
+ object.send("#{k}=", v)
11
12
  end
12
- instance
13
+ object
13
14
  end
14
15
  end
15
16
  end
@@ -1,18 +1,24 @@
1
1
  module Separatum
2
2
  module Converters
3
3
  class Object2Hash
4
+ def initialize(**params)
5
+ @common_fields = params[:common_fields] || {}
6
+ end
7
+
4
8
  def call(*objects)
5
- objects.map do |object|
9
+ objects.flatten.map do |object|
10
+ hash =
11
+ if object.respond_to?(:as_json)
12
+ object.as_json
13
+ elsif object.respond_to?(:to_hash)
14
+ object.to_hash
15
+ elsif object.respond_to?(:to_h)
16
+ object.to_h
17
+ else
18
+ fail
19
+ end
6
20
  klass = object.class.respond_to?(:name) ? object.class.name : object.class.to_s
7
- if object.respond_to?(:as_json)
8
- { _klass: klass }.merge(object.as_json)
9
- elsif object.respond_to?(:to_hash)
10
- { _klass: klass }.merge(object.to_hash)
11
- elsif object.respond_to?(:to_h)
12
- { _klass: klass }.merge(object.to_h)
13
- else
14
- fail(object.inspect)
15
- end
21
+ hash.symbolize_keys.merge(_klass: klass).merge(@common_fields)
16
22
  end
17
23
  end
18
24
  end
@@ -0,0 +1,15 @@
1
+ unless Hash.new.respond_to?(:symbolize_keys)
2
+ class Hash
3
+ def symbolize_keys
4
+ Hash[self.map { |(k, v)| [k.to_sym, v] }]
5
+ end
6
+ end
7
+ end
8
+
9
+ unless String.new.respond_to?(:is_uuid?)
10
+ class String
11
+ def is_uuid?
12
+ !!self.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,8 @@
1
1
  module Separatum
2
2
  module Exporters
3
3
  class ActiveRecord
4
- def call(*objects)
4
+ def call(*hashes)
5
+ objects = ::Separatum::Converters::Hash2Object.new.(hashes.flatten)
5
6
  ::ActiveRecord::Base.transaction do
6
7
  objects.each do |o|
7
8
  o.class.connection.execute("ALTER TABLE %s DISABLE TRIGGER ALL;" % [o.class.table_name])
@@ -1,3 +1,5 @@
1
- <%= object.class %>.connection.execute("ALTER TABLE <%= object.class.table_name %> DISABLE TRIGGER ALL;")
2
- <%= object.class %>.new(<%= attributes_str.join(', ') %>).save!(validate: false)
3
- <%= object.class %>.connection.execute("ALTER TABLE <%= object.class.table_name %> ENABLE TRIGGER ALL;")
1
+ <% if ignore_not_unique_classes.include?(object.class) %>
2
+ without_triggers(:<%= object.class.table_name %>) { ignore_record_not_unique { <%= object.class %>.new(<%= attributes_str.join(', ') %>).save!(validate: false) } }
3
+ <% else %>
4
+ without_triggers(:<%= object.class.table_name %>) { <%= object.class %>.new(<%= attributes_str.join(', ') %>).save!(validate: false) }
5
+ <% end %>
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path('../config/environment', __FILE__)
3
+
4
+ def without_triggers(table_name, &block)
5
+ Transactions::CardPayTransaction.connection.execute("ALTER TABLE #{table_name} DISABLE TRIGGER ALL;")
6
+ yield
7
+ Transactions::CardPayTransaction.connection.execute("ALTER TABLE #{table_name} ENABLE TRIGGER ALL;")
8
+ end
9
+
10
+ def ignore_record_not_unique(&block)
11
+ begin
12
+ yield
13
+ rescue ActiveRecord::RecordNotUnique
14
+ nil
15
+ end
16
+ end
17
+
18
+ <%= transactions_str.join("\n") %>
@@ -1,3 +1,3 @@
1
- ::ActiveRecord::Base.transaction do
1
+ ::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
2
2
  <%= objects_str.join("\n") %>
3
3
  end
@@ -1,26 +1,48 @@
1
1
  module Separatum
2
2
  module Exporters
3
3
  class ActiveRecordCode
4
+ attr_reader :transactions_str, :objects_str, :attributes_str, :key_str, :value_str, :ignore_not_unique_classes
5
+
4
6
  T_TRANSACTION = File.expand_path(File.join(__FILE__, %w(.. active_record_code transaction.rb.erb)))
5
7
  T_OBJECT = File.expand_path(File.join(__FILE__, %w(.. active_record_code object.rb.erb)))
6
8
  T_ATTRIBUTE = File.expand_path(File.join(__FILE__, %w(.. active_record_code attribute.rb.erb)))
9
+ T_PROGRAM = File.expand_path(File.join(__FILE__, %w(.. active_record_code program.rb.erb)))
7
10
 
8
- attr_reader :objects_str, :attributes_str, :key_str, :value_str
11
+ def initialize(**params)
12
+ @file_name = params[:file_name]
13
+ @ignore_not_unique_classes = params[:ignore_not_unique_classes] || []
14
+ end
9
15
 
10
- def call(*objects)
16
+ def call(*hashes)
17
+ objects = ::Separatum::Converters::Hash2Object.new.(hashes.flatten)
11
18
  @objects_str = objects.map do |object|
12
19
  @attributes_str = object.attributes.map do |key, value|
13
20
  @key_str = key
14
- if value.is_a?(ActiveSupport::TimeWithZone)
15
- @value_str = "\"#{value}\""
16
- else
17
- @value_str = value.inspect
18
- end
21
+ @value_str = value_to_code(value)
19
22
  ERB.new(File.read(T_ATTRIBUTE)).result(binding).strip
20
23
  end
21
24
  ERB.new(File.read(T_OBJECT)).result(binding).strip
22
25
  end
23
- ERB.new(File.read(T_TRANSACTION)).result(binding).strip
26
+ @transactions_str = [ERB.new(File.read(T_TRANSACTION)).result(binding).strip]
27
+ script = ERB.new(File.read(T_PROGRAM)).result(binding).strip
28
+
29
+ if @file_name
30
+ File.write(@file_name, script)
31
+ end
32
+
33
+ script
34
+ end
35
+
36
+ def value_to_code(value)
37
+ if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
38
+ value.to_s.dump
39
+ elsif defined?(Date) && value.is_a?(Date)
40
+ value.to_s.dump
41
+ elsif value.is_a?(String)
42
+ value.dump
43
+ else
44
+ value.inspect
45
+ end
24
46
  end
25
47
  end
26
48
  end
@@ -6,10 +6,11 @@ module Separatum
6
6
  @pretty_print = pretty_print
7
7
  end
8
8
 
9
- def call(*array)
10
- str = @pretty_print ? JSON.pretty_generate(array) : JSON.dump(array)
9
+ def call(*hashes)
10
+ hashes.flatten!
11
+ str = @pretty_print ? JSON.pretty_generate(hashes) : JSON.dump(hashes)
11
12
  File.write(@file_name, str)
12
- array
13
+ hashes
13
14
  end
14
15
  end
15
16
  end
@@ -0,0 +1,37 @@
1
+ module Separatum
2
+ module GraphViz
3
+ class Drawer
4
+ def initialize(svg_file_name: nil, dot_file_name: nil)
5
+ @svg_file_name = svg_file_name
6
+ @dot_file_name = dot_file_name
7
+ @gvp = ::Separatum::GraphViz::Proxy.new(::GraphViz.new(:G, type: :digraph))
8
+ end
9
+
10
+ def draw(object_transitions)
11
+ object_transitions.each do |prev_object, next_object|
12
+ prev_node = @gvp.add_node(object_title(prev_object))
13
+ next_node = @gvp.add_node(object_title(next_object))
14
+ @gvp.add_edge(prev_node, next_node)
15
+ end
16
+
17
+ if @svg_file_name
18
+ @gvp.output(svg: @svg_file_name)
19
+ end
20
+
21
+ if @dot_file_name
22
+ @gvp.output(dot: @dot_file_name)
23
+ end
24
+
25
+ self
26
+ end
27
+
28
+ def object_title(object)
29
+ if object.id.is_a?(String) && object.id.is_uuid?
30
+ "#{object.class}[#{object.id.slice(0, 6)}]"
31
+ else
32
+ "#{object.class}[#{object.id}]"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ module Separatum
2
+ module GraphViz
3
+ class Proxy
4
+ attr_reader :gv
5
+
6
+ def initialize(gv)
7
+ @gv = gv
8
+ @gv[:overlap] = :false
9
+ @nodes = {}
10
+ end
11
+
12
+ def add_node(name, *options)
13
+ if @nodes.key?(name)
14
+ @nodes[name]
15
+ else
16
+ @nodes[name] = @gv.add_node(name, *options)
17
+ end
18
+ end
19
+
20
+ def add_edge(node1, node2)
21
+ @gv.add_edges(node1, node2)
22
+ end
23
+
24
+ # FIXME: change to delegate/forwardable
25
+ def output(*params)
26
+ @gv.output(*params)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -6,81 +6,92 @@ module Separatum
6
6
  attr_reader :klass_transitions, :object_transitions
7
7
 
8
8
  def initialize(**params)
9
- @max_depth = params[:max_depth] || 2
10
- @skip_through = params[:skip_through] || true
11
- @skip_nil = params[:skip_nil] || true
9
+ @max_depth = params[:max_depth] || 3
10
+ @edge_classes = params[:edge_classes] || []
11
+ @denied_classes = params[:denied_classes] || []
12
+ @denied_class_transitions = params[:denied_class_transitions] || []
12
13
 
13
- @skip_klasses = params[:skip_klasses] || []
14
- @skip_objects = params[:skip_objects] || []
15
- @skip_klass_transitions = params[:skip_klass_transitions] || []
16
- @skip_object_transitions = params[:skip_object_transitions] || []
14
+ @svg_file_name = params[:svg_file_name]
15
+ @dot_file_name = params[:dot_file_name]
17
16
 
18
17
  @klass_transitions = Set[]
19
18
  @object_transitions = Set[]
20
19
  @objects = Set[]
21
20
  end
22
21
 
23
- def call(object)
24
- @objects.add(object)
25
- act(object)
26
- @objects.to_a
22
+ def call(*objects)
23
+ result = []
24
+ objects.flatten.each do |object|
25
+ @objects = Set[object]
26
+ act(object)
27
+ result += @objects.to_a
28
+ end
29
+
30
+ if @svg_file_name || @dot_file_name
31
+ if defined? ::GraphViz
32
+ ::Separatum::GraphViz::Drawer.new(svg_file_name: @svg_file_name, dot_file_name: @dot_file_name).draw(@object_transitions)
33
+ else
34
+ fail("GraphViz const is undefined. Use ruby-graphviz gem")
35
+ end
36
+ end
37
+
38
+ ::Separatum::Converters::Object2Hash.new.(result.flatten.uniq)
27
39
  end
28
40
 
29
41
  def act(object, depth = 1)
30
42
  object.class.reflections.each do |association_name, reflection|
31
- if @skip_through && reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
32
- next
33
- end
43
+ # Skip Through associations
44
+ next if reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)
34
45
 
35
- if @skip_nil && object.send(association_name).nil?
36
- next
37
- end
46
+ # Skip empty associations
47
+ next if object.send(association_name).nil?
38
48
 
39
- if depth >= @max_depth
40
- next
41
- end
49
+ # Limit depth
50
+ next if depth >= @max_depth
42
51
 
43
52
  case reflection.macro
44
53
  when :belongs_to, :has_one
45
- new_object = object.send(association_name)
46
- next if @objects.include?(new_object)
47
- next if new_object.is_one_of_a?(@skip_klasses)
48
-
49
- next if @object_transitions.include?([new_object, object])
50
- next if @skip_objects.include?(new_object)
51
- next if @skip_object_transitions.include?([object, new_object])
52
- next if @skip_klasses.include?(new_object.class)
53
- next if @skip_klass_transitions.include?([object.class, new_object.class])
54
+ next_object = object.send(association_name)
55
+ next unless process_link(object, next_object)
56
+ puts "#{depth}#{' ' * depth}[#{reflection.macro}] #{object.class} -> #{next_object.class}"
57
+ act(next_object, depth + 1)
54
58
 
55
- @objects.add(new_object)
56
- @klass_transitions.add([object.class, new_object.class])
57
- @object_transitions.add([object, new_object])
58
- act(new_object, depth + 1)
59
-
60
-
61
- when :has_many
59
+ when :has_many, :has_and_belongs_to_many
62
60
  new_objects = object.send(association_name)
63
- new_objects.each do |new_object|
64
- next if @objects.include?(new_object)
65
- next if new_object.is_one_of_a?(@skip_klasses)
66
-
67
- next if @object_transitions.include?([new_object, object])
68
- next if @skip_objects.include?(new_object)
69
- next if @skip_object_transitions.include?([object, new_object])
70
- next if @skip_klasses.include?(new_object.class)
71
- next if @skip_klass_transitions.include?([object.class, new_object.class])
72
-
73
- @objects.add(new_object)
74
- @klass_transitions.add([object.class, new_object.class])
75
- @object_transitions.add([object, new_object])
76
- act(new_object, depth + 1)
61
+ new_objects.each do |next_object|
62
+ next unless process_link(object, next_object)
63
+ puts "#{depth}#{' ' * depth}[#{reflection.macro}] #{object.class} -> #{next_object.class}"
64
+ act(next_object, depth + 1)
77
65
  end
78
66
 
79
67
  else
80
- fail
68
+ fail("Unknown association `#{reflection.macro.inspect} :#{association_name}' in #{object.class}")
81
69
  end
82
70
  end
83
71
  end
72
+
73
+ def process_link(prev_object, next_object)
74
+ # Skip already added object
75
+ return if @objects.include?(next_object)
76
+
77
+ # Skip already processed link
78
+ return if @object_transitions.include?([prev_object, next_object])
79
+
80
+ # Stuck on edge classes
81
+ return if @edge_classes.include?(prev_object.class)
82
+
83
+ # Will not go to denied classes
84
+ return if @denied_classes.include?(next_object.class)
85
+
86
+ # Will not go through denied transitions
87
+ return if @denied_class_transitions.include?([prev_object.class, next_object.class])
88
+
89
+ @objects.add(next_object)
90
+ @klass_transitions.add([prev_object.class, next_object.class])
91
+ @object_transitions.add([prev_object, next_object])
92
+
93
+ true
94
+ end
84
95
  end
85
96
  end
86
97
  end
@@ -5,9 +5,10 @@ module Separatum
5
5
  @file_name = file_name
6
6
  end
7
7
 
8
- def call(*_)
8
+ def call(*)
9
9
  str = File.read(@file_name)
10
- JSON.parse(str)
10
+ hash = JSON.parse(str)
11
+ hash.symbolize_keys
11
12
  end
12
13
  end
13
14
  end
@@ -0,0 +1,64 @@
1
+ module Separatum
2
+ module Processors
3
+ class FieldChanger
4
+ # Allows:
5
+ # SomeClass#method
6
+ # SomeNamespace::SomeClass#method
7
+ # SuperNamespace::SomeNamespace::SomeClass#method
8
+ CLASS_METHOD = /^([A-Za-z_][0-9A-Za-z_]*(?:::[A-Za-z_][0-9A-Za-z_]*)*)#([A-Za-z_][0-9A-Za-z_]*)$/
9
+
10
+ def initialize(*options, &block)
11
+ parse_options(*options, &block)
12
+ end
13
+
14
+ def call(*hashes)
15
+ hashes.flatten.map do |hash|
16
+ new_hash = hash.symbolize_keys
17
+ if hash[:_klass] == @klass && hash.key?(@method)
18
+ new_hash[@method] = @transformer.call(hash[@method])
19
+ end
20
+ new_hash
21
+ end
22
+ end
23
+
24
+ def parse_options(*options)
25
+ p1 = options.shift
26
+ if p1.is_a?(String) && (res = p1.match(CLASS_METHOD))
27
+ @klass = res[1]
28
+ @method = res[2].to_sym
29
+ elsif p1.is_a?(Class)
30
+ @klass = p1.to_s
31
+ p2 = options.shift
32
+ if p2.is_a?(Symbol)
33
+ @method = p2
34
+ else
35
+ fail
36
+ end
37
+ else
38
+ fail
39
+ end
40
+
41
+ if block_given?
42
+ @transformer = Proc.new
43
+ pp @transformer
44
+ else
45
+ p3 = options.shift
46
+ if p3.is_a?(Proc)
47
+ @transformer = p3
48
+ elsif p3.is_a?(String) && (res = p3.match(CLASS_METHOD))
49
+ @transformer = Proc.new { |value| Object.const_get(res[1]).new.send(res[2].to_sym, value) }
50
+ elsif p3.is_a?(Class)
51
+ p4 = options.shift
52
+ if p4.is_a?(Symbol)
53
+ @transformer = Proc.new { |value| p3.new.send(p4, value) }
54
+ else
55
+ fail
56
+ end
57
+ else
58
+ fail
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -2,7 +2,7 @@ module Separatum
2
2
  module Processors
3
3
  class Inspect
4
4
  def call(*array)
5
- array.each { |o| puts "#{o.inspect}" }
5
+ array.flatten.each { |o| puts "#{o.inspect}" }
6
6
  array
7
7
  end
8
8
  end
@@ -3,18 +3,30 @@ require 'securerandom'
3
3
  module Separatum
4
4
  module Processors
5
5
  class UuidChanger
6
- def initialize
6
+ def initialize(**params)
7
+ @skip_classes = (params[:skip_classes] || []).map(&:to_s)
8
+ @skip_classes_uuid = Set[]
7
9
  @uuid_map = {}
8
10
  end
9
11
 
10
12
  def call(*hashes)
11
- hashes.map(&method(:transform_hash))
13
+ hashes.flatten!
14
+ hashes.each(&method(:collect_skip_uuid))
15
+ hashes.map(&method(:transform))
12
16
  end
13
17
 
14
- def transform_hash(h)
18
+ def collect_skip_uuid(hash)
19
+ return unless hash.key?(:id)
20
+ return unless hash[:id].to_s.is_uuid?
21
+ return unless hash.key?(:_klass)
22
+ return unless @skip_classes.include?(hash[:_klass])
23
+ @skip_classes_uuid.add(hash[:id])
24
+ end
25
+
26
+ def transform(hash)
15
27
  new_h = {}
16
- h.each do |k, v|
17
- if v.is_a?(String) && v.is_uuid?
28
+ hash.each do |k, v|
29
+ if v.is_a?(String) && v.is_uuid? && !@skip_classes_uuid.include?(v)
18
30
  unless @uuid_map[v]
19
31
  @uuid_map[v] = SecureRandom.uuid
20
32
  end
@@ -1,3 +1,3 @@
1
1
  module Separatum
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/separatum.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "separatum/version"
2
-
3
1
  Dir[File.expand_path(File.join(__FILE__, %w(.. ** *.rb)))].each(&method(:require))
4
2
 
5
3
  module Separatum
@@ -22,12 +20,11 @@ module Separatum
22
20
  end
23
21
 
24
22
  def call(*options)
25
- current_options = options
26
23
  current_result = nil
27
24
  @stack.each do |el|
28
25
  klass, *params = el
29
26
  instance = klass.new(*params)
30
- current_result = instance.(*(current_result || current_options))
27
+ current_result = instance.(current_result || options)
31
28
  end
32
29
  current_result
33
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: separatum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Osenenko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-16 00:00:00.000000000 Z
11
+ date: 2019-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,18 +71,21 @@ files:
71
71
  - lib/separatum.rb
72
72
  - lib/separatum/converters/hash2_object.rb
73
73
  - lib/separatum/converters/object2_hash.rb
74
+ - lib/separatum/core_ext.rb
74
75
  - lib/separatum/exporters/active_record.rb
75
76
  - lib/separatum/exporters/active_record_code.rb
76
77
  - lib/separatum/exporters/active_record_code/attribute.rb.erb
77
78
  - lib/separatum/exporters/active_record_code/object.rb.erb
79
+ - lib/separatum/exporters/active_record_code/program.rb.erb
78
80
  - lib/separatum/exporters/active_record_code/transaction.rb.erb
79
81
  - lib/separatum/exporters/json_file.rb
82
+ - lib/separatum/graph_viz/drawer.rb
83
+ - lib/separatum/graph_viz/proxy.rb
80
84
  - lib/separatum/importers/active_record.rb
81
85
  - lib/separatum/importers/json_file.rb
82
- - lib/separatum/object.rb
86
+ - lib/separatum/processors/field_changer.rb
83
87
  - lib/separatum/processors/inspect.rb
84
88
  - lib/separatum/processors/uuid_changer.rb
85
- - lib/separatum/string.rb
86
89
  - lib/separatum/version.rb
87
90
  - separatum.gemspec
88
91
  homepage: https://github.com/a0s/separatum
@@ -1,5 +0,0 @@
1
- class Object
2
- def is_one_of_a?(*types)
3
- types.flatten.any? { |t| self.class.to_s == t.to_s }
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- class String
2
- def is_uuid?
3
- !!self.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/)
4
- end
5
- end