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.
- checksums.yaml +7 -0
- data/.devcontainer/devcontainer.json +22 -0
- data/.rubocop.yml +8 -0
- data/.vscode/tasks.json +17 -0
- data/README.md +448 -0
- data/Rakefile +12 -0
- data/TODO.md +15 -0
- data/examples/csvs_to_sql.rb +227 -0
- data/examples/data/customers-100.csv +101 -0
- data/examples/data/json/1.json +1 -0
- data/examples/data/json/2.json +1 -0
- data/examples/data/json/3.json +1 -0
- data/examples/data/json/4.json +1 -0
- data/examples/data/json/5.json +1 -0
- data/examples/data/json/6.json +1 -0
- data/examples/data/json/7.json +1 -0
- data/examples/data/json/8.json +1 -0
- data/examples/data/json/9.json +1 -0
- data/examples/data/json/a.json +1 -0
- data/examples/data/organizations-100.csv +101 -0
- data/examples/data/people-100.csv +101 -0
- data/examples/data/readme.md +5 -0
- data/examples/json_files_to_sql.rb +54 -0
- data/lib/micdrop/errors.rb +23 -0
- data/lib/micdrop/ext/sequel.rb +121 -0
- data/lib/micdrop/files_source.rb +73 -0
- data/lib/micdrop/item_context.rb +512 -0
- data/lib/micdrop/record_context.rb +195 -0
- data/lib/micdrop/stop_skip.rb +7 -0
- data/lib/micdrop/structure_builder.rb +170 -0
- data/lib/micdrop/version.rb +5 -0
- data/lib/micdrop.rb +54 -0
- data/sig/micdrop.rbs +4 -0
- metadata +79 -0
|
@@ -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
|
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
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: []
|