data_porter 0.1.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +62 -1
- data/README.md +63 -386
- data/ROADMAP.md +89 -0
- data/app/assets/javascripts/data_porter/stimulus.min.js +2 -0
- data/app/assets/javascripts/data_porter/turbo.min.js +29 -0
- data/app/assets/stylesheets/data_porter/alerts.css +25 -0
- data/app/assets/stylesheets/data_porter/application.css +12 -646
- data/app/assets/stylesheets/data_porter/badges.css +73 -0
- data/app/assets/stylesheets/data_porter/base.css +56 -0
- data/app/assets/stylesheets/data_porter/cards.css +60 -0
- data/app/assets/stylesheets/data_porter/layout.css +128 -0
- data/app/assets/stylesheets/data_porter/mapping.css +79 -0
- data/app/assets/stylesheets/data_porter/modal.css +49 -0
- data/app/assets/stylesheets/data_porter/preview.css +24 -0
- data/app/assets/stylesheets/data_porter/progress.css +37 -0
- data/app/assets/stylesheets/data_porter/table.css +45 -0
- data/app/controllers/data_porter/imports_controller.rb +74 -10
- data/app/controllers/data_porter/mapping_templates_controller.rb +85 -0
- data/app/javascript/data_porter/mapping_controller.js +86 -0
- data/app/javascript/data_porter/progress_controller.js +1 -1
- data/app/javascript/data_porter/template_form_controller.js +46 -0
- data/app/jobs/data_porter/extract_headers_job.rb +12 -0
- data/app/models/data_porter/data_import.rb +8 -2
- data/app/models/data_porter/mapping_template.rb +15 -0
- data/app/views/data_porter/imports/index.html.erb +9 -8
- data/app/views/data_porter/imports/new.html.erb +10 -4
- data/app/views/data_porter/imports/show.html.erb +41 -13
- data/app/views/data_porter/mapping_templates/_form.html.erb +40 -0
- data/app/views/data_porter/mapping_templates/edit.html.erb +11 -0
- data/app/views/data_porter/mapping_templates/index.html.erb +42 -0
- data/app/views/data_porter/mapping_templates/new.html.erb +11 -0
- data/app/views/layouts/data_porter/application.html.erb +162 -0
- data/config/routes.rb +3 -0
- data/docs/CONFIGURATION.md +81 -0
- data/docs/MAPPING.md +44 -0
- data/docs/SOURCES.md +94 -0
- data/docs/TARGETS.md +176 -0
- data/docs/screenshots/mapping.jpg +0 -0
- data/lib/data_porter/components/mapping/column_row.rb +52 -0
- data/lib/data_porter/components/mapping/form.rb +127 -0
- data/lib/data_porter/components/mapping/template_select.rb +35 -0
- data/lib/data_porter/components/preview/results_summary.rb +21 -0
- data/lib/data_porter/components/preview/summary_cards.rb +32 -0
- data/lib/data_porter/components/preview/table.rb +56 -0
- data/lib/data_porter/components/progress/bar.rb +35 -0
- data/lib/data_porter/components/shared/failure_alert.rb +22 -0
- data/lib/data_porter/components/shared/status_badge.rb +18 -0
- data/lib/data_porter/components.rb +9 -6
- data/lib/data_porter/configuration.rb +1 -1
- data/lib/data_porter/engine.rb +7 -1
- data/lib/data_porter/orchestrator.rb +21 -1
- data/lib/data_porter/sources/base.rb +18 -3
- data/lib/data_porter/sources/csv.rb +5 -0
- data/lib/data_porter/sources/xlsx.rb +76 -0
- data/lib/data_porter/sources.rb +3 -1
- data/lib/data_porter/version.rb +1 -1
- data/lib/generators/data_porter/install/install_generator.rb +4 -0
- data/lib/generators/data_porter/install/templates/create_data_porter_mapping_templates.rb.erb +16 -0
- data/lib/generators/data_porter/install/templates/initializer.rb +1 -1
- metadata +72 -135
- data/.claude/commands/blog-status.md +0 -10
- data/.claude/commands/blog.md +0 -109
- data/.claude/commands/task-done.md +0 -27
- data/.claude/commands/tm/add-dependency.md +0 -58
- data/.claude/commands/tm/add-subtask.md +0 -79
- data/.claude/commands/tm/add-task.md +0 -81
- data/.claude/commands/tm/analyze-complexity.md +0 -124
- data/.claude/commands/tm/analyze-project.md +0 -100
- data/.claude/commands/tm/auto-implement-tasks.md +0 -100
- data/.claude/commands/tm/command-pipeline.md +0 -80
- data/.claude/commands/tm/complexity-report.md +0 -120
- data/.claude/commands/tm/convert-task-to-subtask.md +0 -74
- data/.claude/commands/tm/expand-all-tasks.md +0 -52
- data/.claude/commands/tm/expand-task.md +0 -52
- data/.claude/commands/tm/fix-dependencies.md +0 -82
- data/.claude/commands/tm/help.md +0 -101
- data/.claude/commands/tm/init-project-quick.md +0 -49
- data/.claude/commands/tm/init-project.md +0 -53
- data/.claude/commands/tm/install-taskmaster.md +0 -118
- data/.claude/commands/tm/learn.md +0 -106
- data/.claude/commands/tm/list-tasks-by-status.md +0 -42
- data/.claude/commands/tm/list-tasks-with-subtasks.md +0 -30
- data/.claude/commands/tm/list-tasks.md +0 -46
- data/.claude/commands/tm/next-task.md +0 -69
- data/.claude/commands/tm/parse-prd-with-research.md +0 -51
- data/.claude/commands/tm/parse-prd.md +0 -52
- data/.claude/commands/tm/project-status.md +0 -67
- data/.claude/commands/tm/quick-install-taskmaster.md +0 -23
- data/.claude/commands/tm/remove-all-subtasks.md +0 -94
- data/.claude/commands/tm/remove-dependency.md +0 -65
- data/.claude/commands/tm/remove-subtask.md +0 -87
- data/.claude/commands/tm/remove-subtasks.md +0 -89
- data/.claude/commands/tm/remove-task.md +0 -110
- data/.claude/commands/tm/setup-models.md +0 -52
- data/.claude/commands/tm/show-task.md +0 -85
- data/.claude/commands/tm/smart-workflow.md +0 -58
- data/.claude/commands/tm/sync-readme.md +0 -120
- data/.claude/commands/tm/tm-main.md +0 -147
- data/.claude/commands/tm/to-cancelled.md +0 -58
- data/.claude/commands/tm/to-deferred.md +0 -50
- data/.claude/commands/tm/to-done.md +0 -47
- data/.claude/commands/tm/to-in-progress.md +0 -39
- data/.claude/commands/tm/to-pending.md +0 -35
- data/.claude/commands/tm/to-review.md +0 -43
- data/.claude/commands/tm/update-single-task.md +0 -122
- data/.claude/commands/tm/update-task.md +0 -75
- data/.claude/commands/tm/update-tasks-from-id.md +0 -111
- data/.claude/commands/tm/validate-dependencies.md +0 -72
- data/.claude/commands/tm/view-models.md +0 -52
- data/.env.example +0 -12
- data/.mcp.json +0 -24
- data/.taskmaster/CLAUDE.md +0 -435
- data/.taskmaster/config.json +0 -44
- data/.taskmaster/docs/prd.txt +0 -2044
- data/.taskmaster/state.json +0 -6
- data/.taskmaster/tasks/task_001.md +0 -19
- data/.taskmaster/tasks/task_002.md +0 -19
- data/.taskmaster/tasks/task_003.md +0 -19
- data/.taskmaster/tasks/task_004.md +0 -19
- data/.taskmaster/tasks/task_005.md +0 -19
- data/.taskmaster/tasks/task_006.md +0 -19
- data/.taskmaster/tasks/task_007.md +0 -19
- data/.taskmaster/tasks/task_008.md +0 -19
- data/.taskmaster/tasks/task_009.md +0 -19
- data/.taskmaster/tasks/task_010.md +0 -19
- data/.taskmaster/tasks/task_011.md +0 -19
- data/.taskmaster/tasks/task_012.md +0 -19
- data/.taskmaster/tasks/task_013.md +0 -19
- data/.taskmaster/tasks/task_014.md +0 -19
- data/.taskmaster/tasks/task_015.md +0 -19
- data/.taskmaster/tasks/task_016.md +0 -19
- data/.taskmaster/tasks/task_017.md +0 -19
- data/.taskmaster/tasks/task_018.md +0 -19
- data/.taskmaster/tasks/task_019.md +0 -19
- data/.taskmaster/tasks/task_020.md +0 -19
- data/.taskmaster/tasks/tasks.json +0 -299
- data/.taskmaster/templates/example_prd.txt +0 -47
- data/.taskmaster/templates/example_prd_rpg.txt +0 -511
- data/CLAUDE.md +0 -65
- data/config/database.yml +0 -3
- data/docs/SPEC.md +0 -2012
- data/docs/UI.md +0 -32
- data/docs/blog/001-why-build-a-data-import-engine.md +0 -166
- data/docs/blog/002-scaffolding-a-rails-engine.md +0 -188
- data/docs/blog/003-configuration-dsl.md +0 -222
- data/docs/blog/004-store-model-jsonb.md +0 -237
- data/docs/blog/005-target-dsl.md +0 -284
- data/docs/blog/006-parsing-csv-sources.md +0 -300
- data/docs/blog/007-orchestrator.md +0 -247
- data/docs/blog/008-actioncable-stimulus.md +0 -376
- data/docs/blog/009-phlex-ui-components.md +0 -446
- data/docs/blog/010-controllers-routing.md +0 -374
- data/docs/blog/011-generators.md +0 -364
- data/docs/blog/012-json-api-sources.md +0 -323
- data/docs/blog/013-testing-rails-engine.md +0 -618
- data/docs/blog/014-dry-run.md +0 -307
- data/docs/blog/015-publishing-retro.md +0 -264
- data/docs/blog/016-erb-view-templates.md +0 -431
- data/docs/blog/017-showcase-final-retro.md +0 -220
- data/docs/blog/BACKLOG.md +0 -8
- data/docs/blog/SERIES.md +0 -154
- data/lib/data_porter/components/failure_alert.rb +0 -20
- data/lib/data_porter/components/preview_table.rb +0 -54
- data/lib/data_porter/components/progress_bar.rb +0 -33
- data/lib/data_porter/components/results_summary.rb +0 -19
- data/lib/data_porter/components/status_badge.rb +0 -16
- data/lib/data_porter/components/summary_cards.rb +0 -30
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module Components
|
|
5
|
+
module Preview
|
|
6
|
+
class Table < Base
|
|
7
|
+
def initialize(columns:, records:)
|
|
8
|
+
super()
|
|
9
|
+
@columns = columns
|
|
10
|
+
@records = records
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template
|
|
14
|
+
div(class: "dp-preview-table") do
|
|
15
|
+
table(class: "dp-table") do
|
|
16
|
+
render_header
|
|
17
|
+
render_body
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def render_header
|
|
25
|
+
thead do
|
|
26
|
+
tr do
|
|
27
|
+
th { "#" }
|
|
28
|
+
th { "Status" }
|
|
29
|
+
@columns.each { |col| th { col.label } }
|
|
30
|
+
th { "Errors" }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render_body
|
|
36
|
+
tbody do
|
|
37
|
+
@records.each { |record| render_row(record) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def render_row(record)
|
|
42
|
+
tr(class: "dp-row--#{record.status}") do
|
|
43
|
+
td { record.line_number.to_s }
|
|
44
|
+
td { record.status }
|
|
45
|
+
@columns.each { |col| td { record.data[col.name.to_s].to_s } }
|
|
46
|
+
td(class: "dp-errors") { error_messages(record) }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def error_messages(record)
|
|
51
|
+
record.errors_list.map(&:message).join(", ")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module Components
|
|
5
|
+
module Progress
|
|
6
|
+
class Bar < Base
|
|
7
|
+
def initialize(import_id:)
|
|
8
|
+
super()
|
|
9
|
+
@import_id = import_id
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template
|
|
13
|
+
div(class: "dp-progress", **stimulus_controller_attrs) do
|
|
14
|
+
render_bar
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def render_bar
|
|
21
|
+
div(class: "dp-progress-bar", data_data_porter__progress_target: "bar", style: "width: 0%") do
|
|
22
|
+
span(data_data_porter__progress_target: "text") { "0%" }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stimulus_controller_attrs
|
|
27
|
+
{
|
|
28
|
+
data_controller: "data-porter--progress",
|
|
29
|
+
data_data_porter__progress_id_value: @import_id.to_s
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module Components
|
|
5
|
+
module Shared
|
|
6
|
+
class FailureAlert < Base
|
|
7
|
+
def initialize(report:)
|
|
8
|
+
super()
|
|
9
|
+
@report = report
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template
|
|
13
|
+
div(class: "dp-alert dp-alert--danger") do
|
|
14
|
+
@report.error_reports.each do |err|
|
|
15
|
+
p { err.message }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module Components
|
|
5
|
+
module Shared
|
|
6
|
+
class StatusBadge < Base
|
|
7
|
+
def initialize(status:)
|
|
8
|
+
super()
|
|
9
|
+
@status = status.to_s
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template
|
|
13
|
+
span(class: "dp-badge dp-badge--#{@status}") { @status.capitalize }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "components/base"
|
|
4
|
-
require_relative "components/status_badge"
|
|
5
|
-
require_relative "components/
|
|
6
|
-
require_relative "components/
|
|
7
|
-
require_relative "components/
|
|
8
|
-
require_relative "components/results_summary"
|
|
9
|
-
require_relative "components/
|
|
4
|
+
require_relative "components/shared/status_badge"
|
|
5
|
+
require_relative "components/shared/failure_alert"
|
|
6
|
+
require_relative "components/preview/table"
|
|
7
|
+
require_relative "components/preview/summary_cards"
|
|
8
|
+
require_relative "components/preview/results_summary"
|
|
9
|
+
require_relative "components/progress/bar"
|
|
10
|
+
require_relative "components/mapping/template_select"
|
|
11
|
+
require_relative "components/mapping/column_row"
|
|
12
|
+
require_relative "components/mapping/form"
|
|
10
13
|
|
|
11
14
|
module DataPorter
|
|
12
15
|
module Components
|
data/lib/data_porter/engine.rb
CHANGED
|
@@ -5,7 +5,13 @@ module DataPorter
|
|
|
5
5
|
isolate_namespace DataPorter
|
|
6
6
|
|
|
7
7
|
initializer "data_porter.assets.precompile" do |app|
|
|
8
|
-
|
|
8
|
+
if app.config.respond_to?(:assets)
|
|
9
|
+
app.config.assets.precompile += %w[
|
|
10
|
+
data_porter/application.css
|
|
11
|
+
data_porter/turbo.min.js
|
|
12
|
+
data_porter/stimulus.min.js
|
|
13
|
+
]
|
|
14
|
+
end
|
|
9
15
|
end
|
|
10
16
|
|
|
11
17
|
config.to_prepare do
|
|
@@ -8,6 +8,16 @@ module DataPorter
|
|
|
8
8
|
@source_options = { content: content }.compact
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def extract_headers!
|
|
12
|
+
@data_import.extracting_headers!
|
|
13
|
+
source = build_source
|
|
14
|
+
headers = source.headers
|
|
15
|
+
store_headers(headers)
|
|
16
|
+
@data_import.update!(status: :mapping)
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
handle_failure(e)
|
|
19
|
+
end
|
|
20
|
+
|
|
11
21
|
def parse!
|
|
12
22
|
@data_import.parsing!
|
|
13
23
|
records = build_records
|
|
@@ -37,8 +47,18 @@ module DataPorter
|
|
|
37
47
|
|
|
38
48
|
private
|
|
39
49
|
|
|
50
|
+
def build_source
|
|
51
|
+
@data_import.source_class.new(@data_import, **@source_options)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def store_headers(headers)
|
|
55
|
+
config = @data_import.config || {}
|
|
56
|
+
config["file_headers"] = headers
|
|
57
|
+
@data_import.update!(config: config)
|
|
58
|
+
end
|
|
59
|
+
|
|
40
60
|
def build_records
|
|
41
|
-
source =
|
|
61
|
+
source = build_source
|
|
42
62
|
raw_rows = source.fetch
|
|
43
63
|
columns = @target.class._columns || []
|
|
44
64
|
validator = RecordValidator.new(columns)
|
|
@@ -15,10 +15,25 @@ module DataPorter
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
17
|
def apply_csv_mapping(row)
|
|
18
|
-
|
|
19
|
-
return auto_map(row) if mappings.nil? || mappings.empty?
|
|
18
|
+
return user_map(row) if user_mapping.any?
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
code_mappings = @target_class._csv_mappings
|
|
21
|
+
return explicit_map(row, code_mappings) if code_mappings&.any?
|
|
22
|
+
|
|
23
|
+
auto_map(row)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def user_mapping
|
|
27
|
+
config = @data_import.config
|
|
28
|
+
return {} unless config.is_a?(Hash)
|
|
29
|
+
|
|
30
|
+
config.fetch("column_mapping", {})
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def user_map(row)
|
|
34
|
+
user_mapping.each_with_object({}) do |(header, column), hash|
|
|
35
|
+
hash[column.to_sym] = row[header]
|
|
36
|
+
end
|
|
22
37
|
end
|
|
23
38
|
|
|
24
39
|
def explicit_map(row, mappings)
|
|
@@ -10,6 +10,11 @@ module DataPorter
|
|
|
10
10
|
@content = content
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
def headers
|
|
14
|
+
first_line = csv_content.lines.first
|
|
15
|
+
::CSV.parse_line(first_line, **extra_options).map(&:to_s)
|
|
16
|
+
end
|
|
17
|
+
|
|
13
18
|
def fetch
|
|
14
19
|
rows = []
|
|
15
20
|
::CSV.parse(csv_content, **csv_options) do |row|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "creek"
|
|
4
|
+
require "tempfile"
|
|
5
|
+
|
|
6
|
+
module DataPorter
|
|
7
|
+
module Sources
|
|
8
|
+
class Xlsx < Base
|
|
9
|
+
def initialize(data_import, file_path: nil)
|
|
10
|
+
super(data_import)
|
|
11
|
+
@file_path = file_path
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def headers
|
|
15
|
+
sheet = target_sheet
|
|
16
|
+
first_row = sheet.simple_rows.first
|
|
17
|
+
first_row&.values&.map(&:to_s) || []
|
|
18
|
+
ensure
|
|
19
|
+
cleanup
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def fetch
|
|
23
|
+
rows = parse_sheet(target_sheet)
|
|
24
|
+
rows.map { |row| apply_csv_mapping(row) }
|
|
25
|
+
ensure
|
|
26
|
+
cleanup
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def target_sheet
|
|
32
|
+
creek = Creek::Book.new(xlsx_path)
|
|
33
|
+
creek.sheets[sheet_index]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def parse_sheet(sheet)
|
|
37
|
+
rows = sheet.simple_rows.to_a
|
|
38
|
+
return [] if rows.size <= 1
|
|
39
|
+
|
|
40
|
+
headers = rows.first.values.map(&:to_s)
|
|
41
|
+
rows.drop(1).map { |row| build_row(headers, row) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_row(headers, row)
|
|
45
|
+
values = row.values.map { |v| v&.to_s }
|
|
46
|
+
headers.zip(values).to_h
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def xlsx_path
|
|
50
|
+
@file_path || download_to_tempfile
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def download_to_tempfile
|
|
54
|
+
@tempfile = Tempfile.new(["data_porter", ".xlsx"])
|
|
55
|
+
@tempfile.binmode
|
|
56
|
+
@tempfile.write(@data_import.file.download)
|
|
57
|
+
@tempfile.rewind
|
|
58
|
+
@tempfile.path
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def sheet_index
|
|
62
|
+
config = @data_import.config
|
|
63
|
+
return 0 unless config.is_a?(Hash)
|
|
64
|
+
|
|
65
|
+
config.fetch("sheet_index", 0).to_i
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def cleanup
|
|
69
|
+
return unless @tempfile
|
|
70
|
+
|
|
71
|
+
@tempfile.close
|
|
72
|
+
@tempfile.unlink
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/data_porter/sources.rb
CHANGED
|
@@ -4,13 +4,15 @@ require_relative "sources/base"
|
|
|
4
4
|
require_relative "sources/csv"
|
|
5
5
|
require_relative "sources/json"
|
|
6
6
|
require_relative "sources/api"
|
|
7
|
+
require_relative "sources/xlsx"
|
|
7
8
|
|
|
8
9
|
module DataPorter
|
|
9
10
|
module Sources
|
|
10
11
|
REGISTRY = {
|
|
11
12
|
api: Api,
|
|
12
13
|
csv: Csv,
|
|
13
|
-
json: Json
|
|
14
|
+
json: Json,
|
|
15
|
+
xlsx: Xlsx
|
|
14
16
|
}.freeze
|
|
15
17
|
|
|
16
18
|
def self.resolve(type)
|
data/lib/data_porter/version.rb
CHANGED
|
@@ -15,6 +15,10 @@ module DataPorter
|
|
|
15
15
|
"create_data_porter_imports.rb.erb",
|
|
16
16
|
"db/migrate/create_data_porter_imports.rb"
|
|
17
17
|
)
|
|
18
|
+
migration_template(
|
|
19
|
+
"create_data_porter_mapping_templates.rb.erb",
|
|
20
|
+
"db/migrate/create_data_porter_mapping_templates.rb"
|
|
21
|
+
)
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def copy_initializer
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDataPorterMappingTemplates < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :data_porter_mapping_templates do |t|
|
|
6
|
+
t.string :target_key, null: false
|
|
7
|
+
t.string :name, null: false
|
|
8
|
+
t.jsonb :mapping, null: false, default: {}
|
|
9
|
+
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :data_porter_mapping_templates, :target_key
|
|
14
|
+
add_index :data_porter_mapping_templates, %i[target_key name], unique: true
|
|
15
|
+
end
|
|
16
|
+
end
|