csvops 0.4.0.alpha → 0.6.0.alpha
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/README.md +60 -12
- data/docs/architecture.md +208 -21
- data/docs/release-v0.5.0-alpha.md +89 -0
- data/docs/release-v0.6.0-alpha.md +84 -0
- data/lib/csvtool/application/use_cases/run_cross_csv_dedupe.rb +17 -14
- data/lib/csvtool/application/use_cases/run_csv_parity.rb +70 -0
- data/lib/csvtool/application/use_cases/run_extraction.rb +63 -88
- data/lib/csvtool/application/use_cases/run_row_extraction.rb +45 -73
- data/lib/csvtool/application/use_cases/run_row_randomization.rb +56 -73
- data/lib/csvtool/cli.rb +11 -7
- data/lib/csvtool/domain/csv_parity_session/parity_options.rb +22 -0
- data/lib/csvtool/domain/csv_parity_session/parity_session.rb +20 -0
- data/lib/csvtool/domain/csv_parity_session/source_pair.rb +19 -0
- data/lib/csvtool/infrastructure/csv/csv_parity_comparator.rb +71 -0
- data/lib/csvtool/infrastructure/output/csv_cross_csv_dedupe_file_writer.rb +23 -0
- data/lib/csvtool/infrastructure/output/csv_file_writer.rb +1 -7
- data/lib/csvtool/infrastructure/output/csv_randomized_row_file_writer.rb +23 -0
- data/lib/csvtool/infrastructure/output/csv_row_file_writer.rb +2 -9
- data/lib/csvtool/interface/cli/errors/presenter.rb +4 -0
- data/lib/csvtool/interface/cli/menu_loop.rb +5 -2
- data/lib/csvtool/interface/cli/prompts/dedupe_key_selector_prompt.rb +30 -0
- data/lib/csvtool/interface/cli/prompts/file_path_prompt.rb +4 -2
- data/lib/csvtool/interface/cli/prompts/headers_present_prompt.rb +4 -2
- data/lib/csvtool/interface/cli/prompts/separator_prompt.rb +4 -2
- data/lib/csvtool/interface/cli/prompts/yes_no_prompt.rb +26 -0
- data/lib/csvtool/interface/cli/workflows/builders/column_session_builder.rb +32 -0
- data/lib/csvtool/interface/cli/workflows/builders/cross_csv_dedupe_session_builder.rb +35 -0
- data/lib/csvtool/interface/cli/workflows/builders/csv_parity_session_builder.rb +33 -0
- data/lib/csvtool/interface/cli/workflows/builders/row_extraction_session_builder.rb +22 -0
- data/lib/csvtool/interface/cli/workflows/builders/row_randomization_session_builder.rb +28 -0
- data/lib/csvtool/interface/cli/workflows/presenters/column_extraction_presenter.rb +25 -0
- data/lib/csvtool/interface/cli/workflows/presenters/cross_csv_dedupe_presenter.rb +39 -0
- data/lib/csvtool/interface/cli/workflows/presenters/csv_parity_presenter.rb +38 -0
- data/lib/csvtool/interface/cli/workflows/presenters/row_extraction_presenter.rb +34 -0
- data/lib/csvtool/interface/cli/workflows/presenters/row_randomization_presenter.rb +34 -0
- data/lib/csvtool/interface/cli/workflows/run_cross_csv_dedupe_workflow.rb +48 -125
- data/lib/csvtool/interface/cli/workflows/run_csv_parity_workflow.rb +66 -0
- data/lib/csvtool/interface/cli/workflows/run_extraction_workflow.rb +88 -0
- data/lib/csvtool/interface/cli/workflows/run_row_extraction_workflow.rb +86 -0
- data/lib/csvtool/interface/cli/workflows/run_row_randomization_workflow.rb +80 -0
- data/lib/csvtool/interface/cli/workflows/steps/cross_csv_dedupe/collect_options_step.rb +55 -0
- data/lib/csvtool/interface/cli/workflows/steps/cross_csv_dedupe/collect_profiles_step.rb +52 -0
- data/lib/csvtool/interface/cli/workflows/steps/cross_csv_dedupe/execute_step.rb +34 -0
- data/lib/csvtool/interface/cli/workflows/steps/extraction/build_preview_step.rb +40 -0
- data/lib/csvtool/interface/cli/workflows/steps/extraction/collect_destination_step.rb +28 -0
- data/lib/csvtool/interface/cli/workflows/steps/extraction/collect_inputs_step.rb +47 -0
- data/lib/csvtool/interface/cli/workflows/steps/extraction/execute_step.rb +32 -0
- data/lib/csvtool/interface/cli/workflows/steps/parity/build_session_step.rb +25 -0
- data/lib/csvtool/interface/cli/workflows/steps/parity/collect_inputs_step.rb +32 -0
- data/lib/csvtool/interface/cli/workflows/steps/parity/execute_step.rb +26 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_extraction/collect_destination_step.rb +33 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_extraction/collect_range_step.rb +35 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_extraction/collect_source_step.rb +32 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_extraction/execute_step.rb +43 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_extraction/read_headers_step.rb +29 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_randomization/collect_destination_step.rb +34 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_randomization/collect_inputs_step.rb +49 -0
- data/lib/csvtool/interface/cli/workflows/steps/row_randomization/execute_step.rb +37 -0
- data/lib/csvtool/interface/cli/workflows/steps/workflow_step_pipeline.rb +25 -0
- data/lib/csvtool/interface/cli/workflows/support/output_destination_mapper.rb +23 -0
- data/lib/csvtool/interface/cli/workflows/support/result_error_handler.rb +22 -0
- data/lib/csvtool/version.rb +1 -1
- data/test/csvtool/application/use_cases/io_boundary_test.rb +26 -0
- data/test/csvtool/application/use_cases/run_cross_csv_dedupe_test.rb +28 -0
- data/test/csvtool/application/use_cases/run_csv_parity_test.rb +160 -0
- data/test/csvtool/application/use_cases/run_extraction_test.rb +72 -16
- data/test/csvtool/application/use_cases/run_row_extraction_test.rb +82 -102
- data/test/csvtool/application/use_cases/run_row_randomization_test.rb +96 -86
- data/test/csvtool/cli_test.rb +175 -21
- data/test/csvtool/cli_unit_test.rb +4 -4
- data/test/csvtool/domain/csv_parity_session/parity_options_test.rb +17 -0
- data/test/csvtool/domain/csv_parity_session/parity_session_test.rb +18 -0
- data/test/csvtool/domain/csv_parity_session/source_pair_test.rb +11 -0
- data/test/csvtool/infrastructure/csv/csv_parity_comparator_test.rb +78 -0
- data/test/csvtool/infrastructure/output/csv_cross_csv_dedupe_file_writer_test.rb +32 -0
- data/test/csvtool/infrastructure/output/csv_file_writer_test.rb +0 -4
- data/test/csvtool/infrastructure/output/csv_randomized_row_file_writer_test.rb +32 -0
- data/test/csvtool/infrastructure/output/csv_row_file_writer_test.rb +1 -4
- data/test/csvtool/interface/cli/errors/presenter_test.rb +2 -0
- data/test/csvtool/interface/cli/menu_loop_test.rb +59 -16
- data/test/csvtool/interface/cli/prompts/dedupe_key_selector_prompt_test.rb +30 -0
- data/test/csvtool/interface/cli/prompts/file_path_prompt_test.rb +9 -0
- data/test/csvtool/interface/cli/prompts/headers_present_prompt_test.rb +10 -0
- data/test/csvtool/interface/cli/prompts/separator_prompt_test.rb +10 -0
- data/test/csvtool/interface/cli/prompts/yes_no_prompt_test.rb +22 -0
- data/test/csvtool/interface/cli/workflows/builders/column_session_builder_test.rb +17 -0
- data/test/csvtool/interface/cli/workflows/builders/cross_csv_dedupe_session_builder_test.rb +36 -0
- data/test/csvtool/interface/cli/workflows/builders/csv_parity_session_builder_test.rb +20 -0
- data/test/csvtool/interface/cli/workflows/builders/row_extraction_session_builder_test.rb +21 -0
- data/test/csvtool/interface/cli/workflows/builders/row_randomization_session_builder_test.rb +26 -0
- data/test/csvtool/interface/cli/workflows/presenters/column_extraction_presenter_test.rb +24 -0
- data/test/csvtool/interface/cli/workflows/presenters/cross_csv_dedupe_presenter_test.rb +30 -0
- data/test/csvtool/interface/cli/workflows/presenters/csv_parity_presenter_test.rb +43 -0
- data/test/csvtool/interface/cli/workflows/presenters/row_extraction_presenter_test.rb +33 -0
- data/test/csvtool/interface/cli/workflows/presenters/row_randomization_presenter_test.rb +33 -0
- data/test/csvtool/interface/cli/workflows/run_csv_parity_workflow_test.rb +94 -0
- data/test/csvtool/interface/cli/workflows/run_extraction_workflow_test.rb +56 -0
- data/test/csvtool/interface/cli/workflows/run_row_extraction_workflow_test.rb +83 -0
- data/test/csvtool/interface/cli/workflows/run_row_randomization_workflow_test.rb +69 -0
- data/test/csvtool/interface/cli/workflows/steps/cross_csv_dedupe/collect_options_step_test.rb +41 -0
- data/test/csvtool/interface/cli/workflows/steps/extraction/collect_inputs_step_test.rb +66 -0
- data/test/csvtool/interface/cli/workflows/steps/parity/build_session_step_test.rb +41 -0
- data/test/csvtool/interface/cli/workflows/steps/parity/collect_inputs_step_test.rb +30 -0
- data/test/csvtool/interface/cli/workflows/steps/parity/execute_step_test.rb +40 -0
- data/test/csvtool/interface/cli/workflows/steps/row_extraction/collect_source_step_test.rb +39 -0
- data/test/csvtool/interface/cli/workflows/steps/row_extraction/execute_step_test.rb +91 -0
- data/test/csvtool/interface/cli/workflows/steps/row_extraction/read_headers_step_test.rb +57 -0
- data/test/csvtool/interface/cli/workflows/steps/row_randomization/collect_inputs_step_test.rb +37 -0
- data/test/csvtool/interface/cli/workflows/steps/workflow_step_pipeline_test.rb +30 -0
- data/test/csvtool/interface/cli/workflows/support/output_destination_mapper_test.rb +23 -0
- data/test/csvtool/interface/cli/workflows/support/result_error_handler_test.rb +34 -0
- data/test/fixtures/parity_duplicates_left.csv +4 -0
- data/test/fixtures/parity_duplicates_right.csv +3 -0
- data/test/fixtures/parity_people_header_mismatch.csv +4 -0
- data/test/fixtures/parity_people_many_reordered.csv +13 -0
- data/test/fixtures/parity_people_mismatch.csv +4 -0
- data/test/fixtures/parity_people_reordered.csv +4 -0
- data/test/fixtures/parity_people_reordered.tsv +4 -0
- metadata +90 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module CrossCsvDedupe
|
|
9
|
+
class CollectOptionsStep
|
|
10
|
+
def initialize(selector_prompt:, yes_no_prompt:, output_destination_prompt:, errors:)
|
|
11
|
+
@selector_prompt = selector_prompt
|
|
12
|
+
@yes_no_prompt = yes_no_prompt
|
|
13
|
+
@output_destination_prompt = output_destination_prompt
|
|
14
|
+
@errors = errors
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(context)
|
|
18
|
+
source = context.fetch(:source)
|
|
19
|
+
reference = context.fetch(:reference)
|
|
20
|
+
|
|
21
|
+
source_selector = @selector_prompt.call(label: "Source", headers_present: source.headers_present?)
|
|
22
|
+
if source_selector.nil?
|
|
23
|
+
@errors.column_not_found
|
|
24
|
+
return :halt
|
|
25
|
+
end
|
|
26
|
+
reference_selector = @selector_prompt.call(label: "Reference", headers_present: reference.headers_present?)
|
|
27
|
+
if reference_selector.nil?
|
|
28
|
+
@errors.column_not_found
|
|
29
|
+
return :halt
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
trim_whitespace = @yes_no_prompt.call(label: "Trim whitespace before matching? [Y/n]: ", default: true)
|
|
33
|
+
case_insensitive = @yes_no_prompt.call(label: "Case-insensitive matching? [y/N]: ", default: false)
|
|
34
|
+
|
|
35
|
+
output_destination = @output_destination_prompt.call
|
|
36
|
+
return :halt if output_destination.nil?
|
|
37
|
+
|
|
38
|
+
context[:session] = context.fetch(:session_builder).call(
|
|
39
|
+
source: source,
|
|
40
|
+
reference: reference,
|
|
41
|
+
source_selector: source_selector,
|
|
42
|
+
reference_selector: reference_selector,
|
|
43
|
+
trim_whitespace: trim_whitespace,
|
|
44
|
+
case_insensitive: case_insensitive,
|
|
45
|
+
destination: context.fetch(:output_destination_mapper).call(output_destination)
|
|
46
|
+
)
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "csvtool/domain/cross_csv_dedupe_session/csv_profile"
|
|
4
|
+
|
|
5
|
+
module Csvtool
|
|
6
|
+
module Interface
|
|
7
|
+
module CLI
|
|
8
|
+
module Workflows
|
|
9
|
+
module Steps
|
|
10
|
+
module CrossCsvDedupe
|
|
11
|
+
class CollectProfilesStep
|
|
12
|
+
def initialize(file_path_prompt:, separator_prompt:, headers_present_prompt:, errors:)
|
|
13
|
+
@file_path_prompt = file_path_prompt
|
|
14
|
+
@separator_prompt = separator_prompt
|
|
15
|
+
@headers_present_prompt = headers_present_prompt
|
|
16
|
+
@errors = errors
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(context)
|
|
20
|
+
source_path = @file_path_prompt.call
|
|
21
|
+
return @errors.file_not_found(source_path) || :halt unless File.file?(source_path)
|
|
22
|
+
|
|
23
|
+
source_col_sep = @separator_prompt.call(label: "Source CSV separator:")
|
|
24
|
+
return :halt if source_col_sep.nil?
|
|
25
|
+
source_headers_present = @headers_present_prompt.call(label: "Source headers present? [Y/n]: ")
|
|
26
|
+
|
|
27
|
+
reference_path = @file_path_prompt.call(label: "Reference CSV file path: ")
|
|
28
|
+
return @errors.file_not_found(reference_path) || :halt unless File.file?(reference_path)
|
|
29
|
+
|
|
30
|
+
reference_col_sep = @separator_prompt.call(label: "Reference CSV separator:")
|
|
31
|
+
return :halt if reference_col_sep.nil?
|
|
32
|
+
reference_headers_present = @headers_present_prompt.call(label: "Reference headers present? [Y/n]: ")
|
|
33
|
+
|
|
34
|
+
context[:source] = Domain::CrossCsvDedupeSession::CsvProfile.new(
|
|
35
|
+
path: source_path,
|
|
36
|
+
separator: source_col_sep,
|
|
37
|
+
headers_present: source_headers_present
|
|
38
|
+
)
|
|
39
|
+
context[:reference] = Domain::CrossCsvDedupeSession::CsvProfile.new(
|
|
40
|
+
path: reference_path,
|
|
41
|
+
separator: reference_col_sep,
|
|
42
|
+
headers_present: reference_headers_present
|
|
43
|
+
)
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module CrossCsvDedupe
|
|
9
|
+
class ExecuteStep
|
|
10
|
+
def call(context)
|
|
11
|
+
session = context.fetch(:session)
|
|
12
|
+
presenter = context.fetch(:presenter_factory).call(col_sep: session.source.separator)
|
|
13
|
+
|
|
14
|
+
result = context.fetch(:use_case).call(
|
|
15
|
+
session: session,
|
|
16
|
+
on_header: ->(headers) { presenter.print_header(headers) },
|
|
17
|
+
on_row: ->(fields) { presenter.print_row(fields) }
|
|
18
|
+
)
|
|
19
|
+
unless result.ok?
|
|
20
|
+
context.fetch(:handle_error).call(result)
|
|
21
|
+
return :halt
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
presenter.print_file_written(result.data[:output_path]) if session.output_destination.file?
|
|
25
|
+
presenter.print_summary(result.data[:stats])
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "csvtool/domain/column_session/extraction_value"
|
|
4
|
+
require "csvtool/domain/column_session/preview"
|
|
5
|
+
|
|
6
|
+
module Csvtool
|
|
7
|
+
module Interface
|
|
8
|
+
module CLI
|
|
9
|
+
module Workflows
|
|
10
|
+
module Steps
|
|
11
|
+
module Extraction
|
|
12
|
+
class BuildPreviewStep
|
|
13
|
+
def initialize(confirm_prompt:)
|
|
14
|
+
@confirm_prompt = confirm_prompt
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(context)
|
|
18
|
+
preview_result = context.fetch(:use_case).preview(session: context.fetch(:session))
|
|
19
|
+
unless preview_result.ok?
|
|
20
|
+
context.fetch(:handle_error).call(preview_result)
|
|
21
|
+
return :halt
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
preview = Domain::ColumnSession::Preview.new(
|
|
25
|
+
values: preview_result.data[:preview_values].map { |value| Domain::ColumnSession::ExtractionValue.new(value) }
|
|
26
|
+
)
|
|
27
|
+
session = context.fetch(:session).with_preview(preview)
|
|
28
|
+
confirmed = @confirm_prompt.call(session.preview.to_strings)
|
|
29
|
+
return :halt unless confirmed
|
|
30
|
+
|
|
31
|
+
context[:session] = session.confirm!
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module Extraction
|
|
9
|
+
class CollectDestinationStep
|
|
10
|
+
def initialize(output_destination_prompt:)
|
|
11
|
+
@output_destination_prompt = output_destination_prompt
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(context)
|
|
15
|
+
output_destination = @output_destination_prompt.call
|
|
16
|
+
return :halt if output_destination.nil?
|
|
17
|
+
|
|
18
|
+
destination = context.fetch(:output_destination_mapper).call(output_destination)
|
|
19
|
+
context[:session] = context.fetch(:session).with_output_destination(destination)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module Extraction
|
|
9
|
+
class CollectInputsStep
|
|
10
|
+
def initialize(file_path_prompt:, separator_prompt:, column_selector_prompt:, skip_blanks_prompt:)
|
|
11
|
+
@file_path_prompt = file_path_prompt
|
|
12
|
+
@separator_prompt = separator_prompt
|
|
13
|
+
@column_selector_prompt = column_selector_prompt
|
|
14
|
+
@skip_blanks_prompt = skip_blanks_prompt
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(context)
|
|
18
|
+
file_path = @file_path_prompt.call
|
|
19
|
+
col_sep = @separator_prompt.call
|
|
20
|
+
return :halt if col_sep.nil?
|
|
21
|
+
|
|
22
|
+
header_result = context.fetch(:use_case).read_headers(file_path: file_path, col_sep: col_sep)
|
|
23
|
+
unless header_result.ok?
|
|
24
|
+
context.fetch(:handle_error).call(header_result)
|
|
25
|
+
return :halt
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
headers = header_result.data[:headers]
|
|
29
|
+
column_name = @column_selector_prompt.call(headers)
|
|
30
|
+
return :halt if column_name.nil?
|
|
31
|
+
|
|
32
|
+
skip_blanks = @skip_blanks_prompt.call
|
|
33
|
+
context[:session] = context.fetch(:session_builder).call(
|
|
34
|
+
file_path: file_path,
|
|
35
|
+
col_sep: col_sep,
|
|
36
|
+
column_name: column_name,
|
|
37
|
+
skip_blanks: skip_blanks
|
|
38
|
+
)
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module Extraction
|
|
9
|
+
class ExecuteStep
|
|
10
|
+
def call(context)
|
|
11
|
+
result = context.fetch(:use_case).extract(
|
|
12
|
+
session: context.fetch(:session),
|
|
13
|
+
on_value: ->(value) { context.fetch(:presenter).print_value(value) }
|
|
14
|
+
)
|
|
15
|
+
unless result.ok?
|
|
16
|
+
context.fetch(:handle_error).call(result)
|
|
17
|
+
return :halt
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
session = context.fetch(:session)
|
|
21
|
+
if session.output_destination.file?
|
|
22
|
+
context.fetch(:presenter).print_file_written(result.data[:output_path])
|
|
23
|
+
end
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module Parity
|
|
9
|
+
class BuildSessionStep
|
|
10
|
+
def call(context)
|
|
11
|
+
context[:session] = context.fetch(:session_builder).call(
|
|
12
|
+
left_path: context.fetch(:left_path),
|
|
13
|
+
right_path: context.fetch(:right_path),
|
|
14
|
+
col_sep: context.fetch(:col_sep),
|
|
15
|
+
headers_present: context.fetch(:headers_present)
|
|
16
|
+
)
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module Parity
|
|
9
|
+
class CollectInputsStep
|
|
10
|
+
def initialize(file_path_prompt:, separator_prompt:, headers_present_prompt:)
|
|
11
|
+
@file_path_prompt = file_path_prompt
|
|
12
|
+
@separator_prompt = separator_prompt
|
|
13
|
+
@headers_present_prompt = headers_present_prompt
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(context)
|
|
17
|
+
context[:left_path] = @file_path_prompt.call(label: "Left CSV file path: ")
|
|
18
|
+
context[:right_path] = @file_path_prompt.call(label: "Right CSV file path: ")
|
|
19
|
+
col_sep = @separator_prompt.call
|
|
20
|
+
return :halt if col_sep.nil?
|
|
21
|
+
|
|
22
|
+
context[:col_sep] = col_sep
|
|
23
|
+
context[:headers_present] = @headers_present_prompt.call
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module Parity
|
|
9
|
+
class ExecuteStep
|
|
10
|
+
def call(context)
|
|
11
|
+
result = context.fetch(:use_case).call(session: context.fetch(:session))
|
|
12
|
+
unless result.ok?
|
|
13
|
+
context.fetch(:handle_error).call(result)
|
|
14
|
+
return :halt
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context.fetch(:presenter).print_summary(result.data)
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module RowExtraction
|
|
9
|
+
class CollectDestinationStep
|
|
10
|
+
def initialize(output_destination_prompt:)
|
|
11
|
+
@output_destination_prompt = output_destination_prompt
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(context)
|
|
15
|
+
output_destination = @output_destination_prompt.call
|
|
16
|
+
return :halt if output_destination.nil?
|
|
17
|
+
|
|
18
|
+
destination = context.fetch(:output_destination_mapper).call(output_destination)
|
|
19
|
+
context[:session] = context.fetch(:session_builder).call(
|
|
20
|
+
file_path: context.fetch(:file_path),
|
|
21
|
+
col_sep: context.fetch(:col_sep),
|
|
22
|
+
row_range: context.fetch(:row_range),
|
|
23
|
+
destination: destination
|
|
24
|
+
)
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "csvtool/domain/row_session/row_range"
|
|
4
|
+
|
|
5
|
+
module Csvtool
|
|
6
|
+
module Interface
|
|
7
|
+
module CLI
|
|
8
|
+
module Workflows
|
|
9
|
+
module Steps
|
|
10
|
+
module RowExtraction
|
|
11
|
+
class CollectRangeStep
|
|
12
|
+
def initialize(stdin:, stdout:)
|
|
13
|
+
@stdin = stdin
|
|
14
|
+
@stdout = stdout
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(context)
|
|
18
|
+
@stdout.print "Start row (1-based, inclusive): "
|
|
19
|
+
start_row_input = @stdin.gets&.strip.to_s
|
|
20
|
+
@stdout.print "End row (1-based, inclusive): "
|
|
21
|
+
end_row_input = @stdin.gets&.strip.to_s
|
|
22
|
+
|
|
23
|
+
context[:row_range] = Domain::RowSession::RowRange.from_inputs(
|
|
24
|
+
start_row_input: start_row_input,
|
|
25
|
+
end_row_input: end_row_input
|
|
26
|
+
)
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "csvtool/interface/cli/prompts/file_path_prompt"
|
|
4
|
+
require "csvtool/interface/cli/prompts/separator_prompt"
|
|
5
|
+
|
|
6
|
+
module Csvtool
|
|
7
|
+
module Interface
|
|
8
|
+
module CLI
|
|
9
|
+
module Workflows
|
|
10
|
+
module Steps
|
|
11
|
+
module RowExtraction
|
|
12
|
+
class CollectSourceStep
|
|
13
|
+
def initialize(file_path_prompt:, separator_prompt:)
|
|
14
|
+
@file_path_prompt = file_path_prompt
|
|
15
|
+
@separator_prompt = separator_prompt
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(context)
|
|
19
|
+
context[:file_path] = @file_path_prompt.call
|
|
20
|
+
col_sep = @separator_prompt.call
|
|
21
|
+
return :halt if col_sep.nil?
|
|
22
|
+
|
|
23
|
+
context[:col_sep] = col_sep
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module RowExtraction
|
|
9
|
+
class ExecuteStep
|
|
10
|
+
def initialize(stdout:, errors:, presenter_class: Presenters::RowExtractionPresenter)
|
|
11
|
+
@stdout = stdout
|
|
12
|
+
@errors = errors
|
|
13
|
+
@presenter_class = presenter_class
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(context)
|
|
17
|
+
session = context.fetch(:session)
|
|
18
|
+
presenter = @presenter_class.new(
|
|
19
|
+
stdout: @stdout,
|
|
20
|
+
headers: context.fetch(:headers),
|
|
21
|
+
col_sep: ","
|
|
22
|
+
)
|
|
23
|
+
result = context.fetch(:use_case).extract(
|
|
24
|
+
session: session,
|
|
25
|
+
headers: context.fetch(:headers),
|
|
26
|
+
on_row: ->(fields) { presenter.print_row(fields) }
|
|
27
|
+
)
|
|
28
|
+
unless result.ok?
|
|
29
|
+
context.fetch(:handle_error).call(result)
|
|
30
|
+
return :halt
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
presenter.print_file_written(result.data[:output_path]) if result.data[:wrote_rows]
|
|
34
|
+
@errors.row_range_out_of_bounds(result.data[:row_count]) unless result.data[:matched]
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module RowExtraction
|
|
9
|
+
class ReadHeadersStep
|
|
10
|
+
def call(context)
|
|
11
|
+
result = context.fetch(:use_case).read_headers(
|
|
12
|
+
file_path: context.fetch(:file_path),
|
|
13
|
+
col_sep: context.fetch(:col_sep)
|
|
14
|
+
)
|
|
15
|
+
unless result.ok?
|
|
16
|
+
context.fetch(:handle_error).call(result)
|
|
17
|
+
return :halt
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context[:headers] = result.data[:headers]
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module RowRandomization
|
|
9
|
+
class CollectDestinationStep
|
|
10
|
+
def initialize(output_destination_prompt:)
|
|
11
|
+
@output_destination_prompt = output_destination_prompt
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(context)
|
|
15
|
+
output_destination = @output_destination_prompt.call
|
|
16
|
+
return :halt if output_destination.nil?
|
|
17
|
+
|
|
18
|
+
destination = context.fetch(:output_destination_mapper).call(output_destination)
|
|
19
|
+
context[:session] = context.fetch(:session_builder).call(
|
|
20
|
+
file_path: context.fetch(:file_path),
|
|
21
|
+
col_sep: context.fetch(:col_sep),
|
|
22
|
+
headers_present: context.fetch(:headers_present),
|
|
23
|
+
seed: context.fetch(:seed),
|
|
24
|
+
destination: destination
|
|
25
|
+
)
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module RowRandomization
|
|
9
|
+
class CollectInputsStep
|
|
10
|
+
def initialize(file_path_prompt:, separator_prompt:, headers_present_prompt:, seed_prompt:)
|
|
11
|
+
@file_path_prompt = file_path_prompt
|
|
12
|
+
@separator_prompt = separator_prompt
|
|
13
|
+
@headers_present_prompt = headers_present_prompt
|
|
14
|
+
@seed_prompt = seed_prompt
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(context)
|
|
18
|
+
file_path = @file_path_prompt.call
|
|
19
|
+
col_sep = @separator_prompt.call
|
|
20
|
+
return :halt if col_sep.nil?
|
|
21
|
+
|
|
22
|
+
headers_present = @headers_present_prompt.call
|
|
23
|
+
header_result = context.fetch(:use_case).read_headers(
|
|
24
|
+
file_path: file_path,
|
|
25
|
+
col_sep: col_sep,
|
|
26
|
+
headers_present: headers_present
|
|
27
|
+
)
|
|
28
|
+
unless header_result.ok?
|
|
29
|
+
context.fetch(:handle_error).call(header_result)
|
|
30
|
+
return :halt
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
seed = @seed_prompt.call
|
|
34
|
+
return :halt if seed == Interface::CLI::Prompts::SeedPrompt::INVALID
|
|
35
|
+
|
|
36
|
+
context[:file_path] = file_path
|
|
37
|
+
context[:col_sep] = col_sep
|
|
38
|
+
context[:headers_present] = headers_present
|
|
39
|
+
context[:headers] = header_result.data[:headers]
|
|
40
|
+
context[:seed] = seed
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Csvtool
|
|
4
|
+
module Interface
|
|
5
|
+
module CLI
|
|
6
|
+
module Workflows
|
|
7
|
+
module Steps
|
|
8
|
+
module RowRandomization
|
|
9
|
+
class ExecuteStep
|
|
10
|
+
def call(context)
|
|
11
|
+
session = context.fetch(:session)
|
|
12
|
+
presenter = context.fetch(:presenter_factory).call(
|
|
13
|
+
headers: context.fetch(:headers),
|
|
14
|
+
col_sep: session.source.separator
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
presenter.print_console_start unless session.output_destination.file?
|
|
18
|
+
result = context.fetch(:use_case).randomize(
|
|
19
|
+
session: session,
|
|
20
|
+
headers: context.fetch(:headers),
|
|
21
|
+
on_row: ->(fields) { presenter.print_row(fields) }
|
|
22
|
+
)
|
|
23
|
+
unless result.ok?
|
|
24
|
+
context.fetch(:handle_error).call(result)
|
|
25
|
+
return :halt
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
presenter.print_file_written(result.data[:output_path]) if session.output_destination.file?
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|