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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -3
  3. data/.tool-versions +1 -0
  4. data/Dockerfile +1 -1
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +25 -6
  7. data/README.md +61 -93
  8. data/docs/README.md +33 -0
  9. data/docs/about.md +219 -0
  10. data/docs/builder/add.md +52 -0
  11. data/docs/builder/black_white_list.md +83 -0
  12. data/docs/builder/copy.md +48 -0
  13. data/docs/builder/explode.md +70 -0
  14. data/docs/builder/main.md +43 -0
  15. data/docs/builder/remove.md +46 -0
  16. data/docs/examples/examples.md +164 -0
  17. data/docs/export/main.md +37 -0
  18. data/docs/extract/main.md +89 -0
  19. data/docs/metadata/main.md +29 -0
  20. data/docs/query/main.md +45 -0
  21. data/docs/sources/main.md +36 -0
  22. data/docs/transform/main.md +53 -0
  23. data/docs/validate/main.md +42 -0
  24. data/exe/cure +12 -41
  25. data/exe/cure.old +59 -0
  26. data/lib/cure/builder/base_builder.rb +151 -0
  27. data/lib/cure/builder/candidate.rb +56 -0
  28. data/lib/cure/cli/command.rb +105 -0
  29. data/lib/cure/cli/generate_command.rb +54 -0
  30. data/lib/cure/cli/new_command.rb +52 -0
  31. data/lib/cure/cli/run_command.rb +19 -0
  32. data/lib/cure/cli/templates/README.md.erb +1 -0
  33. data/lib/cure/cli/templates/gemfile.erb +5 -0
  34. data/lib/cure/cli/templates/gitignore.erb +181 -0
  35. data/lib/cure/cli/templates/new_template.rb.erb +31 -0
  36. data/lib/cure/cli/templates/tool-versions.erb +1 -0
  37. data/lib/cure/config.rb +142 -18
  38. data/lib/cure/coordinator.rb +61 -25
  39. data/lib/cure/database.rb +191 -0
  40. data/lib/cure/dsl/builder.rb +26 -0
  41. data/lib/cure/dsl/exporters.rb +45 -0
  42. data/lib/cure/dsl/extraction.rb +60 -0
  43. data/lib/cure/dsl/metadata.rb +33 -0
  44. data/lib/cure/dsl/queries.rb +36 -0
  45. data/lib/cure/dsl/source_files.rb +36 -0
  46. data/lib/cure/dsl/template.rb +131 -0
  47. data/lib/cure/dsl/transformations.rb +95 -0
  48. data/lib/cure/dsl/validator.rb +22 -0
  49. data/lib/cure/export/base_processor.rb +194 -0
  50. data/lib/cure/export/manager.rb +24 -0
  51. data/lib/cure/extract/base_processor.rb +47 -0
  52. data/lib/cure/extract/csv_lookup.rb +14 -3
  53. data/lib/cure/extract/extractor.rb +41 -84
  54. data/lib/cure/extract/filter.rb +118 -0
  55. data/lib/cure/extract/named_range.rb +94 -0
  56. data/lib/cure/extract/named_range_processor.rb +128 -0
  57. data/lib/cure/extract/variable.rb +25 -0
  58. data/lib/cure/extract/variable_processor.rb +57 -0
  59. data/lib/cure/generator/base_generator.rb +14 -4
  60. data/lib/cure/generator/case_generator.rb +10 -3
  61. data/lib/cure/generator/character_generator.rb +9 -3
  62. data/lib/cure/generator/erb_generator.rb +21 -0
  63. data/lib/cure/generator/eval_generator.rb +34 -0
  64. data/lib/cure/generator/faker_generator.rb +7 -1
  65. data/lib/cure/generator/guid_generator.rb +7 -2
  66. data/lib/cure/generator/hex_generator.rb +6 -1
  67. data/lib/cure/generator/imports.rb +4 -0
  68. data/lib/cure/generator/number_generator.rb +6 -1
  69. data/lib/cure/generator/placeholder_generator.rb +7 -1
  70. data/lib/cure/generator/proc_generator.rb +21 -0
  71. data/lib/cure/generator/redact_generator.rb +9 -3
  72. data/lib/cure/generator/static_generator.rb +21 -0
  73. data/lib/cure/generator/variable_generator.rb +11 -5
  74. data/lib/cure/helpers/file_helpers.rb +12 -2
  75. data/lib/cure/helpers/object_helpers.rb +5 -17
  76. data/lib/cure/helpers/perf_helpers.rb +30 -0
  77. data/lib/cure/helpers/string.rb +54 -0
  78. data/lib/cure/launcher.rb +125 -0
  79. data/lib/cure/log.rb +7 -0
  80. data/lib/cure/planner.rb +136 -0
  81. data/lib/cure/strategy/append_strategy.rb +4 -0
  82. data/lib/cure/strategy/base_strategy.rb +19 -44
  83. data/lib/cure/strategy/contain_strategy.rb +51 -0
  84. data/lib/cure/strategy/end_with_strategy.rb +7 -1
  85. data/lib/cure/strategy/full_strategy.rb +4 -0
  86. data/lib/cure/strategy/history/history_cache.rb +82 -0
  87. data/lib/cure/strategy/imports.rb +2 -0
  88. data/lib/cure/strategy/match_strategy.rb +7 -2
  89. data/lib/cure/strategy/prepend_strategy.rb +28 -0
  90. data/lib/cure/strategy/regex_strategy.rb +7 -1
  91. data/lib/cure/strategy/split_strategy.rb +8 -3
  92. data/lib/cure/strategy/start_with_strategy.rb +7 -1
  93. data/lib/cure/transformation/candidate.rb +32 -35
  94. data/lib/cure/transformation/transform.rb +22 -56
  95. data/lib/cure/validator/base_rule.rb +78 -0
  96. data/lib/cure/validator/candidate.rb +54 -0
  97. data/lib/cure/validator/manager.rb +21 -0
  98. data/lib/cure/validators.rb +3 -3
  99. data/lib/cure/version.rb +1 -1
  100. data/lib/cure.rb +19 -11
  101. data/templates/dsl_example.rb +48 -0
  102. data/templates/empty_template.rb +31 -0
  103. metadata +132 -21
  104. data/lib/cure/export/exporter.rb +0 -74
  105. data/lib/cure/extract/builder.rb +0 -27
  106. data/lib/cure/main.rb +0 -72
  107. data/lib/cure/template/dispatch.rb +0 -30
  108. data/lib/cure/template/extraction.rb +0 -38
  109. data/lib/cure/template/template.rb +0 -28
  110. data/lib/cure/template/transformations.rb +0 -26
  111. data/templates/aws_cur_template.json +0 -145
  112. data/templates/example_template.json +0 -54
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sequel"
4
+ require "sqlite3"
5
+ require "singleton"
6
+
7
+ require "cure/config"
8
+
9
+ module Cure
10
+ module Database
11
+ def database_service
12
+ database = DatabaseSource.instance.database_service
13
+ return database if database
14
+
15
+ init_database
16
+
17
+ DatabaseSource.instance.database_service
18
+ end
19
+
20
+ def init_database
21
+ DatabaseSource.instance.init_database
22
+ end
23
+ end
24
+
25
+ class DatabaseSource
26
+ include Singleton
27
+
28
+ attr_reader :database_service
29
+
30
+ def init_database
31
+ @database_service = DatabaseService.new
32
+ end
33
+ end
34
+
35
+ class DatabaseService
36
+ include Cure::Configuration
37
+
38
+ # @return [Sequel::SQLite::Database]
39
+ attr_reader :database
40
+
41
+ def initialize
42
+ @database = init_database
43
+ setup_db
44
+ end
45
+
46
+ def setup_db
47
+ # Load this from config defined by user?
48
+ @database.execute <<-SQL
49
+ PRAGMA journal_mode = OFF;
50
+ PRAGMA synchronous = 0;
51
+ PRAGMA cache_size = 1000000;
52
+ PRAGMA locking_mode = EXCLUSIVE;
53
+ PRAGMA temp_store = MEMORY;
54
+ SQL
55
+ end
56
+
57
+ # App Service calls
58
+ def find_variable(property_name)
59
+ @database.from(:variables).where(name: property_name).get(:value)
60
+ end
61
+
62
+ def find_translation(source_value)
63
+ @database.from(:translations).where(source_value: source_value).get(:value)
64
+ end
65
+
66
+ def all_translations
67
+ @database.from(:translations).all
68
+ end
69
+
70
+ # @param [Symbol,String] tbl_name
71
+ # @param [Array] columns
72
+ def create_table(tbl_name, columns, auto_increment: true)
73
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
74
+
75
+ @database.create_table tbl_name do
76
+ primary_key :_id, auto_increment: auto_increment
77
+ columns.each do |col_name|
78
+ column col_name.to_sym, String
79
+ end
80
+ end
81
+ end
82
+
83
+ # @param [Symbol,String] tbl_name
84
+ def truncate_table(tbl_name)
85
+ @database[tbl_name.to_sym].truncate
86
+ end
87
+
88
+ # @param [Symbol,String] tbl_name
89
+ def table_count(tbl_name)
90
+ @database[tbl_name.to_sym].count
91
+ end
92
+
93
+ def with_transaction(&block)
94
+ @database.transaction({}, &block)
95
+ end
96
+
97
+ # @param [Symbol,String] tbl_name
98
+ # @return [TrueClass, FalseClass]
99
+ def table_exist?(tbl_name)
100
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
101
+
102
+ @database.table_exists?(tbl_name)
103
+ end
104
+ alias table_exists? table_exist?
105
+
106
+ # @param [Symbol,String] tbl_name
107
+ # @param [Array<String>] row
108
+ def insert_row(tbl_name, row)
109
+ @database[tbl_name.to_sym].insert(row)
110
+ end
111
+
112
+ # @param [Symbol,String] tbl_name
113
+ # @param [Array<String>] rows
114
+ def insert_batched_rows(tbl_name, rows)
115
+ @database[tbl_name.to_sym].import(@database[tbl_name.to_sym].columns, rows)
116
+ end
117
+
118
+ def add_column(tbl_name, new_column, default: "")
119
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
120
+ new_column = new_column.to_sym if new_column.class != Symbol
121
+
122
+ @database.add_column(tbl_name, new_column, String, default: default)
123
+ end
124
+
125
+ def remove_column(tbl_name, remove_column)
126
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
127
+ remove_column = remove_column.to_sym if remove_column.class != Symbol
128
+
129
+ @database.drop_column tbl_name, remove_column
130
+ end
131
+
132
+ def list_columns(tbl_name)
133
+ @database[tbl_name.to_sym].columns
134
+ end
135
+
136
+ def rename_column(tbl_name, old_column, new_column)
137
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
138
+ old_column = old_column.to_sym if old_column.class != Symbol
139
+ new_column = new_column.to_sym if new_column.class != Symbol
140
+
141
+ @database.rename_column tbl_name, old_column, new_column
142
+ end
143
+
144
+ def copy_column(tbl_name, from_column, to_column)
145
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
146
+ from_column = from_column.to_sym if from_column.class != Symbol
147
+ to_column = to_column.to_sym if to_column.class != Symbol
148
+
149
+ add_column tbl_name, to_column
150
+ run("UPDATE #{tbl_name} SET #{to_column} = #{from_column}")
151
+ end
152
+
153
+ def run(query, opts={})
154
+ @database.run(query, opts)
155
+ end
156
+
157
+ # Can we decouple query from named range? Probably more difficult
158
+ # than it seems. But would be nice to create two queries that doesn't
159
+ # require two tables (named ranges).
160
+ def with_paged_result(tbl_name, chunk_size: 100, &block)
161
+ raise "No block given" unless block
162
+
163
+ query = config.template.queries.find(tbl_name)
164
+ if query
165
+ @database[query.query].order(:_id).paged_each(rows_per_fetch: chunk_size, &block)
166
+ else
167
+ @database[tbl_name.to_sym].order(:_id).paged_each(rows_per_fetch: chunk_size, &block)
168
+ end
169
+ end
170
+
171
+ def list_tables
172
+ tbl_arr = @database.tables
173
+ tbl_arr.delete(:variables)
174
+
175
+ if tbl_arr.include?(:translations)
176
+ tbl_arr.delete(:translations)
177
+ tbl_arr.push(:translations)
178
+ end
179
+
180
+ tbl_arr
181
+ end
182
+
183
+ private
184
+
185
+ def init_database
186
+ # Build from config
187
+ # This must clean the database if its not in memory
188
+ Sequel.connect("sqlite:/")
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/builder/base_builder"
4
+ require "cure/builder/candidate"
5
+
6
+ module Cure
7
+ module Dsl
8
+ class Builder
9
+
10
+ attr_reader :candidates
11
+
12
+ def initialize
13
+ @candidates = []
14
+ end
15
+
16
+ # @param [String,nil] column
17
+ # @param [String] named_range
18
+ # @param [Proc] block
19
+ def candidate(column: nil, named_range: "_default", &block)
20
+ candidate = Cure::Builder::Candidate.new(column, named_range)
21
+ @candidates << candidate
22
+ candidate.instance_exec(&block)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/export/base_processor"
4
+ require "cure/extract/named_range"
5
+
6
+ module Cure
7
+ module Dsl
8
+ class Exporters
9
+
10
+ attr_reader :processors
11
+
12
+ def initialize
13
+ @processors = []
14
+ end
15
+
16
+ # @param [String,Symbol] _method_name
17
+ # @param [TrueClass,FalseClass] _include_private
18
+ # @return [TrueClass]
19
+ def respond_to_missing?(_method_name, _include_private=false)
20
+ true
21
+ end
22
+
23
+ # @param [Symbol] method_name
24
+ # @return [Cure::Export::BaseBuilder]
25
+ def method_missing(method_name, **args)
26
+ klass_name = "Cure::Export::#{method_name.to_s.split("_").map(&:capitalize).join("")}Processor"
27
+ raise "#{method_name} is not valid" unless class_exists?(klass_name)
28
+
29
+ @processors << Kernel.const_get(klass_name).new(
30
+ args[:named_range] || "_default",
31
+ args || {}
32
+ )
33
+ end
34
+
35
+ # @param [String] klass_name
36
+ # @return [TrueClass,FalseClass]
37
+ def class_exists?(klass_name)
38
+ klass = Module.const_get(klass_name)
39
+ klass.is_a?(Class)
40
+ rescue NameError
41
+ false
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/extract/named_range"
4
+ require "cure/extract/variable"
5
+
6
+ module Cure
7
+ module Dsl
8
+ class Extraction
9
+
10
+ attr_reader :named_ranges, :variables, :sample_rows
11
+
12
+ def initialize
13
+ @named_ranges = []
14
+ @variables = []
15
+ end
16
+
17
+ def named_range(name:, at: -1, headers: nil, ref_name: nil, placeholder: false, &block)
18
+ named_range = Extract::NamedRange.new(name, at,
19
+ headers: headers,
20
+ ref_name: ref_name,
21
+ placeholder: placeholder
22
+ )
23
+
24
+ if block_given?
25
+ named_range.filter.instance_eval(&block)
26
+ end
27
+
28
+ @named_ranges << named_range
29
+ end
30
+
31
+ def variable(name:, at:, ref_name: nil)
32
+ @variables << Cure::Extract::Variable.new(name, at, ref_name: ref_name)
33
+ end
34
+
35
+ def sample(rows: nil)
36
+ @sample_rows = rows
37
+ end
38
+
39
+ # @param [String] ref_name
40
+ # @return [Array]
41
+ def required_named_ranges(ref_name: "_default")
42
+ # This now needs to take support multiple files. We don't want named ranges
43
+ # for different files
44
+ return @named_ranges if ref_name == "default"
45
+
46
+ @named_ranges.select { |nr| nr.ref_name == ref_name && nr.placeholder == false }
47
+ end
48
+
49
+ # @param [String] ref_name
50
+ # @return [Array]
51
+ def required_variables(ref_name: "_default")
52
+ # This now needs to take support multiple files. We don't want named ranges
53
+ # for different files
54
+ return @variables if ref_name == "_default"
55
+
56
+ @variables.select { |v| v.ref_name == ref_name }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cure
4
+ module Dsl
5
+ class Metadata
6
+
7
+ attr_reader :_name, :_version, :_comments, :_additional
8
+
9
+ def initialize
10
+ @_name = nil
11
+ @_version = nil
12
+ @_comments = nil
13
+ @_additional = {}
14
+ end
15
+
16
+ def name(name)
17
+ @_name = name
18
+ end
19
+
20
+ def version(version)
21
+ @_version = version
22
+ end
23
+
24
+ def comments(comments)
25
+ @_comments = comments
26
+ end
27
+
28
+ def additional(data: {})
29
+ @_additional = data
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/builder/base_builder"
4
+ require "cure/builder/candidate"
5
+
6
+ module Cure
7
+ module Dsl
8
+ class Queries
9
+
10
+ attr_reader :candidates
11
+
12
+ def initialize
13
+ @candidates = []
14
+ end
15
+
16
+ def with(query:, named_range: "_default")
17
+ candidate = Query.new(named_range.to_sym, query)
18
+ @candidates << candidate
19
+ end
20
+
21
+ def find(named_range)
22
+ @candidates.find { |candidate| candidate.named_range.to_sym == named_range.to_sym }
23
+ end
24
+
25
+ class Query
26
+ attr_reader :named_range, :query
27
+
28
+ def initialize(named_range, query)
29
+ @named_range = named_range
30
+ @query = query
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/builder/base_builder"
4
+ require "cure/builder/candidate"
5
+
6
+ module Cure
7
+ module Dsl
8
+ class SourceFiles
9
+
10
+ attr_reader :candidates
11
+
12
+ def initialize
13
+ @candidates = []
14
+ end
15
+
16
+ def csv(type, value, ref_name: nil)
17
+ candidate = SourceFile.new(type, value, ref_name)
18
+ @candidates << candidate
19
+ end
20
+
21
+ def has_candidates?
22
+ @candidates.length.positive?
23
+ end
24
+
25
+ class SourceFile
26
+ attr_accessor :type, :value, :ref_name
27
+
28
+ def initialize(type, value, ref_name)
29
+ @type = type
30
+ @value = value
31
+ @ref_name = ref_name
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/log"
4
+ require "cure/dsl/extraction"
5
+ require "cure/dsl/builder"
6
+ require "cure/dsl/validator"
7
+ require "cure/dsl/transformations"
8
+ require "cure/dsl/exporters"
9
+ require "cure/dsl/queries"
10
+ require "cure/dsl/metadata"
11
+ require "cure/dsl/source_files"
12
+
13
+ module Cure
14
+ module Dsl
15
+ class DslHandler
16
+ include Log
17
+ def self.init(&block)
18
+ DslHandler.new(block)
19
+ end
20
+
21
+ def self.init_from_content(dsl_source, identifier, line_number=1)
22
+ proc = Binding.get.eval(<<-SOURCE, identifier, line_number)
23
+ Proc.new do
24
+ #{dsl_source}
25
+ end
26
+ SOURCE
27
+
28
+ DslHandler.new(proc)
29
+ end
30
+
31
+ def initialize(proc)
32
+ @proc = proc
33
+ end
34
+
35
+ def generate(instance_variables={})
36
+ dsl = Template.new
37
+ instance_variables.each do |name, value|
38
+ dsl.instance_variable_set("@#{name}", value)
39
+ end
40
+
41
+ dsl.instance_eval(&@proc)
42
+ dsl
43
+
44
+ rescue StandardError => e
45
+ log_error "Error parsing DSL: #{e.message}"
46
+
47
+ # Raise specific error in future.
48
+ raise e
49
+ end
50
+ end
51
+
52
+ module Binding
53
+ # @return [Object] Empty object for binding purposes
54
+ def self.get
55
+ binding
56
+ end
57
+ end
58
+
59
+ class Template
60
+
61
+ # @return [Dsl::Extraction]
62
+ attr_reader :extraction
63
+
64
+ # @return [Dsl::Builder]
65
+ attr_reader :builder
66
+
67
+ # @return [Dsl::Validator]
68
+ attr_reader :validator
69
+
70
+ # @return [Dsl::Transformations]
71
+ attr_reader :transformations
72
+
73
+ # @return [Dsl::Exporters]
74
+ attr_reader :exporters
75
+
76
+ # @return [Dsl::Queries]
77
+ attr_reader :queries
78
+
79
+ # @return [Dsl::Metadata]
80
+ attr_reader :meta_data
81
+
82
+ # @return [Dsl::SourceFiles]
83
+ attr_reader :source_files
84
+
85
+ def initialize
86
+ @source_files = SourceFiles.new
87
+ @extraction = Extraction.new
88
+ @builder = Builder.new
89
+ @validator = Validator.new
90
+ @transformations = Transformations.new
91
+ @exporters = Exporters.new
92
+ @queries = Queries.new
93
+ @meta_data = Metadata.new
94
+ end
95
+
96
+ private
97
+
98
+ def sources(&block)
99
+ @source_files.instance_exec(&block)
100
+ end
101
+
102
+ def extract(&block)
103
+ @extraction.instance_exec(&block)
104
+ end
105
+
106
+ def query(&block)
107
+ @queries.instance_exec(&block)
108
+ end
109
+
110
+ def build(&block)
111
+ @builder.instance_exec(&block)
112
+ end
113
+
114
+ def validate(&block)
115
+ @validator.instance_exec(&block)
116
+ end
117
+
118
+ def transform(&block)
119
+ @transformations.instance_exec(&block)
120
+ end
121
+
122
+ def metadata(&block)
123
+ @meta_data.instance_exec(&block)
124
+ end
125
+
126
+ def export(&block)
127
+ @exporters.instance_exec(&block)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/strategy/imports"
4
+ require "cure/generator/imports"
5
+ require "cure/transformation/candidate"
6
+
7
+ module Cure
8
+ module Dsl
9
+ class Transformations
10
+
11
+ attr_reader :candidates, :placeholders
12
+
13
+ def initialize
14
+ @candidates = []
15
+ @placeholders = []
16
+ end
17
+
18
+ def candidate(column:, named_range: "_default", options: {}, &block)
19
+ candidate = Candidate.new(column, named_range: named_range)
20
+ candidate.instance_exec(&block)
21
+
22
+ @candidates << Transformation::Candidate
23
+ .new(candidate.column, named_range: candidate.named_range, options: options)
24
+ .with_translations(candidate.translations)
25
+ .with_no_match_translation(candidate.no_match_translation)
26
+ end
27
+
28
+ def place_holders(hash)
29
+ @placeholders = hash
30
+ end
31
+
32
+ class Candidate
33
+
34
+ attr_reader :column, :named_range, :ref, :translations, :no_match_translation
35
+
36
+ def initialize(column, named_range:, ref: "_default")
37
+ @column = column
38
+ @named_range = named_range
39
+ @ref = ref
40
+
41
+ @translations = []
42
+ @no_match_translation = nil
43
+ end
44
+
45
+ def with_translation(&block)
46
+ translation = Translation.new
47
+ translation.instance_exec(&block)
48
+
49
+ @translations << Transformation::Translation.new(
50
+ translation.strategy,
51
+ translation.generator
52
+ )
53
+ end
54
+
55
+ def if_no_match(&block)
56
+ no_match_translation = Translation.new
57
+ no_match_translation.instance_exec(&block)
58
+
59
+ @no_match_translation = Transformation::Translation.new(
60
+ no_match_translation.strategy,
61
+ no_match_translation.generator
62
+ )
63
+ end
64
+ end
65
+
66
+ class Translation
67
+
68
+ attr_reader :strategy, :generator
69
+
70
+ def replace(name, **options)
71
+ klass_name = "Cure::Strategy::#{name.to_s.capitalize}Strategy"
72
+ raise "#{name} is not valid" unless class_exists?(klass_name)
73
+
74
+ @strategy = Kernel.const_get(klass_name).new(options)
75
+ self
76
+ end
77
+
78
+ def with(name, **options)
79
+ klass_name = "Cure::Generator::#{name.to_s.capitalize}Generator"
80
+ raise "#{name} is not valid" unless class_exists?(klass_name)
81
+
82
+ @generator = Kernel.const_get(klass_name).new(options)
83
+ self
84
+ end
85
+
86
+ def class_exists?(klass_name)
87
+ klass = Module.const_get(klass_name)
88
+ klass.is_a?(Class)
89
+ rescue NameError
90
+ false
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/validator/candidate"
4
+
5
+ module Cure
6
+ module Dsl
7
+ class Validator
8
+
9
+ attr_reader :candidates
10
+
11
+ def initialize
12
+ @candidates = []
13
+ end
14
+
15
+ def candidate(column: nil, named_range: "_default", options: {}, &block)
16
+ candidate = Cure::Validator::Candidate.new(column, named_range, options)
17
+ @candidates << candidate
18
+ candidate.instance_exec(&block)
19
+ end
20
+ end
21
+ end
22
+ end