cure 0.1.1 → 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/.rubocop.yml +16 -3
- data/.tool-versions +1 -0
- data/Dockerfile +1 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +25 -6
- data/README.md +59 -81
- data/docs/README.md +33 -0
- data/docs/about.md +219 -0
- data/docs/builder/add.md +52 -0
- data/docs/builder/black_white_list.md +83 -0
- data/docs/builder/copy.md +48 -0
- data/docs/builder/explode.md +70 -0
- data/docs/builder/main.md +43 -0
- data/docs/builder/remove.md +46 -0
- data/docs/examples/examples.md +164 -0
- data/docs/export/main.md +37 -0
- data/docs/extract/main.md +89 -0
- data/docs/metadata/main.md +29 -0
- data/docs/query/main.md +45 -0
- data/docs/sources/main.md +36 -0
- data/docs/transform/main.md +53 -0
- data/docs/validate/main.md +42 -0
- data/exe/cure +12 -37
- data/exe/cure.old +59 -0
- data/lib/cure/builder/base_builder.rb +151 -0
- data/lib/cure/builder/candidate.rb +56 -0
- data/lib/cure/cli/command.rb +105 -0
- data/lib/cure/cli/generate_command.rb +54 -0
- data/lib/cure/cli/new_command.rb +52 -0
- data/lib/cure/cli/run_command.rb +19 -0
- data/lib/cure/cli/templates/README.md.erb +1 -0
- data/lib/cure/cli/templates/gemfile.erb +5 -0
- data/lib/cure/cli/templates/gitignore.erb +181 -0
- data/lib/cure/cli/templates/new_template.rb.erb +31 -0
- data/lib/cure/cli/templates/tool-versions.erb +1 -0
- data/lib/cure/config.rb +151 -13
- data/lib/cure/coordinator.rb +108 -0
- data/lib/cure/database.rb +191 -0
- data/lib/cure/dsl/builder.rb +26 -0
- data/lib/cure/dsl/exporters.rb +45 -0
- data/lib/cure/dsl/extraction.rb +60 -0
- data/lib/cure/dsl/metadata.rb +33 -0
- data/lib/cure/dsl/queries.rb +36 -0
- data/lib/cure/dsl/source_files.rb +36 -0
- data/lib/cure/dsl/template.rb +131 -0
- data/lib/cure/dsl/transformations.rb +95 -0
- data/lib/cure/dsl/validator.rb +22 -0
- data/lib/cure/export/base_processor.rb +194 -0
- data/lib/cure/export/manager.rb +24 -0
- data/lib/cure/extract/base_processor.rb +47 -0
- data/lib/cure/extract/csv_lookup.rb +43 -0
- data/lib/cure/extract/extractor.rb +80 -0
- data/lib/cure/extract/filter.rb +118 -0
- data/lib/cure/extract/named_range.rb +94 -0
- data/lib/cure/extract/named_range_processor.rb +128 -0
- data/lib/cure/extract/variable.rb +25 -0
- data/lib/cure/extract/variable_processor.rb +57 -0
- data/lib/cure/generator/base_generator.rb +61 -0
- data/lib/cure/generator/case_generator.rb +32 -0
- data/lib/cure/generator/character_generator.rb +41 -0
- data/lib/cure/generator/erb_generator.rb +21 -0
- data/lib/cure/generator/eval_generator.rb +34 -0
- data/lib/cure/generator/faker_generator.rb +31 -0
- data/lib/cure/generator/guid_generator.rb +21 -0
- data/lib/cure/generator/hex_generator.rb +21 -0
- data/lib/cure/generator/imports.rb +16 -0
- data/lib/cure/generator/number_generator.rb +21 -0
- data/lib/cure/generator/placeholder_generator.rb +26 -0
- data/lib/cure/generator/proc_generator.rb +21 -0
- data/lib/cure/generator/redact_generator.rb +22 -0
- data/lib/cure/generator/static_generator.rb +21 -0
- data/lib/cure/generator/variable_generator.rb +26 -0
- data/lib/cure/helpers/file_helpers.rb +50 -0
- data/lib/cure/helpers/object_helpers.rb +17 -0
- data/lib/cure/helpers/perf_helpers.rb +30 -0
- data/lib/cure/helpers/string.rb +54 -0
- data/lib/cure/launcher.rb +125 -0
- data/lib/cure/log.rb +10 -3
- data/lib/cure/planner.rb +136 -0
- data/lib/cure/strategy/append_strategy.rb +28 -0
- data/lib/cure/strategy/base_strategy.rb +98 -0
- data/lib/cure/strategy/contain_strategy.rb +51 -0
- data/lib/cure/strategy/end_with_strategy.rb +52 -0
- data/lib/cure/strategy/full_strategy.rb +28 -0
- data/lib/cure/strategy/history/history_cache.rb +82 -0
- data/lib/cure/strategy/imports.rb +12 -0
- data/lib/cure/strategy/match_strategy.rb +48 -0
- data/lib/cure/strategy/prepend_strategy.rb +28 -0
- data/lib/cure/strategy/regex_strategy.rb +55 -0
- data/lib/cure/strategy/split_strategy.rb +58 -0
- data/lib/cure/strategy/start_with_strategy.rb +53 -0
- data/lib/cure/transformation/candidate.rb +47 -36
- data/lib/cure/transformation/transform.rb +29 -71
- data/lib/cure/validator/base_rule.rb +78 -0
- data/lib/cure/validator/candidate.rb +54 -0
- data/lib/cure/validator/manager.rb +21 -0
- data/lib/cure/validators.rb +71 -0
- data/lib/cure/version.rb +1 -1
- data/lib/cure.rb +19 -6
- data/templates/dsl_example.rb +48 -0
- data/templates/empty_template.rb +31 -0
- metadata +161 -23
- data/lib/cure/csv_helpers.rb +0 -6
- data/lib/cure/export/exporter.rb +0 -49
- data/lib/cure/file_helpers.rb +0 -38
- data/lib/cure/generator/base.rb +0 -148
- data/lib/cure/main.rb +0 -63
- data/lib/cure/object_helpers.rb +0 -27
- data/lib/cure/strategy/base.rb +0 -223
- data/templates/aws_cur_template.json +0 -143
- data/templates/example_template.json +0 -38
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/strategy/base_strategy"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Strategy
|
7
|
+
class StartWithStrategy < BaseStrategy
|
8
|
+
# validates :match
|
9
|
+
|
10
|
+
# Additional details needed to make substitution.
|
11
|
+
# @return [StartWithStrategyParams]
|
12
|
+
attr_accessor :params
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
super(StartWithStrategyParams.new(options))
|
16
|
+
end
|
17
|
+
|
18
|
+
# gsub catchment group
|
19
|
+
# @param [String] source_value
|
20
|
+
def _retrieve_value(source_value)
|
21
|
+
@params.match || nil if source_value.start_with? @params.match
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [String] source_value
|
25
|
+
# @param [String] generated_value
|
26
|
+
# @return [String]
|
27
|
+
def _replace_value(source_value, generated_value)
|
28
|
+
return unless source_value.start_with? @params.match
|
29
|
+
|
30
|
+
return generated_value unless replace_partial_record
|
31
|
+
|
32
|
+
@params.match + generated_value
|
33
|
+
end
|
34
|
+
|
35
|
+
def _describe
|
36
|
+
"Start with replacement will look for '#{@params.match}'. " \
|
37
|
+
"It will do a #{replace_partial_record ? "partial" : "full"} replacement. " \
|
38
|
+
"[Note: If the value does not include '#{@params.match}', no substitution is made.]"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class StartWithStrategyParams < BaseStrategyParams
|
43
|
+
attr_reader :match
|
44
|
+
|
45
|
+
validates :match
|
46
|
+
|
47
|
+
def initialize(options=nil)
|
48
|
+
@match = options[:match]
|
49
|
+
super(options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,86 +1,97 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "cure/object_helpers"
|
4
|
-
require "cure/strategy/
|
5
|
-
require "cure/generator/
|
3
|
+
require "cure/helpers/object_helpers"
|
4
|
+
require "cure/strategy/imports"
|
5
|
+
require "cure/generator/imports"
|
6
|
+
require "cure/extract/extractor"
|
6
7
|
require "cure/log"
|
7
8
|
|
8
9
|
module Cure
|
9
10
|
module Transformation
|
10
11
|
# Per row, we will have a candidate for each transformation that needs to be made
|
11
12
|
class Candidate
|
12
|
-
include ObjectHelpers
|
13
|
+
include Helpers::ObjectHelpers
|
13
14
|
include Log
|
14
15
|
|
16
|
+
# Named range that column exists in
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :named_range
|
19
|
+
|
15
20
|
# Lookup column name for CSV.
|
16
21
|
# @return [String]
|
17
|
-
|
22
|
+
attr_reader :column
|
18
23
|
|
19
24
|
# What sort of data needs to be generated.
|
20
25
|
# @return [List<Translation>]
|
21
26
|
attr_reader :translations
|
22
27
|
|
28
|
+
# @return [Translation]
|
23
29
|
attr_reader :no_match_translation
|
24
30
|
|
25
|
-
|
26
|
-
|
31
|
+
attr_reader :ignore_empty
|
32
|
+
|
33
|
+
def initialize(column, named_range: Cure::Extraction.default_named_range, options: {})
|
34
|
+
@column = column
|
35
|
+
@named_range = named_range
|
36
|
+
@ignore_empty = options.fetch(:ignore_empty, false)
|
37
|
+
|
38
|
+
@translations = []
|
39
|
+
@no_match_translation = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [String, Nil] source_value
|
43
|
+
# @param [RowCtx] row_ctx
|
44
|
+
# @return [String,nil]
|
27
45
|
# Transforms the existing value
|
28
|
-
def perform(source_value)
|
29
|
-
# log_debug("Performing substitution for [#{@column}] with [#{@translations.length}] translations")
|
46
|
+
def perform(source_value, row_ctx)
|
30
47
|
value = source_value
|
31
48
|
|
32
49
|
@translations.each do |translation|
|
33
|
-
temp = translation.extract(value)
|
50
|
+
temp = translation.extract(value, row_ctx)
|
34
51
|
value = temp if temp
|
35
52
|
end
|
36
53
|
|
37
|
-
if value == source_value
|
38
|
-
|
39
|
-
value = @no_match_translation
|
40
|
-
|
54
|
+
if value == source_value && @no_match_translation
|
55
|
+
log_trace("No translation made for #{value} [#{source_value}]")
|
56
|
+
value = @no_match_translation.extract(source_value, row_ctx)
|
57
|
+
log_trace("Translated to #{value} from [#{source_value}]")
|
41
58
|
end
|
42
59
|
|
43
60
|
value
|
44
61
|
end
|
45
62
|
|
46
|
-
def translations
|
47
|
-
@translations =
|
63
|
+
def with_translations(translations)
|
64
|
+
@translations = translations
|
65
|
+
self
|
48
66
|
end
|
49
67
|
|
50
|
-
def no_match_translation
|
51
|
-
@no_match_translation =
|
68
|
+
def with_no_match_translation(no_match_translation)
|
69
|
+
@no_match_translation = no_match_translation
|
70
|
+
self
|
52
71
|
end
|
53
72
|
end
|
54
73
|
|
55
74
|
class Translation
|
56
|
-
include ObjectHelpers
|
75
|
+
include Helpers::ObjectHelpers
|
57
76
|
|
58
77
|
# What sort of replacement is done, full/random/lookup/partial.
|
59
|
-
# @return [Strategy::
|
78
|
+
# @return [Strategy::BaseStrategy]
|
60
79
|
attr_reader :strategy
|
61
80
|
|
62
81
|
# What sort of data needs to be generated.
|
63
|
-
# @return [Generator::
|
82
|
+
# @return [Generator::BaseGenerator]
|
64
83
|
attr_reader :generator
|
65
84
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
@strategy.extract(source_value, @generator)
|
70
|
-
end
|
71
|
-
|
72
|
-
# @param [Hash] opts
|
73
|
-
def strategy=(opts)
|
74
|
-
clazz_name = "Cure::Strategy::#{opts["name"].to_s.capitalize}Strategy"
|
75
|
-
@strategy = Kernel.const_get(clazz_name).new(opts["options"] || {})
|
85
|
+
def initialize(strategy, generator)
|
86
|
+
@strategy = strategy
|
87
|
+
@generator = generator
|
76
88
|
end
|
77
89
|
|
78
|
-
# @param [
|
79
|
-
|
80
|
-
|
81
|
-
@
|
90
|
+
# @param [String] source_value
|
91
|
+
# @return [String]
|
92
|
+
def extract(source_value, row_ctx)
|
93
|
+
@strategy.extract(source_value, row_ctx, @generator)
|
82
94
|
end
|
83
95
|
end
|
84
96
|
end
|
85
97
|
end
|
86
|
-
|
@@ -1,14 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "cure/log"
|
4
|
-
require "cure/file_helpers"
|
4
|
+
require "cure/helpers/file_helpers"
|
5
|
+
require "cure/config"
|
6
|
+
require "cure/extract/extractor"
|
7
|
+
|
5
8
|
require "rcsv"
|
6
9
|
|
7
10
|
module Cure
|
8
11
|
module Transformation
|
12
|
+
# Operational file for conducting transforms
|
9
13
|
class Transform
|
10
14
|
include Log
|
11
|
-
include FileHelpers
|
15
|
+
include Helpers::FileHelpers
|
16
|
+
include Configuration
|
12
17
|
|
13
18
|
# @return [Array<Candidate>]
|
14
19
|
attr_accessor :candidates
|
@@ -18,87 +23,40 @@ module Cure
|
|
18
23
|
@candidates = candidates
|
19
24
|
end
|
20
25
|
|
21
|
-
# @param [
|
22
|
-
# @return [
|
23
|
-
def
|
24
|
-
|
25
|
-
extract_from_contents(file_contents)
|
26
|
-
end
|
27
|
-
|
28
|
-
# @param [String] file_contents
|
29
|
-
# @return [TransformContext]
|
30
|
-
def extract_from_contents(file_contents)
|
31
|
-
ctx = TransformContext.new
|
32
|
-
parse_content(ctx, file_contents, header: :none) do |row|
|
33
|
-
if ctx.row_count == 1
|
34
|
-
ctx.extract_column_headers(row)
|
35
|
-
next
|
36
|
-
end
|
37
|
-
|
38
|
-
row = transform(ctx.column_headers, row)
|
39
|
-
ctx.add_transformed_row(row)
|
40
|
-
end
|
26
|
+
# @param [Hash] row
|
27
|
+
# @return [Hash]
|
28
|
+
def transform(row)
|
29
|
+
original_row = row.dup
|
41
30
|
|
42
|
-
|
43
|
-
|
31
|
+
@candidates.each do |candidate|
|
32
|
+
column = candidate.column.to_sym
|
44
33
|
|
45
|
-
|
34
|
+
next unless row.key?(column)
|
46
35
|
|
47
|
-
|
48
|
-
|
49
|
-
# @param [Proc] _block
|
50
|
-
# @param [Hash] opts
|
51
|
-
# @yield [Array] row
|
52
|
-
# @yield [TransformContext] ctx
|
53
|
-
# @return [TransformContext]
|
54
|
-
def parse_content(ctx, file_contents, opts={}, &_block)
|
55
|
-
return nil unless block_given?
|
36
|
+
existing_value = row[column]
|
37
|
+
next if existing_value.nil? && candidate.ignore_empty
|
56
38
|
|
57
|
-
|
58
|
-
|
59
|
-
yield row
|
39
|
+
new_value = candidate.perform(existing_value, RowCtx.new(row, original_row: original_row)) # transform value
|
40
|
+
row[column] = new_value
|
60
41
|
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# @param [Hash] column_headers
|
64
|
-
# @param [Array] row
|
65
|
-
# @return [Array]
|
66
|
-
def transform(column_headers, row)
|
67
|
-
@candidates.each do |candidate|
|
68
|
-
column_idx = column_headers[candidate.column]
|
69
|
-
next unless column_idx
|
70
|
-
|
71
|
-
existing_value = row[column_idx]
|
72
|
-
next unless existing_value
|
73
42
|
|
74
|
-
|
75
|
-
|
76
|
-
end
|
43
|
+
remove_system_columns(row)
|
44
|
+
end
|
77
45
|
|
46
|
+
def remove_system_columns(row)
|
47
|
+
row.delete(:_id)
|
78
48
|
row
|
79
49
|
end
|
80
50
|
end
|
81
51
|
|
82
|
-
class
|
83
|
-
|
84
|
-
|
85
|
-
attr_accessor :
|
86
|
-
:transformed_rows,
|
87
|
-
:column_headers
|
88
|
-
|
89
|
-
def initialize
|
90
|
-
@row_count = 0
|
91
|
-
@transformed_rows = []
|
92
|
-
@column_headers = {}
|
93
|
-
end
|
94
|
-
|
95
|
-
# @param [Array<String>] row
|
96
|
-
def extract_column_headers(row)
|
97
|
-
row.each_with_index { |column, idx| @column_headers[column] = idx }
|
98
|
-
end
|
52
|
+
# This class looks useless, but it isn't. It exists purely to give a hook to add
|
53
|
+
# more stuff to a strategy in the future without the method signature changing
|
54
|
+
class RowCtx
|
55
|
+
attr_accessor :row, :original_row
|
99
56
|
|
100
|
-
def
|
101
|
-
@
|
57
|
+
def initialize(row, original_row: nil)
|
58
|
+
@row = row
|
59
|
+
@original_row = original_row
|
102
60
|
end
|
103
61
|
end
|
104
62
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/log"
|
4
|
+
require "cure/config"
|
5
|
+
require "cure/database"
|
6
|
+
require "cure/helpers/file_helpers"
|
7
|
+
require "cure/extract/extractor"
|
8
|
+
|
9
|
+
require "rcsv"
|
10
|
+
|
11
|
+
module Cure
|
12
|
+
module Validator
|
13
|
+
class BaseRule
|
14
|
+
|
15
|
+
def initialize(named_range, column, options)
|
16
|
+
@named_range = named_range
|
17
|
+
@column = column
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def process(_value)
|
22
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"Base Rule"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class NotNullRule < BaseRule
|
31
|
+
def process(value)
|
32
|
+
!value.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"Not null"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class LengthRule < BaseRule
|
41
|
+
def process(value)
|
42
|
+
return true if value.nil?
|
43
|
+
return true unless value.respond_to? :size
|
44
|
+
|
45
|
+
length = value.size
|
46
|
+
length >= min && length <= max
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"Length [Min: #{min}, Max: #{max}]"
|
51
|
+
end
|
52
|
+
|
53
|
+
def min
|
54
|
+
@min || @options.fetch(:max, 0)
|
55
|
+
end
|
56
|
+
|
57
|
+
def max
|
58
|
+
@max || @options.fetch(:max, 99_999)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class CustomRule < BaseRule
|
63
|
+
def process(value)
|
64
|
+
return true if value.nil?
|
65
|
+
|
66
|
+
custom_proc.call(value)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
"Custom"
|
71
|
+
end
|
72
|
+
|
73
|
+
def custom_proc
|
74
|
+
@options.fetch(:proc, proc { |_x| false })
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/helpers/object_helpers"
|
4
|
+
require "cure/validator/base_rule"
|
5
|
+
require "cure/log"
|
6
|
+
|
7
|
+
module Cure
|
8
|
+
module Validator
|
9
|
+
class Candidate
|
10
|
+
include Helpers::ObjectHelpers
|
11
|
+
include Log
|
12
|
+
|
13
|
+
# Named range that column exists in
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :named_range
|
16
|
+
|
17
|
+
# Lookup column name for CSV.
|
18
|
+
# @return [String]
|
19
|
+
attr_reader :column
|
20
|
+
|
21
|
+
# # What sort of data needs to be generated.
|
22
|
+
# # @return [Array<Cure::Validator::BaseRule>]
|
23
|
+
attr_reader :rules
|
24
|
+
|
25
|
+
DEFAULT_OPTIONS = {
|
26
|
+
fail_on_error: false
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def initialize(column, named_range, options={})
|
30
|
+
@column = column
|
31
|
+
@named_range = named_range || "_default"
|
32
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
33
|
+
@rules = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def perform(value)
|
37
|
+
result = @rules.filter_map do |rule|
|
38
|
+
rule.process(value) ? nil : "#{rule} failed -> [#{@column}][#{value}]"
|
39
|
+
end
|
40
|
+
|
41
|
+
raise "Validation failed:\n#{result.join("\n")}" if @options[:fail_on_error] && !result.empty?
|
42
|
+
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_rule(method_name, options={})
|
47
|
+
klass_name = "Cure::Validator::#{method_name.to_s.split("_").map(&:capitalize).join}Rule"
|
48
|
+
raise "#{method_name} is not valid" unless class_exists?(klass_name)
|
49
|
+
|
50
|
+
@rules << Kernel.const_get(klass_name).new(@named_range, @column, options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cure
|
4
|
+
module Export
|
5
|
+
class Manager
|
6
|
+
|
7
|
+
# @param [Array<Cure::Validator::BaseRule>] candidates
|
8
|
+
attr_reader :validators
|
9
|
+
|
10
|
+
def initialize(named_range, validators)
|
11
|
+
@named_range = named_range
|
12
|
+
@validators = validators
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Hash] row
|
16
|
+
def process_row(row)
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cure
|
4
|
+
module Validators
|
5
|
+
# Should be an array, we can have multiple validators for the same obj
|
6
|
+
@validators = {}
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :validators
|
10
|
+
|
11
|
+
# @param [String] prop
|
12
|
+
# @param [Object] options
|
13
|
+
def register_validator(caller, prop, options)
|
14
|
+
@validators[caller] = [] unless @validators.has_key? caller
|
15
|
+
@validators[caller] << {prop: "@#{prop}".to_sym, options:}
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [TrueClass, FalseClass]
|
19
|
+
def validate(zelf) # rubocop:disable Metrics/AbcSize
|
20
|
+
return true unless @validators.has_key? zelf.class
|
21
|
+
|
22
|
+
variables = instance_variables_hash(zelf)
|
23
|
+
@validators[zelf.class].all? do |k|
|
24
|
+
options = k[:options]
|
25
|
+
return true if options.empty? # No validator, no need to run.
|
26
|
+
|
27
|
+
validator_prop = options[:validator]
|
28
|
+
proc = case validator_prop
|
29
|
+
when Symbol
|
30
|
+
common_validators.fetch(validator_prop, proc { |_x| false })
|
31
|
+
# when Proc
|
32
|
+
# validator_prop
|
33
|
+
else
|
34
|
+
proc { |_x| false }
|
35
|
+
end
|
36
|
+
|
37
|
+
property = variables[k[:prop]]
|
38
|
+
proc.call(property)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [Object] zelf
|
43
|
+
# @return [Hash]
|
44
|
+
def instance_variables_hash(zelf)
|
45
|
+
zelf.instance_variables.each_with_object({}) do |attribute, hash|
|
46
|
+
hash[attribute] = zelf.instance_variable_get(attribute)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def common_validators
|
51
|
+
{
|
52
|
+
presence: proc { |current_val| !current_val.nil? }
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def validates(property, options={})
|
58
|
+
Validators.register_validator(self, property, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
module Helpers
|
62
|
+
def valid?(suppress_error: false)
|
63
|
+
status = Validators.validate(self)
|
64
|
+
return true if status
|
65
|
+
return false if suppress_error
|
66
|
+
|
67
|
+
raise "Object is invalid"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/cure/version.rb
CHANGED
data/lib/cure.rb
CHANGED
@@ -1,26 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "logger"
|
3
4
|
require "cure/log"
|
5
|
+
require "cure/launcher"
|
4
6
|
require "cure/config"
|
5
7
|
require "cure/version"
|
6
|
-
|
7
|
-
require "cure/
|
8
|
+
require "cure/dsl/template"
|
9
|
+
require "cure/strategy/imports"
|
10
|
+
require "cure/generator/imports"
|
8
11
|
require "cure/transformation/transform"
|
9
|
-
|
10
|
-
require "cure/main"
|
11
|
-
|
12
|
-
require "logger"
|
12
|
+
require "cure/helpers/file_helpers"
|
13
13
|
|
14
14
|
module Cure
|
15
15
|
class << self
|
16
16
|
attr_writer :logger
|
17
17
|
|
18
|
+
attr_reader :config
|
19
|
+
|
18
20
|
def logger
|
19
21
|
@logger ||= Logger.new($stdout).tap do |log|
|
20
22
|
log.progname = name
|
23
|
+
log.formatter = proc do |severity, _datetime, _progname, msg|
|
24
|
+
"[#{severity}] #{msg}\n"
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
29
|
+
def init(&block)
|
30
|
+
launcher = Cure::Launcher.new
|
31
|
+
launcher.with_config(&block)
|
32
|
+
end
|
24
33
|
|
34
|
+
def init_from_file(file_path)
|
35
|
+
launcher = Cure::Launcher.new
|
36
|
+
launcher.with_config_file(file_path)
|
37
|
+
end
|
25
38
|
end
|
26
39
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
sources do
|
4
|
+
csv :pathname, Pathname.new("spec/cure/e2e/input/simple_names.csv"), ref_name: "names"
|
5
|
+
csv :pathname, Pathname.new("spec/cure/e2e/input/simple_ages.csv"), ref_name: "ages"
|
6
|
+
end
|
7
|
+
|
8
|
+
extract do
|
9
|
+
named_range name: "section_1", at: "B2:G6"
|
10
|
+
named_range name: "section_2", at: "B18:G20", headers: "B2:G2"
|
11
|
+
|
12
|
+
variable name: "new_field", location: "A16"
|
13
|
+
variable name: "new_field_2", location: "B16"
|
14
|
+
end
|
15
|
+
|
16
|
+
query do
|
17
|
+
with named_range: "section_1", query: <<-SQL
|
18
|
+
SELECT * FROM section_1
|
19
|
+
SQL
|
20
|
+
|
21
|
+
with named_range: "section_2", query: <<-SQL
|
22
|
+
SELECT * FROM section_2
|
23
|
+
SQL
|
24
|
+
end
|
25
|
+
|
26
|
+
build do
|
27
|
+
candidate column: "new_column", named_range: "section_1" do
|
28
|
+
add options: {}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
transform do
|
33
|
+
candidate column: "new_column", named_range: "section_1" do
|
34
|
+
with_translation { replace("full").with("variable", name: "new_field") }
|
35
|
+
end
|
36
|
+
|
37
|
+
candidate column: "new_column", named_range: "section_2" do
|
38
|
+
with_translation { replace("full").with("variable", name: "new_field") }
|
39
|
+
end
|
40
|
+
|
41
|
+
placeholders({key: "value", key2: "value2"})
|
42
|
+
end
|
43
|
+
|
44
|
+
export do
|
45
|
+
terminal named_range: "section_1", title: "Exported", limit_rows: 5
|
46
|
+
csv named_range: "section_1", file: "/tmp/cure/section_1.csv"
|
47
|
+
csv named_range: "section_2", file: "/tmp/cure/section_2.csv"
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
metadata do
|
2
|
+
# ...
|
3
|
+
end
|
4
|
+
|
5
|
+
sources do
|
6
|
+
# ...
|
7
|
+
end
|
8
|
+
|
9
|
+
extract do
|
10
|
+
# ...
|
11
|
+
end
|
12
|
+
|
13
|
+
validate do
|
14
|
+
# ...
|
15
|
+
end
|
16
|
+
|
17
|
+
build do
|
18
|
+
# ...
|
19
|
+
end
|
20
|
+
|
21
|
+
query do
|
22
|
+
# ...
|
23
|
+
end
|
24
|
+
|
25
|
+
transform do
|
26
|
+
# ...
|
27
|
+
end
|
28
|
+
|
29
|
+
export do
|
30
|
+
# ...
|
31
|
+
end
|