micdrop 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.
@@ -0,0 +1,170 @@
1
+ module Micdrop
2
+ class StructureBuilder
3
+ def initialize(raw_value, parent = nil, exists: true, push_parent: false, parent_key: nil)
4
+ @raw_value = raw_value
5
+ @parent = parent
6
+ @exists = exists
7
+ @push_parent = push_parent
8
+ @parent_key = parent_key
9
+ @enforced_types = nil
10
+ end
11
+
12
+ def self.new_blank
13
+ StructureBuilder.new nil, nil, exists: false
14
+ end
15
+
16
+ attr_reader :parent, :exists, :raw_value
17
+
18
+ def [](*args)
19
+ if args.empty?
20
+ # x[]
21
+ enforce_array
22
+ StructureBuilder.new nil, self, exists: false, push_parent: true
23
+ elsif args.count == 1
24
+ arg = args.first
25
+ if args.first.is_a?(Integer)
26
+ # x[1]
27
+ enforce_array_or_hash
28
+ else
29
+ # x["thing"]
30
+ enforce_hash
31
+ end
32
+ if @exists && @raw_value.is_a?(Array) && arg.is_a?(Integer) && arg < @raw_value.length && arg >= -@raw_value.length
33
+ StructureBuilder.new @raw_value[arg], self, parent_key: arg
34
+ elsif @exists && @raw_value.is_a?(Hash) && @raw_value.has_key?(arg)
35
+ StructureBuilder.new @raw_value[arg], self, parent_key: arg
36
+ else
37
+ StructureBuilder.new nil, self, exists: false, parent_key: arg
38
+ end
39
+ elsif args.count == 2 && args[0].is_a?(Integer) && args[1].is_a?(Integer)
40
+ # x[3, 7] = :something
41
+ enforce_array
42
+ context[*args] = value
43
+ else
44
+ raise IndexError
45
+ end
46
+ end
47
+
48
+ def []=(*args)
49
+ value = args.pop
50
+ if args.empty?
51
+ # x[] = :something
52
+ enforce_array
53
+ realize
54
+ @raw_value.push value
55
+ elsif args.count == 1
56
+ arg = args.first
57
+ if arg.is_a?(Integer)
58
+ # x[1] = :something
59
+ enforce_array_or_hash
60
+ else
61
+ # x["thing"] = :something
62
+ enforce_hash
63
+ end
64
+ realize
65
+ @raw_value[arg] = value
66
+ elsif args.count == 2 && args[0].is_a?(Integer) && args[1].is_a?(Integer)
67
+ # x[3, 7] = :something
68
+ enforce_array
69
+ realize
70
+ @raw_value[*args] = value
71
+ else
72
+ raise IndexError
73
+ end
74
+ end
75
+
76
+ def <<(value)
77
+ # x << :something
78
+ enforce_array
79
+ realize
80
+ @raw_value.push value
81
+ end
82
+
83
+ ##
84
+ # This is intended as an inverse to :dig, automatically assembling structure as needed
85
+ def bury(value, *keys)
86
+ context = self
87
+ last_key = keys.pop
88
+ keys.each do |key|
89
+ context = if key.nil?
90
+ context[]
91
+ else
92
+ context[key]
93
+ end
94
+ end
95
+ if last_key.nil?
96
+ context[] = value
97
+ else
98
+ context[last_key] = value
99
+ end
100
+ end
101
+
102
+ def enforce_array
103
+ raise StandardError, "Value is not an array" if @exists && !@raw_value.is_a?(Array)
104
+
105
+ @enforced_types = if @enforced_types.nil?
106
+ [:array]
107
+ else
108
+ [:array].intersection @enforced_types
109
+ end
110
+ end
111
+
112
+ def enforce_hash
113
+ raise StandardError, "Value is not a" if @exists && !@raw_value.is_a?(Hash)
114
+
115
+ @enforced_types = if @enforced_types.nil?
116
+ [:hash]
117
+ else
118
+ [:hash].intersection @enforced_types
119
+ end
120
+ end
121
+
122
+ def enforce_array_or_hash
123
+ raise StandardError, "Value is not a" if @exists && !(@raw_value.is_a?(Array) || @raw_value.is_a?(Hash))
124
+
125
+ @enforced_types = if @enforced_types.nil?
126
+ %i[array hash]
127
+ else
128
+ %i[array hash].intersection @enforced_types
129
+ end
130
+ end
131
+
132
+ def realize
133
+ return if @exists
134
+
135
+ # First make sure the parent is real
136
+ @parent.realize unless @parent.nil? || @parent.exists
137
+
138
+ # Then make ourselves real
139
+ if @enforced_types.nil?
140
+ raise StandardError, "No specified type"
141
+ elsif @enforced_types.empty?
142
+ raise StandardError, "No allowed type"
143
+ else
144
+ case @enforced_types.first
145
+ when :array
146
+ @raw_value = []
147
+ when :hash
148
+ @raw_value = {}
149
+ else
150
+ raise StandardError, "Unknown type: #{@enforced_types.first}"
151
+ end
152
+ end
153
+
154
+ unless @parent.nil?
155
+ # Then add ourselves to the parent
156
+ if !@parent_key.nil?
157
+ @parent.raw_value[@parent_key] = @raw_value
158
+ elsif @push_parent
159
+ @parent_key = @parent.raw_value.length
160
+ @parent.raw_value << @raw_value
161
+ else
162
+ raise StandardError, "Use either push_parent or parent_key for non-existing values"
163
+ end
164
+ end
165
+
166
+ # And now we're done!
167
+ @exists = true
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micdrop
4
+ VERSION = "0.1.0"
5
+ end
data/lib/micdrop.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "micdrop/errors"
4
+ require_relative "micdrop/files_source"
5
+ require_relative "micdrop/item_context"
6
+ require_relative "micdrop/record_context"
7
+ require_relative "micdrop/stop_skip"
8
+ require_relative "micdrop/structure_builder"
9
+ require_relative "micdrop/version"
10
+
11
+ module Micdrop
12
+ def self.migrate(from, to, &block)
13
+ # TODO: can we reduce the duplicated code?
14
+ if from.respond_to? :each_with_index
15
+ from.each_with_index do |loop_item, loop_index|
16
+ migrate_item_helper(from, to, loop_item, loop_index, &block)
17
+ rescue Skip
18
+ next
19
+ rescue Stop
20
+ break
21
+ end
22
+ elsif from.respond_to? :each_pair
23
+ from.each_pair do |loop_index, loop_item|
24
+ migrate_item_helper(from, to, loop_item, loop_index, &block)
25
+ rescue Skip
26
+ next
27
+ rescue Stop
28
+ break
29
+ end
30
+ elsif from.respond_to? :each
31
+ from.each.with_index do |loop_item, loop_index|
32
+ migrate_item_helper(from, to, loop_item, loop_index, &block)
33
+ rescue Skip
34
+ next
35
+ rescue Stop
36
+ break
37
+ end
38
+ else
39
+ # TODO: error
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Register a lookup, allowing it to be used in subsequent migrations
45
+ def self.register_lookup(name, lookup)
46
+ ItemContext.register_lookup name, lookup
47
+ end
48
+
49
+ def self.migrate_item_helper(from, to, loop_item, loop_index, &block)
50
+ ctx = RootRecordContext.new(from, to, loop_item, loop_index)
51
+ ctx.instance_eval(&block)
52
+ ctx.flush reset: false # No need to reset; item processing is done
53
+ end
54
+ end
data/sig/micdrop.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Micdrop
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: micdrop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dominick Johnson
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Migrate data from a legacy application to a new application, write scripts
13
+ to import data from third-party applications, or convert data into the format your
14
+ application expects--all in a flexible, declaritive syntax. Works with tabular data,
15
+ such as CSV or database tables, with support for structured formats such as JSON
16
+ as well.
17
+ email:
18
+ - dominick.johnson@tylertech.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - ".devcontainer/devcontainer.json"
24
+ - ".rubocop.yml"
25
+ - ".vscode/tasks.json"
26
+ - README.md
27
+ - Rakefile
28
+ - TODO.md
29
+ - examples/csvs_to_sql.rb
30
+ - examples/data/customers-100.csv
31
+ - examples/data/json/1.json
32
+ - examples/data/json/2.json
33
+ - examples/data/json/3.json
34
+ - examples/data/json/4.json
35
+ - examples/data/json/5.json
36
+ - examples/data/json/6.json
37
+ - examples/data/json/7.json
38
+ - examples/data/json/8.json
39
+ - examples/data/json/9.json
40
+ - examples/data/json/a.json
41
+ - examples/data/organizations-100.csv
42
+ - examples/data/people-100.csv
43
+ - examples/data/readme.md
44
+ - examples/json_files_to_sql.rb
45
+ - lib/micdrop.rb
46
+ - lib/micdrop/errors.rb
47
+ - lib/micdrop/ext/sequel.rb
48
+ - lib/micdrop/files_source.rb
49
+ - lib/micdrop/item_context.rb
50
+ - lib/micdrop/record_context.rb
51
+ - lib/micdrop/stop_skip.rb
52
+ - lib/micdrop/structure_builder.rb
53
+ - lib/micdrop/version.rb
54
+ - sig/micdrop.rbs
55
+ homepage: https://github.com/dmjohnsson23/micdrop-ruby
56
+ licenses: []
57
+ metadata:
58
+ allowed_push_host: https://rubygems.org
59
+ homepage_uri: https://github.com/dmjohnsson23/micdrop-ruby
60
+ source_code_uri: https://github.com/dmjohnsson23/micdrop-ruby
61
+ changelog_uri: https://github.com/dmjohnsson23/micdrop-ruby/commits/main/
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 3.1.0
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubygems_version: 3.6.7
77
+ specification_version: 4
78
+ summary: Utility library for application data migrations, imports, and conversions
79
+ test_files: []