cure 0.1.2 → 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 +13 -3
- data/.tool-versions +1 -0
- data/Dockerfile +1 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +25 -6
- data/README.md +61 -93
- 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 -41
- 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 +142 -18
- data/lib/cure/coordinator.rb +61 -25
- 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 +14 -3
- data/lib/cure/extract/extractor.rb +41 -84
- 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 +14 -4
- data/lib/cure/generator/case_generator.rb +10 -3
- data/lib/cure/generator/character_generator.rb +9 -3
- 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 +7 -1
- data/lib/cure/generator/guid_generator.rb +7 -2
- data/lib/cure/generator/hex_generator.rb +6 -1
- data/lib/cure/generator/imports.rb +4 -0
- data/lib/cure/generator/number_generator.rb +6 -1
- data/lib/cure/generator/placeholder_generator.rb +7 -1
- data/lib/cure/generator/proc_generator.rb +21 -0
- data/lib/cure/generator/redact_generator.rb +9 -3
- data/lib/cure/generator/static_generator.rb +21 -0
- data/lib/cure/generator/variable_generator.rb +11 -5
- data/lib/cure/helpers/file_helpers.rb +12 -2
- data/lib/cure/helpers/object_helpers.rb +5 -17
- 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 +7 -0
- data/lib/cure/planner.rb +136 -0
- data/lib/cure/strategy/append_strategy.rb +4 -0
- data/lib/cure/strategy/base_strategy.rb +19 -44
- data/lib/cure/strategy/contain_strategy.rb +51 -0
- data/lib/cure/strategy/end_with_strategy.rb +7 -1
- data/lib/cure/strategy/full_strategy.rb +4 -0
- data/lib/cure/strategy/history/history_cache.rb +82 -0
- data/lib/cure/strategy/imports.rb +2 -0
- data/lib/cure/strategy/match_strategy.rb +7 -2
- data/lib/cure/strategy/prepend_strategy.rb +28 -0
- data/lib/cure/strategy/regex_strategy.rb +7 -1
- data/lib/cure/strategy/split_strategy.rb +8 -3
- data/lib/cure/strategy/start_with_strategy.rb +7 -1
- data/lib/cure/transformation/candidate.rb +32 -35
- data/lib/cure/transformation/transform.rb +22 -56
- 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 +3 -3
- data/lib/cure/version.rb +1 -1
- data/lib/cure.rb +19 -11
- data/templates/dsl_example.rb +48 -0
- data/templates/empty_template.rb +31 -0
- metadata +132 -21
- data/lib/cure/export/exporter.rb +0 -74
- data/lib/cure/extract/builder.rb +0 -27
- data/lib/cure/main.rb +0 -72
- data/lib/cure/template/dispatch.rb +0 -30
- data/lib/cure/template/extraction.rb +0 -38
- data/lib/cure/template/template.rb +0 -28
- data/lib/cure/template/transformations.rb +0 -26
- data/templates/aws_cur_template.json +0 -145
- data/templates/example_template.json +0 -54
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is by Ivan Black - available here:
|
|
4
|
+
# https://stackoverflow.com/questions/1489183/how-can-i-use-ruby-to-colorize-the-text-output-to-a-terminal
|
|
5
|
+
|
|
6
|
+
class String
|
|
7
|
+
def black
|
|
8
|
+
"\e[30m#{self}\e[0m"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def red
|
|
12
|
+
"\e[31m#{self}\e[0m"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def green
|
|
16
|
+
"\e[32m#{self}\e[0m"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def brown
|
|
20
|
+
"\e[33m#{self}\e[0m"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def blue
|
|
24
|
+
"\e[34m#{self}\e[0m"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def magenta
|
|
28
|
+
"\e[35m#{self}\e[0m"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def cyan
|
|
32
|
+
"\e[36m#{self}\e[0m"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def gray
|
|
36
|
+
"\e[37m#{self}\e[0m"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def bold
|
|
40
|
+
"\e[1m#{self}\e[22m"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def italic
|
|
44
|
+
"\e[3m#{self}\e[23m"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def underline
|
|
48
|
+
"\e[4m#{self}\e[24m"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def blink
|
|
52
|
+
"\e[5m#{self}\e[25m"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cure/transformation/candidate"
|
|
4
|
+
require "cure/transformation/transform"
|
|
5
|
+
require "cure/coordinator"
|
|
6
|
+
require "cure/database"
|
|
7
|
+
require "cure/planner"
|
|
8
|
+
require "cure/config"
|
|
9
|
+
|
|
10
|
+
require "json"
|
|
11
|
+
|
|
12
|
+
module Cure
|
|
13
|
+
class Launcher
|
|
14
|
+
include Database
|
|
15
|
+
include Configuration
|
|
16
|
+
include Helpers::FileHelpers
|
|
17
|
+
|
|
18
|
+
# @return [Cure::Coordinator]
|
|
19
|
+
attr_accessor :coordinator
|
|
20
|
+
|
|
21
|
+
def initialize(coordinator: Coordinator.new, planner: Planner.new)
|
|
22
|
+
@coordinator = coordinator
|
|
23
|
+
@planner = planner
|
|
24
|
+
@validated = false
|
|
25
|
+
@csv_files = []
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# This will only support single file CSV processing, and is deprecated now
|
|
29
|
+
#
|
|
30
|
+
# @param [Symbol] type
|
|
31
|
+
# @param [Object] obj
|
|
32
|
+
# @param [TrueClass,FalseClass] print_query_plan
|
|
33
|
+
# @return [void]
|
|
34
|
+
# @deprecated
|
|
35
|
+
def process(type, obj, print_query_plan: true)
|
|
36
|
+
@csv_files << Cure::Configuration::CsvFileProxy.load_file(type, obj, "_default")
|
|
37
|
+
run_export(print_query_plan: print_query_plan)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run_export(print_query_plan: true)
|
|
41
|
+
setup
|
|
42
|
+
|
|
43
|
+
raise "Not initialized" unless @validated
|
|
44
|
+
|
|
45
|
+
query_plan if print_query_plan
|
|
46
|
+
@coordinator.process
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# -- Builder opts start
|
|
50
|
+
|
|
51
|
+
# @param [Symbol] type
|
|
52
|
+
# @param [Object] obj
|
|
53
|
+
# @return [Cure::Main]
|
|
54
|
+
def with_csv_file(type, obj, ref_name: nil)
|
|
55
|
+
if ref_name.nil?
|
|
56
|
+
ref_name = @csv_files.length.zero? ? "_default" : "_default_#{@csv_files.length}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@csv_files << Cure::Configuration::CsvFileProxy.load_file(type, obj, ref_name)
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Cure::Main]
|
|
64
|
+
def with_config(&block)
|
|
65
|
+
raise "No block given to config" unless block
|
|
66
|
+
|
|
67
|
+
dsl = Dsl::DslHandler.init(&block)
|
|
68
|
+
@template = dsl.generate
|
|
69
|
+
|
|
70
|
+
load_csv_from_config
|
|
71
|
+
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [Cure::Main]
|
|
76
|
+
def with_config_file(file_location)
|
|
77
|
+
contents = read_file(file_location.to_s)
|
|
78
|
+
|
|
79
|
+
dsl = Dsl::DslHandler.init_from_content(contents, "cure")
|
|
80
|
+
@template = dsl.generate
|
|
81
|
+
|
|
82
|
+
load_csv_from_config
|
|
83
|
+
|
|
84
|
+
self
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# -- Builder end
|
|
88
|
+
|
|
89
|
+
# @return [Cure::Main]
|
|
90
|
+
def setup
|
|
91
|
+
raise "CSV File(s) are required" if @csv_files.empty?
|
|
92
|
+
raise "Template is required" unless @template
|
|
93
|
+
|
|
94
|
+
config = create_config(@csv_files, @template)
|
|
95
|
+
register_config(config)
|
|
96
|
+
|
|
97
|
+
init_database
|
|
98
|
+
init_history
|
|
99
|
+
|
|
100
|
+
@validated = true
|
|
101
|
+
self
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @return [void]
|
|
105
|
+
def query_plan
|
|
106
|
+
raise "Not initialized" unless @validated
|
|
107
|
+
|
|
108
|
+
@planner.process
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def load_csv_from_config
|
|
114
|
+
return unless @template.source_files.has_candidates?
|
|
115
|
+
|
|
116
|
+
@template.source_files.candidates.each do |source|
|
|
117
|
+
with_csv_file(source.type, source.value, ref_name: source.ref_name)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def init_history
|
|
122
|
+
Cure::History::HistoryCache.instance.reset
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/cure/log.rb
CHANGED
|
@@ -4,6 +4,13 @@ require "cure"
|
|
|
4
4
|
|
|
5
5
|
module Cure
|
|
6
6
|
module Log
|
|
7
|
+
# @param [String] _message
|
|
8
|
+
def log_trace(_message)
|
|
9
|
+
# Ruby default logger doesnt have trace, so my version
|
|
10
|
+
# is to just uncomment it when you need it for now.
|
|
11
|
+
# Cure.logger.trace(message)
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
# @param [String] message
|
|
8
15
|
def log_debug(message)
|
|
9
16
|
Cure.logger.debug(message)
|
data/lib/cure/planner.rb
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cure/log"
|
|
4
|
+
require "cure/config"
|
|
5
|
+
require "cure/helpers/string"
|
|
6
|
+
|
|
7
|
+
require "artii"
|
|
8
|
+
|
|
9
|
+
module Cure
|
|
10
|
+
class Planner
|
|
11
|
+
include Configuration
|
|
12
|
+
include Log
|
|
13
|
+
|
|
14
|
+
def process
|
|
15
|
+
print_starter
|
|
16
|
+
print_extract_plan
|
|
17
|
+
print_build_plan
|
|
18
|
+
print_transformations_plan
|
|
19
|
+
print_ender
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def print_starter # rubocop:disable Metrics/AbcSize
|
|
23
|
+
a = Artii::Base.new({font: "isometric1"})
|
|
24
|
+
puts a.asciify("C u r e")
|
|
25
|
+
puts "\nIf you require assistance, please read:"
|
|
26
|
+
puts "https://github.com/williamthom-as/cure/tree/main/docs\n"
|
|
27
|
+
puts ""
|
|
28
|
+
log_info "_______________________________________________"
|
|
29
|
+
print_spacer
|
|
30
|
+
log_info "Cure Execution Plan".bold.underline
|
|
31
|
+
log_info ""
|
|
32
|
+
log_info "Source file location: #{config.source_files.map(&:description).join(",")}"
|
|
33
|
+
log_info "Template file descriptor below"
|
|
34
|
+
|
|
35
|
+
print_spacer
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def print_ender
|
|
39
|
+
log_info "_______________________________________________"
|
|
40
|
+
print_spacer
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def print_extract_plan # rubocop:disable Metrics/AbcSize
|
|
44
|
+
print_title "Extract"
|
|
45
|
+
named_ranges = config.template.extraction.named_ranges
|
|
46
|
+
variables = config.template.extraction.variables
|
|
47
|
+
|
|
48
|
+
if named_ranges.empty?
|
|
49
|
+
print_empty(named_ranges, "If you wanted to add a named range, please read docs/extraction.md")
|
|
50
|
+
else
|
|
51
|
+
log_info("[#{named_ranges.length}] named ranges specified")
|
|
52
|
+
named_ranges.each do |nr|
|
|
53
|
+
log_info "-- #{nr.name} will extract values from #{nr.section}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
print_spacer
|
|
58
|
+
|
|
59
|
+
if variables.empty?
|
|
60
|
+
print_empty("variables")
|
|
61
|
+
else
|
|
62
|
+
log_info("[#{variables.length}] variables specified")
|
|
63
|
+
variables.each do |v|
|
|
64
|
+
log_info "-- #{v.name} will extract from #{v.location}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
print_spacer
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def print_build_plan
|
|
72
|
+
print_title "Build"
|
|
73
|
+
candidates = config.template.builder.candidates
|
|
74
|
+
|
|
75
|
+
if candidates.empty?
|
|
76
|
+
print_empty("Build")
|
|
77
|
+
else
|
|
78
|
+
candidates.each do |c|
|
|
79
|
+
log_info "-- #{c.column || "Multiple columns>"} from #{c.named_range} will be changed with #{c.action}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
print_spacer
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def print_transformations_plan # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
|
|
87
|
+
print_title "Transforms"
|
|
88
|
+
candidates = config.template.transformations.candidates
|
|
89
|
+
placeholders = config.template.transformations.placeholders
|
|
90
|
+
|
|
91
|
+
if candidates.empty?
|
|
92
|
+
print_empty("Transforms")
|
|
93
|
+
else
|
|
94
|
+
candidates.each do |c|
|
|
95
|
+
log_info "-- #{c.column} from #{c.named_range} will be changed with #{c.translations.size} translation"
|
|
96
|
+
c.translations.each do |tr|
|
|
97
|
+
log_info "\t\t> #{"Replacement".bold} [#{tr.strategy.class}]: #{tr.strategy.describe}"
|
|
98
|
+
log_info "\t\t> #{"Generator".bold} [#{tr.generator.class}]: #{tr.generator.describe}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
print_spacer
|
|
104
|
+
|
|
105
|
+
if placeholders.nil? || placeholders.empty?
|
|
106
|
+
print_empty("Placeholders")
|
|
107
|
+
else
|
|
108
|
+
log_info "-- Variables"
|
|
109
|
+
placeholders.each do |k, v|
|
|
110
|
+
log_info "\t\t> #{k} => #{v}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
print_spacer
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
# @param [String] title
|
|
120
|
+
def print_title(title)
|
|
121
|
+
log_info title.bold.underline
|
|
122
|
+
print_spacer
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @param [String] descriptor
|
|
126
|
+
# @param [String,nil] remedy
|
|
127
|
+
def print_empty(descriptor, remedy=nil)
|
|
128
|
+
log_info "No #{descriptor} specified.".italic
|
|
129
|
+
log_info "[Remedy: #{remedy}]" unless remedy.nil?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def print_spacer
|
|
133
|
+
log_info ""
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -2,47 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require "singleton"
|
|
4
4
|
require "cure/validators"
|
|
5
|
+
require "cure/strategy/history/history_cache"
|
|
5
6
|
|
|
6
7
|
module Cure
|
|
7
8
|
module Strategy
|
|
8
|
-
# Singleton Strategy for storing data across all processes
|
|
9
|
-
module History
|
|
10
|
-
# @return [Hash]
|
|
11
|
-
def history
|
|
12
|
-
HistoryCache.instance.history_cache
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# @return [String]
|
|
16
|
-
def retrieve_history(source_value)
|
|
17
|
-
history[source_value] unless source_value.nil? || source_value == ""
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# @param [String] source_value
|
|
21
|
-
# @param [String] value
|
|
22
|
-
def store_history(source_value, value)
|
|
23
|
-
history[source_value] = value unless source_value.nil? || source_value == ""
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def reset_history
|
|
27
|
-
HistoryCache.instance.reset
|
|
28
|
-
end
|
|
29
|
-
alias clear_history reset_history
|
|
30
|
-
|
|
31
|
-
class HistoryCache
|
|
32
|
-
include Singleton
|
|
33
|
-
|
|
34
|
-
attr_reader :history_cache
|
|
35
|
-
|
|
36
|
-
def initialize
|
|
37
|
-
@history_cache = {}
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def reset
|
|
41
|
-
@history_cache = {}
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
9
|
class BaseStrategy
|
|
47
10
|
include History
|
|
48
11
|
|
|
@@ -57,17 +20,18 @@ module Cure
|
|
|
57
20
|
end
|
|
58
21
|
|
|
59
22
|
# @param [String] source_value
|
|
23
|
+
# @param [Transformation::RowCtx,nil] row_ctx
|
|
60
24
|
# @param [Generator::BaseGenerator] generator
|
|
61
25
|
# @return [String]
|
|
62
26
|
#
|
|
63
27
|
# This will retrieve the (partial) value, then generate a new replacement.
|
|
64
|
-
def extract(source_value, generator)
|
|
28
|
+
def extract(source_value, row_ctx, generator)
|
|
65
29
|
extracted_value = _retrieve_value(source_value)
|
|
66
|
-
|
|
67
30
|
existing = retrieve_history(extracted_value)
|
|
68
|
-
return _replace_value(source_value, existing) if existing
|
|
69
31
|
|
|
70
|
-
|
|
32
|
+
return _replace_value(source_value, existing) if existing && !@params.force_replace
|
|
33
|
+
|
|
34
|
+
generated_value = generator.generate(source_value, row_ctx)&.to_s
|
|
71
35
|
value = _replace_value(source_value, generated_value)
|
|
72
36
|
|
|
73
37
|
store_history(extracted_value, generated_value)
|
|
@@ -75,6 +39,10 @@ module Cure
|
|
|
75
39
|
value
|
|
76
40
|
end
|
|
77
41
|
|
|
42
|
+
def describe
|
|
43
|
+
_describe
|
|
44
|
+
end
|
|
45
|
+
|
|
78
46
|
private
|
|
79
47
|
|
|
80
48
|
def replace_partial_record
|
|
@@ -99,6 +67,10 @@ module Cure
|
|
|
99
67
|
def _replace_value(_source_value, _generated_value)
|
|
100
68
|
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
|
101
69
|
end
|
|
70
|
+
|
|
71
|
+
def _describe
|
|
72
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
|
73
|
+
end
|
|
102
74
|
end
|
|
103
75
|
|
|
104
76
|
class BaseStrategyParams
|
|
@@ -108,11 +80,14 @@ module Cure
|
|
|
108
80
|
# Additional details needed to make substitution.
|
|
109
81
|
# @return [Hash]
|
|
110
82
|
attr_accessor :options
|
|
111
|
-
attr_accessor :replace_partial
|
|
83
|
+
attr_accessor :replace_partial, :force_replace
|
|
112
84
|
|
|
113
85
|
def initialize(options={})
|
|
114
|
-
@replace_partial = options[
|
|
86
|
+
@replace_partial = options[:replace_partial] || false
|
|
87
|
+
@force_replace = options[:force_replace] || false
|
|
115
88
|
@options = options
|
|
89
|
+
|
|
90
|
+
validate_params
|
|
116
91
|
end
|
|
117
92
|
|
|
118
93
|
def validate_params
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cure/strategy/base_strategy"
|
|
4
|
+
|
|
5
|
+
module Cure
|
|
6
|
+
module Strategy
|
|
7
|
+
class ContainStrategy < BaseStrategy
|
|
8
|
+
# Additional details needed to make substitution.
|
|
9
|
+
# @return [ContainStrategyParams]
|
|
10
|
+
attr_accessor :params
|
|
11
|
+
|
|
12
|
+
def initialize(options)
|
|
13
|
+
super(ContainStrategyParams.new(options))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param [String] source_value
|
|
17
|
+
def _retrieve_value(source_value)
|
|
18
|
+
return unless source_value.include?(@params.match)
|
|
19
|
+
|
|
20
|
+
@params.match
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param [String] source_value
|
|
24
|
+
# @param [String] generated_value
|
|
25
|
+
# @return [String]
|
|
26
|
+
def _replace_value(source_value, generated_value)
|
|
27
|
+
return unless source_value.include?(@params.match)
|
|
28
|
+
|
|
29
|
+
source_value.gsub(@params.match, generated_value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def _describe
|
|
33
|
+
"Replacing matched value on '#{@params.match}') " \
|
|
34
|
+
"[Note: If the value does not include '#{@params.match}', no substitution is made.]"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class ContainStrategyParams < BaseStrategyParams
|
|
39
|
+
attr_reader :match
|
|
40
|
+
|
|
41
|
+
validates :match, validator: :presence
|
|
42
|
+
|
|
43
|
+
def initialize(options=nil)
|
|
44
|
+
@match = options[:match]
|
|
45
|
+
# valid?
|
|
46
|
+
|
|
47
|
+
super(options)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -30,6 +30,12 @@ module Cure
|
|
|
30
30
|
generated_value + @params.match
|
|
31
31
|
# generated_value + source_value.reverse.chomp(@options["match"].reverse).reverse
|
|
32
32
|
end
|
|
33
|
+
|
|
34
|
+
def _describe
|
|
35
|
+
"End with replacement will look for '#{@params.match}'. " \
|
|
36
|
+
"It will do a #{replace_partial_record ? "partial" : "full"} replacement. " \
|
|
37
|
+
"[Note: If the value does not include '#{@params.match}', no substitution is made.]"
|
|
38
|
+
end
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
class EndWithStrategyParams < BaseStrategyParams
|
|
@@ -38,7 +44,7 @@ module Cure
|
|
|
38
44
|
validates :match
|
|
39
45
|
|
|
40
46
|
def initialize(options=nil)
|
|
41
|
-
@match = options[
|
|
47
|
+
@match = options[:match]
|
|
42
48
|
super(options)
|
|
43
49
|
end
|
|
44
50
|
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cure/database"
|
|
4
|
+
|
|
5
|
+
# Singleton Strategy for storing data across all processes
|
|
6
|
+
|
|
7
|
+
module Cure
|
|
8
|
+
module History
|
|
9
|
+
|
|
10
|
+
# @return [HistoryCache]
|
|
11
|
+
def history
|
|
12
|
+
HistoryCache.instance
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [String]
|
|
16
|
+
def retrieve_history(source_value)
|
|
17
|
+
history.search(source_value) unless source_value.nil? || source_value == ""
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param [String] source_value
|
|
21
|
+
# @param [String] value
|
|
22
|
+
def store_history(source_value, value)
|
|
23
|
+
unless source_value.nil? || source_value == ""
|
|
24
|
+
history.insert(source_value, value)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reset_history
|
|
29
|
+
history.reset
|
|
30
|
+
end
|
|
31
|
+
alias clear_history reset_history
|
|
32
|
+
|
|
33
|
+
class HistoryCache
|
|
34
|
+
include Database
|
|
35
|
+
include Singleton
|
|
36
|
+
|
|
37
|
+
attr_accessor :count
|
|
38
|
+
|
|
39
|
+
def initialize
|
|
40
|
+
@count = 0
|
|
41
|
+
|
|
42
|
+
init_cache
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [String]
|
|
46
|
+
def search(source_value, _named_range: nil, _column: nil)
|
|
47
|
+
database_service.find_translation(source_value)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def insert(source_value, value, named_range: nil, column: nil)
|
|
51
|
+
@count += 1
|
|
52
|
+
|
|
53
|
+
database_service.insert_row(:translations, [
|
|
54
|
+
@count, source_value, value, named_range, column
|
|
55
|
+
])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def all_values
|
|
59
|
+
database_service.all_translations
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def reset
|
|
63
|
+
@count = 0
|
|
64
|
+
if database_service.table_exist?(:translations)
|
|
65
|
+
database_service.truncate_table(:translations)
|
|
66
|
+
else
|
|
67
|
+
init_cache
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def table_count
|
|
72
|
+
database_service.table_count(:translations)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def init_cache
|
|
76
|
+
return if database_service.table_exist?(:translations)
|
|
77
|
+
|
|
78
|
+
database_service.create_table(:translations, %w[source_value value named_range column])
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -23,10 +23,15 @@ module Cure
|
|
|
23
23
|
# @param [String] generated_value
|
|
24
24
|
# @return [String]
|
|
25
25
|
def _replace_value(source_value, generated_value)
|
|
26
|
-
return unless source_value.include? @params.match
|
|
26
|
+
return source_value unless source_value.include? @params.match
|
|
27
27
|
|
|
28
28
|
source_value.gsub(@params.match, generated_value)
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
def _describe
|
|
32
|
+
"Match replacement will look for the presence of '#{@params.match}', and replace that value. " \
|
|
33
|
+
"[Note: If the value does not include '#{@params.match}', no substitution is made.]"
|
|
34
|
+
end
|
|
30
35
|
end
|
|
31
36
|
|
|
32
37
|
class MatchStrategyParams < BaseStrategyParams
|
|
@@ -35,7 +40,7 @@ module Cure
|
|
|
35
40
|
validates :match
|
|
36
41
|
|
|
37
42
|
def initialize(options=nil)
|
|
38
|
-
@match = options[
|
|
43
|
+
@match = options[:match]
|
|
39
44
|
super(options)
|
|
40
45
|
end
|
|
41
46
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cure/strategy/base_strategy"
|
|
4
|
+
|
|
5
|
+
module Cure
|
|
6
|
+
module Strategy
|
|
7
|
+
class PrependStrategy < BaseStrategy
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# @param [String] source_value
|
|
11
|
+
# @return [String]
|
|
12
|
+
def _retrieve_value(source_value)
|
|
13
|
+
source_value
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param [String] source_value
|
|
17
|
+
# @param [String] generated_value
|
|
18
|
+
# @return [String]
|
|
19
|
+
def _replace_value(source_value, generated_value)
|
|
20
|
+
generated_value + source_value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def _describe
|
|
24
|
+
"Prepend generated value to the end of source value"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -33,6 +33,12 @@ module Cure
|
|
|
33
33
|
|
|
34
34
|
source_value.gsub(m[1], generated_value)
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
def _describe
|
|
38
|
+
"Matching on '#{@params.regex_cg}'. " \
|
|
39
|
+
"[Note: If the regex does not match, or has no capture group, no substitution is made.]"
|
|
40
|
+
end
|
|
41
|
+
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
class RegexStrategyParams < BaseStrategyParams
|
|
@@ -41,7 +47,7 @@ module Cure
|
|
|
41
47
|
validates :regex_cg
|
|
42
48
|
|
|
43
49
|
def initialize(options=nil)
|
|
44
|
-
@regex_cg = options[
|
|
50
|
+
@regex_cg = options[:regex_cg]
|
|
45
51
|
super(options)
|
|
46
52
|
end
|
|
47
53
|
end
|