cure 0.1.1 → 0.1.2
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 +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +16 -6
- data/exe/cure +7 -3
- data/lib/cure/config.rb +17 -3
- data/lib/cure/coordinator.rb +72 -0
- data/lib/cure/export/exporter.rb +32 -7
- data/lib/cure/extract/builder.rb +27 -0
- data/lib/cure/extract/csv_lookup.rb +32 -0
- data/lib/cure/extract/extractor.rb +123 -0
- data/lib/cure/generator/base_generator.rb +51 -0
- data/lib/cure/generator/case_generator.rb +25 -0
- data/lib/cure/generator/character_generator.rb +35 -0
- data/lib/cure/generator/faker_generator.rb +25 -0
- data/lib/cure/generator/guid_generator.rb +16 -0
- data/lib/cure/generator/hex_generator.rb +16 -0
- data/lib/cure/generator/imports.rb +12 -0
- data/lib/cure/generator/number_generator.rb +16 -0
- data/lib/cure/generator/placeholder_generator.rb +20 -0
- data/lib/cure/generator/redact_generator.rb +16 -0
- data/lib/cure/generator/variable_generator.rb +20 -0
- data/lib/cure/helpers/file_helpers.rb +40 -0
- data/lib/cure/helpers/object_helpers.rb +29 -0
- data/lib/cure/log.rb +3 -3
- data/lib/cure/main.rb +40 -31
- data/lib/cure/strategy/append_strategy.rb +24 -0
- data/lib/cure/strategy/base_strategy.rb +123 -0
- data/lib/cure/strategy/end_with_strategy.rb +46 -0
- data/lib/cure/strategy/full_strategy.rb +24 -0
- data/lib/cure/strategy/imports.rb +10 -0
- data/lib/cure/strategy/match_strategy.rb +43 -0
- data/lib/cure/strategy/regex_strategy.rb +49 -0
- data/lib/cure/strategy/split_strategy.rb +53 -0
- data/lib/cure/strategy/start_with_strategy.rb +47 -0
- data/lib/cure/template/dispatch.rb +30 -0
- data/lib/cure/template/extraction.rb +38 -0
- data/lib/cure/template/template.rb +28 -0
- data/lib/cure/template/transformations.rb +26 -0
- data/lib/cure/transformation/candidate.rb +23 -9
- data/lib/cure/transformation/transform.rb +33 -41
- data/lib/cure/validators.rb +71 -0
- data/lib/cure/version.rb +1 -1
- data/lib/cure.rb +9 -4
- data/templates/aws_cur_template.json +130 -128
- data/templates/example_template.json +46 -30
- metadata +36 -9
- data/lib/cure/csv_helpers.rb +0 -6
- data/lib/cure/file_helpers.rb +0 -38
- data/lib/cure/generator/base.rb +0 -148
- data/lib/cure/object_helpers.rb +0 -27
- data/lib/cure/strategy/base.rb +0 -223
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29e7c915346b45c1407208f6ba1810ece68004c8d7dd9ffb8eb2b5dcca903b35
|
4
|
+
data.tar.gz: c356c649bc4a902fcb347e9e16f2299f4c8a7c0db576a7fc8242d080b575920d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5999a52c084656af0a6a18ba68884aec3de825be9f84f5c67cdf771031af414789aaebf24dfc9fed52f6a76b9a9495081f0f4e1c0f872927fbe34c271fa86e62
|
7
|
+
data.tar.gz: 458962e9905380ef0e5832ef9e2ae2da1ede47f448992314bceba95c997c2af3bb4d6f2b92e1f23e906563c77ad1fbcd37d171d75732cf29c15b6b9b0e05e5bc
|
data/.rubocop.yml
CHANGED
@@ -26,6 +26,9 @@ Metrics/ClassLength:
|
|
26
26
|
Layout/SpaceAroundEqualsInParameterDefault:
|
27
27
|
EnforcedStyle: no_space
|
28
28
|
|
29
|
+
Layout/EmptyLinesAroundClassBody:
|
30
|
+
Enabled: false
|
31
|
+
|
29
32
|
# We do not need to support Ruby 1.9, so this is good to use.
|
30
33
|
Style/SymbolArray:
|
31
34
|
Enabled: true
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,19 +1,29 @@
|
|
1
1
|
# Cure
|
2
2
|
|
3
3
|

|
4
|
+
[](https://badge.fury.io/rb/cure)
|
4
5
|
|
5
|
-
Cure is a simple tool to **remove/redact/anonymize** and **replace**
|
6
|
-
It has been written to anonymize private cloud billing data for use in public demo environments.
|
6
|
+
Cure is a simple tool to **extract/clean/transform/remove/redact/anonymize** and **replace** information in a spreadsheet.
|
7
|
+
It has been written to anonymize private cloud billing data for use in public demo environments. Since then, it has grown to
|
8
|
+
additional processing capabilities that can take a CSV from junk to workable data.
|
7
9
|
|
8
10
|
It has several key features:
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
|
11
|
+
- Operate on your data to build what you need.
|
12
|
+
- Files are taken through an `Extract -> Build -> Transform -> Export` pipeline.
|
13
|
+
- Extract parts of your file into named ranges to remove junk.
|
14
|
+
- Build (Add/Remove/Explode) columns - handy for files that may have JSON as a column value.
|
15
|
+
- Transform values:
|
16
|
+
- Define either full or regex match groups replacements.
|
17
|
+
- Choose from many strategies to replace anonymous data - random number sequences, GUIDs, placeholders, multipliers amongst many others.
|
18
|
+
- **Existing generated values are stored and recalled** so once a replacement is defined, it is kept around for other columns to use.
|
19
|
+
- For example, once a replacement **Account Number** is generated, any further use of that number sequence is other columns will be used, keeping data real(ish) and functional in a relational sense.
|
20
|
+
- Export into one (or many) files, in a selection of chosen formats (CSV at the moment, coming soon with JSON, Parquet).
|
13
21
|
|
14
22
|
## Use Cases
|
15
23
|
|
16
24
|
- Strip out personal data from a CSV that may be used for public demo.
|
25
|
+
- Extract specific parts of a CSV file and junk the rest.
|
26
|
+
- Explode JSON values into individual columns per key.
|
17
27
|
|
18
28
|
## Usage
|
19
29
|
|
data/exe/cure
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
# rubocop:disable Style/MixinUsage
|
5
|
+
# rubocop:disable Style/MethodCalledOnDoEndBlock
|
6
|
+
|
4
7
|
require "cure"
|
5
8
|
require "optparse"
|
6
9
|
|
@@ -34,9 +37,10 @@ OptionParser.new do |opts|
|
|
34
37
|
end.parse!
|
35
38
|
|
36
39
|
log_info "Config loaded successfully, initialising environment ..."
|
37
|
-
main = Cure::Main.
|
40
|
+
main = Cure::Main.init_from_file(conf[:template_file_location], conf[:source_file_location], conf[:output_dir])
|
38
41
|
|
39
42
|
log_info "... set up complete. Beginning process"
|
40
|
-
main.
|
41
|
-
|
43
|
+
main.run_export
|
42
44
|
|
45
|
+
# rubocop:enable Style/MixinUsage
|
46
|
+
# rubocop:enable Style/MethodCalledOnDoEndBlock
|
data/lib/cure/config.rb
CHANGED
@@ -19,16 +19,30 @@ module Cure
|
|
19
19
|
ConfigurationSource.instance.load_config(request_config)
|
20
20
|
end
|
21
21
|
|
22
|
+
# @param [String] source_file_location
|
23
|
+
# @param [Cure::Template] template
|
24
|
+
# @param [String] output_dir
|
22
25
|
# @return [Config]
|
23
26
|
def create_config(source_file_location, template, output_dir)
|
24
27
|
Config.new(source_file_location, template, output_dir)
|
25
28
|
end
|
26
29
|
|
30
|
+
# If we are overloading here as a "data store" and "config store", we
|
31
|
+
# could break out variables and placeholders into their own singleton.
|
32
|
+
#
|
33
|
+
# This should be a kind of instance cache, which loads once per run,
|
34
|
+
# and junk can be jammed in there?
|
27
35
|
class Config
|
28
|
-
attr_accessor :source_file_location, :
|
36
|
+
attr_accessor :source_file_location, :output_dir
|
37
|
+
|
38
|
+
# @return [Cure::Template] template
|
39
|
+
attr_accessor :template
|
40
|
+
|
41
|
+
# @return [Hash] variables
|
42
|
+
attr_accessor :variables
|
29
43
|
|
30
44
|
# @param [String] source_file_location
|
31
|
-
# @param [
|
45
|
+
# @param [Cure::Template] template
|
32
46
|
# @param [String] output_dir
|
33
47
|
def initialize(source_file_location, template, output_dir)
|
34
48
|
@source_file_location = source_file_location
|
@@ -37,7 +51,7 @@ module Cure
|
|
37
51
|
end
|
38
52
|
|
39
53
|
def placeholders
|
40
|
-
@template
|
54
|
+
@template.transformations.placeholders || {}
|
41
55
|
end
|
42
56
|
end
|
43
57
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/log"
|
4
|
+
require "cure/config"
|
5
|
+
require "cure/helpers/file_helpers"
|
6
|
+
|
7
|
+
require "cure/extract/extractor"
|
8
|
+
require "cure/transformation/transform"
|
9
|
+
require "cure/export/exporter"
|
10
|
+
|
11
|
+
require "rcsv"
|
12
|
+
|
13
|
+
module Cure
|
14
|
+
# Coordinates the entire process:
|
15
|
+
# Extract -> Build -> Transform -> Export
|
16
|
+
class Coordinator
|
17
|
+
include Configuration
|
18
|
+
include Log
|
19
|
+
|
20
|
+
def process
|
21
|
+
# need to check config is init'd
|
22
|
+
|
23
|
+
extracted_csv = extract
|
24
|
+
built_csv = build(extracted_csv)
|
25
|
+
transformed_csv = transform(built_csv)
|
26
|
+
export(transformed_csv)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# @return [Cure::Extract::WrappedCSV]
|
32
|
+
def extract
|
33
|
+
log_info "Beginning the extraction process..."
|
34
|
+
|
35
|
+
extractor = Extract::Extractor.new({})
|
36
|
+
result = extractor.extract_from_file(config.source_file_location)
|
37
|
+
|
38
|
+
log_debug "Setting extracted variables to global conf for access downstream"
|
39
|
+
config.variables = result.variables
|
40
|
+
|
41
|
+
log_info "...extraction complete"
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Cure::Extract::WrappedCSV] wrapped_csv
|
46
|
+
def build(wrapped_csv)
|
47
|
+
log_info "Beginning the building process..."
|
48
|
+
log_info "... building complete"
|
49
|
+
wrapped_csv
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Hash<String,Cure::Transformation::TransformResult>]
|
53
|
+
# @param [Cure::Extract::WrappedCSV] parsed_csv
|
54
|
+
def transform(parsed_csv)
|
55
|
+
log_info "Beginning the transformation process..."
|
56
|
+
transformer = Cure::Transformation::Transform.new(config.template.transformations.candidates)
|
57
|
+
content = transformer.transform_content(parsed_csv)
|
58
|
+
|
59
|
+
log_info "...transform complete"
|
60
|
+
content
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Hash<String,Cure::Transformation::TransformResult>]
|
64
|
+
def export(transformed_result)
|
65
|
+
log_info "Beginning export process..."
|
66
|
+
Cure::Export::Exporter.export_result(transformed_result, config.output_dir)
|
67
|
+
log_info "... export complete."
|
68
|
+
|
69
|
+
transformed_result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/cure/export/exporter.rb
CHANGED
@@ -1,24 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "cure/log"
|
4
|
-
require "cure/
|
4
|
+
require "cure/config"
|
5
|
+
require "cure/helpers/file_helpers"
|
5
6
|
|
6
7
|
module Cure
|
7
8
|
module Export
|
8
9
|
class Exporter
|
9
|
-
include
|
10
|
+
include Helpers::FileHelpers
|
11
|
+
include Configuration
|
10
12
|
include Log
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
# @param [Array<Cure::Transform::TransformResult>] result
|
15
|
+
def self.export_result(results, output_dir)
|
16
|
+
exporter = Exporter.new(output_dir)
|
17
|
+
exporter.export_results(results)
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :output_dir
|
21
|
+
|
22
|
+
def initialize(output_dir)
|
23
|
+
@output_dir = output_dir
|
24
|
+
end
|
14
25
|
|
15
|
-
|
16
|
-
|
26
|
+
# @param [Array<Cure::Transform::TransformResult>] result
|
27
|
+
def export_results(result)
|
28
|
+
export_ranges = config.template.dispatch.named_ranges
|
29
|
+
|
30
|
+
export_ranges.each do |range|
|
31
|
+
named_range = range["named_range"]
|
32
|
+
unless result.has_key?(named_range)
|
33
|
+
raise "Missing named range - #{range} from candidates [#{result.keys.join(", ")}]"
|
34
|
+
end
|
35
|
+
|
36
|
+
data = result[named_range]
|
37
|
+
column_headers = data.column_headers.keys
|
38
|
+
export(@output_dir, range["file_name"], data.transformed_rows, column_headers)
|
39
|
+
end
|
17
40
|
end
|
18
41
|
|
19
42
|
# @param [Array] rows
|
20
43
|
# @param [Array] columns
|
21
44
|
def export(output_dir, file_name, rows, columns)
|
45
|
+
file_name = "#{file_name}-#{Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S%-z")}"
|
46
|
+
|
22
47
|
log_info("Exporting file to [#{output_dir}/#{file_name}] with #{rows.length} rows")
|
23
48
|
|
24
49
|
file_contents = []
|
@@ -37,7 +62,7 @@ module Cure
|
|
37
62
|
# @param [String] contents
|
38
63
|
# @param [String] file_extension
|
39
64
|
def write_to_file(file_path, file_name, file_extension, contents)
|
40
|
-
file_location = "#{file_path}/#{file_name
|
65
|
+
file_location = "#{file_path}/#{file_name}"
|
41
66
|
clean_dir(file_path)
|
42
67
|
|
43
68
|
with_file(file_location, file_extension) do |file|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/extract/csv_lookup"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Extract
|
7
|
+
class Builder
|
8
|
+
|
9
|
+
# @param [Hash] opts
|
10
|
+
attr_reader :opts
|
11
|
+
|
12
|
+
# @param [Hash] opts
|
13
|
+
def initialize(opts)
|
14
|
+
@opts = opts
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [Array<Array>] _sheet
|
18
|
+
# @param [Hash<String, Integer>] _column_headers
|
19
|
+
# @return [Array]
|
20
|
+
#
|
21
|
+
# This returns changed column headers and sheets
|
22
|
+
def handle(_sheet, _column_headers)
|
23
|
+
[]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cure
|
4
|
+
module Extract
|
5
|
+
class CsvLookup
|
6
|
+
|
7
|
+
# @param [String] position - [Ex A1:B1, A1:B1,A2:B2]
|
8
|
+
# @return [Array] [column_start_idx, column_end_idx, row_start_idx, row_end_idx]
|
9
|
+
def self.array_position_lookup(position)
|
10
|
+
return [0, -1, 0, -1] if position.is_a?(Integer) && position == -1 # Whole sheet
|
11
|
+
|
12
|
+
start, finish, *_excess = position.split(":")
|
13
|
+
raise "Invalid format" unless start || finish
|
14
|
+
|
15
|
+
[
|
16
|
+
position_for_letter(start),
|
17
|
+
position_for_letter(finish),
|
18
|
+
position_for_digit(start),
|
19
|
+
position_for_digit(finish)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.position_for_letter(range)
|
24
|
+
range.upcase.scan(/[A-Z]+/).first.ord - 65 # A (65) - 65 = 0 idx
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.position_for_digit(range)
|
28
|
+
range.upcase.scan(/\d+/).first.to_i - 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/log"
|
4
|
+
require "cure/config"
|
5
|
+
require "cure/extract/csv_lookup"
|
6
|
+
require "cure/helpers/file_helpers"
|
7
|
+
|
8
|
+
module Cure
|
9
|
+
module Extract
|
10
|
+
class Extractor
|
11
|
+
include Log
|
12
|
+
include Configuration
|
13
|
+
include Helpers::FileHelpers
|
14
|
+
|
15
|
+
# @param [Hash] opts
|
16
|
+
attr_reader :opts
|
17
|
+
|
18
|
+
# @param [Hash] opts
|
19
|
+
def initialize(opts)
|
20
|
+
@opts = opts
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [String] csv_file_location
|
24
|
+
# @return [WrappedCSV]
|
25
|
+
def extract_from_file(csv_file_location)
|
26
|
+
file_contents = read_file(csv_file_location)
|
27
|
+
extract_from_contents(file_contents)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [String] file_contents
|
31
|
+
# @return [WrappedCSV]
|
32
|
+
def extract_from_contents(file_contents)
|
33
|
+
parsed_content = parse_csv(file_contents, header: :none)
|
34
|
+
log_info("Parsed CSV into #{parsed_content.content.length} sections.")
|
35
|
+
parsed_content
|
36
|
+
end
|
37
|
+
|
38
|
+
# private
|
39
|
+
|
40
|
+
# @param [String] file_contents
|
41
|
+
# @param [Hash] opts
|
42
|
+
# @return [WrappedCSV]
|
43
|
+
def parse_csv(file_contents, opts={})
|
44
|
+
csv_rows = []
|
45
|
+
|
46
|
+
Rcsv.parse(file_contents, opts) { |row| csv_rows << row }
|
47
|
+
|
48
|
+
result = WrappedCSV.new
|
49
|
+
result.content = extract_named_ranges(csv_rows)
|
50
|
+
result.variables = extract_variables(csv_rows)
|
51
|
+
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param [Array<Array>] csv_rows
|
56
|
+
# @return [Array<Hash>]
|
57
|
+
def extract_named_ranges(csv_rows)
|
58
|
+
# Use only the NR's that are defined from the candidates list
|
59
|
+
candidates = config.template.transformations.candidates
|
60
|
+
candidate_nrs = config.template.extraction.required_named_ranges(candidates.map(&:named_range).uniq)
|
61
|
+
candidate_nrs.map do |nr|
|
62
|
+
{
|
63
|
+
"rows" => extract_from_rows(csv_rows, nr["section"]),
|
64
|
+
"name" => nr["name"]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [Array<Array>] csv_rows
|
70
|
+
# @return [Hash]
|
71
|
+
def extract_variables(csv_rows)
|
72
|
+
config.template.extraction.variables.each_with_object({}) do |variable, hash|
|
73
|
+
hash[variable["name"]] = lookup_location(csv_rows, variable["location"])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param [Array<Array>] rows
|
78
|
+
def extract_from_rows(rows, named_range)
|
79
|
+
psx = CsvLookup.array_position_lookup(named_range)
|
80
|
+
|
81
|
+
ret_val = []
|
82
|
+
rows.each_with_index do |row, idx|
|
83
|
+
# If the position of the end row is -1, we need all,
|
84
|
+
# otherwise if its between/equal to start/finish
|
85
|
+
ret_val << row[psx[0]..psx[1]] if psx[3] == -1 || (idx >= psx[2] && idx <= psx[3])
|
86
|
+
end
|
87
|
+
|
88
|
+
ret_val
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param [Array<Array>] rows
|
92
|
+
# @param [String] variable_location
|
93
|
+
def lookup_location(rows, variable_location)
|
94
|
+
psx = [CsvLookup.position_for_letter(variable_location),
|
95
|
+
CsvLookup.position_for_digit(variable_location)]
|
96
|
+
rows[psx[1]][psx[0]]
|
97
|
+
end
|
98
|
+
|
99
|
+
# @param [Integer] row_idx
|
100
|
+
# @param [Array] row
|
101
|
+
# @param [Array] psx
|
102
|
+
# @return [Array, nil]
|
103
|
+
def handle_row(row_idx, row, psx)
|
104
|
+
return nil unless psx[3] == -1 || (row_idx >= psx[2] && row_idx <= psx[3])
|
105
|
+
|
106
|
+
row[psx[0]..psx[1]]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class WrappedCSV
|
111
|
+
# @return [Array<Hash>]
|
112
|
+
attr_accessor :content
|
113
|
+
|
114
|
+
# @return [Hash]
|
115
|
+
attr_accessor :variables
|
116
|
+
|
117
|
+
def initialize
|
118
|
+
@content = []
|
119
|
+
@variables = {}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cure
|
4
|
+
module Generator
|
5
|
+
class BaseGenerator
|
6
|
+
# @return [Hash]
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param [Object/Nil] source_value
|
14
|
+
# @return [String]
|
15
|
+
def generate(source_value=nil)
|
16
|
+
translated = _generate(source_value)
|
17
|
+
translated = "#{prefix}#{translated}" if prefix
|
18
|
+
translated = "#{translated}#{suffix}" if suffix
|
19
|
+
translated
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @param [Object/Nil] _source_value
|
25
|
+
# @return [String]
|
26
|
+
def _generate(_source_value)
|
27
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
def prefix(default=nil)
|
31
|
+
extract_property("prefix", default)
|
32
|
+
end
|
33
|
+
|
34
|
+
def suffix(default=nil)
|
35
|
+
extract_property("suffix", default)
|
36
|
+
end
|
37
|
+
|
38
|
+
def length(default=nil)
|
39
|
+
extract_property("length", default)
|
40
|
+
end
|
41
|
+
|
42
|
+
def property_name(default=nil)
|
43
|
+
extract_property("name", default)
|
44
|
+
end
|
45
|
+
|
46
|
+
def extract_property(property, default_val)
|
47
|
+
@options.fetch(property, default_val)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Generator
|
7
|
+
class CaseGenerator < BaseGenerator
|
8
|
+
private
|
9
|
+
|
10
|
+
# @param [Object] source_value
|
11
|
+
def _generate(source_value)
|
12
|
+
result = case_options.fetch("switch").find { |opts| opts["case"] == source_value }&.fetch("return_value", nil)
|
13
|
+
|
14
|
+
return result if result
|
15
|
+
|
16
|
+
case_options.fetch("else", {}).fetch("return_value", nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Hash]
|
20
|
+
def case_options
|
21
|
+
@case_options ||= extract_property("statement", nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Generator
|
7
|
+
class CharacterGenerator < BaseGenerator
|
8
|
+
|
9
|
+
def initialize(options=nil)
|
10
|
+
super(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @param [Object] source_value
|
16
|
+
def _generate(source_value)
|
17
|
+
arr = build_options.map(&:to_a).flatten
|
18
|
+
(0...length(source_value&.length || 5)).map { arr[rand(arr.length)] }.join
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_options
|
22
|
+
return [("a".."z"), ("A".."Z"), (0..9)] unless @options.key?("types")
|
23
|
+
|
24
|
+
type_array = @options["types"]
|
25
|
+
|
26
|
+
arr = []
|
27
|
+
arr << ("a".."z") if type_array.include? "lowercase"
|
28
|
+
arr << ("A".."Z") if type_array.include? "uppercase"
|
29
|
+
arr << (0..9) if type_array.include? "number"
|
30
|
+
arr << ("!".."+") if type_array.include? "symbol"
|
31
|
+
arr
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
require "faker"
|
5
|
+
|
6
|
+
module Cure
|
7
|
+
module Generator
|
8
|
+
class FakerGenerator < BaseGenerator
|
9
|
+
private
|
10
|
+
|
11
|
+
# @param [Object] _source_value
|
12
|
+
def _generate(_source_value)
|
13
|
+
mod_code = extract_property("module", nil)
|
14
|
+
mod = Faker.const_get(mod_code)
|
15
|
+
|
16
|
+
raise "No Faker module found for [#{mod_code}]" unless mod
|
17
|
+
|
18
|
+
meth_code = extract_property("method", nil)&.to_sym
|
19
|
+
raise "No Faker module found for [#{meth_code}]" unless mod.methods.include?(meth_code)
|
20
|
+
|
21
|
+
mod.send(meth_code)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Generator
|
7
|
+
class GuidGenerator < BaseGenerator
|
8
|
+
private
|
9
|
+
|
10
|
+
# @param [Object] _source_value
|
11
|
+
def _generate(_source_value)
|
12
|
+
SecureRandom.uuid.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Generator
|
7
|
+
class HexGenerator < BaseGenerator
|
8
|
+
private
|
9
|
+
|
10
|
+
# @param [Object] _source_value
|
11
|
+
def _generate(_source_value)
|
12
|
+
1.upto(length(rand(0..9))).map { rand(0..15).to_s(16) }.join("")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
require "cure/generator/hex_generator"
|
5
|
+
require "cure/generator/case_generator"
|
6
|
+
require "cure/generator/character_generator"
|
7
|
+
require "cure/generator/faker_generator"
|
8
|
+
require "cure/generator/guid_generator"
|
9
|
+
require "cure/generator/number_generator"
|
10
|
+
require "cure/generator/placeholder_generator"
|
11
|
+
require "cure/generator/redact_generator"
|
12
|
+
require "cure/generator/variable_generator"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/generator/base_generator"
|
4
|
+
|
5
|
+
module Cure
|
6
|
+
module Generator
|
7
|
+
class NumberGenerator < BaseGenerator
|
8
|
+
private
|
9
|
+
|
10
|
+
# @param [Object] _source_value
|
11
|
+
def _generate(_source_value)
|
12
|
+
1.upto(length(rand(0..9))).map { rand(1..9) }.join("").to_i
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|