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,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/log"
4
+ require "cure/config"
5
+ require "cure/extract/csv_lookup"
6
+ require "cure/extract/base_processor"
7
+
8
+ require "csv"
9
+
10
+ module Cure
11
+ module Extract
12
+ class NamedRangeProcessor < BaseProcessor
13
+
14
+ # @return [Array<Extraction::NamedRange>] named_ranges
15
+ attr_reader :candidate_nrs
16
+
17
+ def initialize(database_service, candidate_nrs)
18
+ @candidate_nrs = candidate_nrs
19
+ @cache = init_cache
20
+
21
+ @tables_created = []
22
+ super database_service
23
+ end
24
+
25
+ # @param [Integer] row_idx
26
+ # @param [Array] csv_row
27
+ def process_row(row_idx, csv_row) # rubocop:disable all
28
+ # Return if row is not in any named range
29
+ return unless row_bounds.cover?(row_idx)
30
+
31
+ # Iterate over the NR's, if its inside those bounds, add it
32
+ @candidate_nrs.each do |nr|
33
+ next unless nr.row_in_bounds?(row_idx)
34
+
35
+ if nr.filter.row_handler.has_content?
36
+ unless nr.filter.row_handler.including_proc[:where].call(csv_row)
37
+ nr.row_count += 1
38
+ next
39
+ end
40
+ end
41
+
42
+ # Row is inbounds - we need to do two things, filter the content, create the table, insert the row
43
+ if nr.header_in_bounds?(nr.active_row_count(row_idx))
44
+ column_headers = csv_row[nr.section[0]..nr.section[1]]
45
+
46
+ if nr.filter.col_handler.has_content?
47
+ nr.filter.col_handler.set_col_positions(column_headers)
48
+ column_headers = nr.filter.col_handler.translate_headers(column_headers)
49
+ end
50
+
51
+ # Create table, flush cache
52
+ create_table(nr.name, column_headers)
53
+ @tables_created << nr.name
54
+
55
+ @cache[nr.name].each do |row|
56
+ insert_record(
57
+ nr.name,
58
+ nr.filter.col_handler.filter_row(row)
59
+ )
60
+ end
61
+
62
+ @cache[nr.name] = [] # Evict cache
63
+
64
+ next
65
+ end
66
+
67
+ next unless nr.content_in_bounds?(row_idx)
68
+
69
+ # 0. Remove unnecessary columns
70
+
71
+
72
+ # 2. If cache is over n records and if the table exists,
73
+ # add it to the database.
74
+
75
+ filtered_row = nr.filter.col_handler.filter_row(
76
+ csv_row[nr.section[0]..nr.section[1]]
77
+ )
78
+
79
+ if @tables_created.include?(nr.name)
80
+ @cache[nr.name] << filtered_row.unshift(row_idx)
81
+
82
+ if @cache[nr.name].size >= 10
83
+ insert_cache(nr.name)
84
+ next
85
+ end
86
+ else
87
+ # If the table doesnt exist, cache it for now.
88
+ @cache[nr.name] << filtered_row.unshift(row_idx)
89
+ end
90
+ end
91
+ end
92
+
93
+ def after_process
94
+ @cache.each do |named_range, cache|
95
+ insert_cache(named_range) if cache.size.positive?
96
+ end
97
+ end
98
+
99
+ # @return [Range]
100
+ # This covers the max size of all named ranges
101
+ def row_bounds
102
+ @row_bounds ||= calculate_row_bounds
103
+ end
104
+
105
+ # @return [Range]
106
+ def calculate_row_bounds
107
+ positions = @candidate_nrs.map(&:row_bounds).flatten.sort
108
+ (positions.first..positions.last)
109
+ end
110
+
111
+ private
112
+
113
+ def init_cache
114
+ cache = {}
115
+ @candidate_nrs.each do |nr|
116
+ cache[nr.name] = []
117
+ end
118
+
119
+ cache
120
+ end
121
+
122
+ def insert_cache(named_range)
123
+ insert_batched_rows(named_range, @cache[named_range])
124
+ @cache[named_range] = []
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cure
4
+ module Extract
5
+ class Variable
6
+ attr_reader :name, :location, :ref_name
7
+
8
+ def initialize(name, location, ref_name: "_default")
9
+ @name = name
10
+ @location = [Extract::CsvLookup.position_for_letter(location),
11
+ Extract::CsvLookup.position_for_digit(location)]
12
+ @ref_name = ref_name
13
+ end
14
+
15
+ def row_in_bounds?(row_idx)
16
+ row_bounds_range.cover?(row_idx)
17
+ end
18
+
19
+ # @return [Range]
20
+ def row_bounds_range
21
+ @row_bounds_range ||= (@location&.last..@location&.last)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/log"
4
+ require "cure/config"
5
+ require "cure/extract/base_processor"
6
+
7
+ require "csv"
8
+
9
+ module Cure
10
+ module Extract
11
+ class VariableProcessor < BaseProcessor
12
+
13
+ # @return [Array<Extraction::Variable>] variables
14
+ attr_reader :candidate_variables
15
+
16
+ def initialize(database_service, candidate_variables)
17
+ super(database_service)
18
+
19
+ @candidate_variables = candidate_variables
20
+ @candidate_count = candidate_variables.length
21
+ @processed = 0
22
+
23
+ init_db
24
+ end
25
+
26
+ # @param [Integer] row_idx
27
+ # @param [Array] csv_row
28
+ def process_row(row_idx, csv_row)
29
+ # Return if row is not in any variable OR if all candidates are processed.
30
+
31
+ return if @candidate_count == @processed
32
+ return unless candidate_rows.include?(row_idx)
33
+
34
+ # Iterate over the NR's, if its inside those bounds, add it
35
+ @candidate_variables.each do |cv|
36
+ next unless cv.row_in_bounds?(row_idx)
37
+
38
+ insert_record(:variables, [nil, cv.name, csv_row[cv.location.first]])
39
+ @processed += 1
40
+ end
41
+ end
42
+
43
+ # @return [Array]
44
+ def candidate_rows
45
+ @candidate_rows ||= @candidate_variables.map { |v| v.location.last }
46
+ end
47
+
48
+ private
49
+
50
+ def init_db
51
+ return if @database_service.table_exist?(:variables)
52
+
53
+ create_table(:variables, %w[name value])
54
+ end
55
+ end
56
+ end
57
+ end
@@ -11,19 +11,29 @@ module Cure
11
11
  end
12
12
 
13
13
  # @param [Object/Nil] source_value
14
+ # @param [RowCtx/Nil] row_ctx
14
15
  # @return [String]
15
- def generate(source_value=nil)
16
- translated = _generate(source_value)
16
+ def generate(source_value, row_ctx)
17
+ translated = _generate(source_value, row_ctx)
17
18
  translated = "#{prefix}#{translated}" if prefix
18
19
  translated = "#{translated}#{suffix}" if suffix
19
20
  translated
20
21
  end
21
22
 
23
+ def describe
24
+ _describe
25
+ end
26
+
22
27
  private
23
28
 
29
+ # @param [RowCtx] _row_ctx
24
30
  # @param [Object/Nil] _source_value
25
31
  # @return [String]
26
- def _generate(_source_value)
32
+ def _generate(_source_value, _row_ctx)
33
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
34
+ end
35
+
36
+ def _describe
27
37
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
28
38
  end
29
39
 
@@ -44,7 +54,7 @@ module Cure
44
54
  end
45
55
 
46
56
  def extract_property(property, default_val)
47
- @options.fetch(property, default_val)
57
+ @options.fetch(property.to_sym, @options.fetch(property, default_val))
48
58
  end
49
59
  end
50
60
  end
@@ -8,18 +8,25 @@ module Cure
8
8
  private
9
9
 
10
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)
11
+ # @param [RowCtx] _row_ctx
12
+ def _generate(source_value, _row_ctx)
13
+ result = case_options.fetch(:switch)
14
+ .find { |opts| opts[:case] == source_value }
15
+ &.fetch(:return_value, nil)
13
16
 
14
17
  return result if result
15
18
 
16
- case_options.fetch("else", {}).fetch("return_value", nil)
19
+ case_options.fetch(:else, {}).fetch(:return_value, nil)
17
20
  end
18
21
 
19
22
  # @return [Hash]
20
23
  def case_options
21
24
  @case_options ||= extract_property("statement", nil)
22
25
  end
26
+
27
+ def _describe
28
+ "Will match source value against a value included in #{case_options}"
29
+ end
23
30
  end
24
31
  end
25
32
  end
@@ -13,15 +13,16 @@ module Cure
13
13
  private
14
14
 
15
15
  # @param [Object] source_value
16
- def _generate(source_value)
16
+ # @param [RowCtx] _row_ctx
17
+ def _generate(source_value, _row_ctx)
17
18
  arr = build_options.map(&:to_a).flatten
18
19
  (0...length(source_value&.length || 5)).map { arr[rand(arr.length)] }.join
19
20
  end
20
21
 
21
22
  def build_options
22
- return [("a".."z"), ("A".."Z"), (0..9)] unless @options.key?("types")
23
+ return [("a".."z"), ("A".."Z"), (0..9)] unless @options.key?(:types)
23
24
 
24
- type_array = @options["types"]
25
+ type_array = @options[:types]
25
26
 
26
27
  arr = []
27
28
  arr << ("a".."z") if type_array.include? "lowercase"
@@ -30,6 +31,11 @@ module Cure
30
31
  arr << ("!".."+") if type_array.include? "symbol"
31
32
  arr
32
33
  end
34
+
35
+ def _describe
36
+ "Will create a random list of #{@options[:types]} " \
37
+ "with as many characters as the source string."
38
+ end
33
39
  end
34
40
  end
35
41
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/generator/base_generator"
4
+ require "erb"
5
+
6
+ module Cure
7
+ module Generator
8
+ class ErbGenerator < BaseGenerator
9
+ private
10
+
11
+ # @param [Cure::Transformation::RowCtx] _source_value
12
+ # @param [RowCtx] row_ctx
13
+ def _generate(_source_value, row_ctx)
14
+ template = @options.fetch(:template, nil)
15
+ ERB.new(template).result_with_hash(row_ctx.row)
16
+ end
17
+
18
+ def _describe; end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/generator/base_generator"
4
+
5
+ module Cure
6
+ module Generator
7
+ class EvalGenerator < BaseGenerator
8
+ private
9
+
10
+ # @param [Object] _source_value
11
+ # @param [Transformation::RowCtx] _row_ctx
12
+ # This will be changed with expression evaluator
13
+ def _generate(_source_value, _row_ctx)
14
+ eval_str = extract_property("eval", nil)
15
+ result = nil
16
+ with_safe do
17
+ result = eval(eval_str) # rubocop:disable Security/Eval
18
+ end
19
+
20
+ result
21
+ rescue StandardError => e
22
+ raise "Cannot eval statement #{extract_property("eval", nil)} [#{e.message}]"
23
+ end
24
+
25
+ def with_safe(&_block)
26
+ $SAFE = 1
27
+ yield
28
+ $SAFE = 0
29
+ end
30
+
31
+ def _describe; end
32
+ end
33
+ end
34
+ end
@@ -9,7 +9,8 @@ module Cure
9
9
  private
10
10
 
11
11
  # @param [Object] _source_value
12
- def _generate(_source_value)
12
+ # @param [RowCtx] _row_ctx
13
+ def _generate(_source_value, _row_ctx)
13
14
  mod_code = extract_property("module", nil)
14
15
  mod = Faker.const_get(mod_code)
15
16
 
@@ -20,6 +21,11 @@ module Cure
20
21
 
21
22
  mod.send(meth_code)
22
23
  end
24
+
25
+ def _describe
26
+ "Will create a Faker value from [#{extract_property("module", nil)}::" \
27
+ "#{extract_property("method", nil)}]"
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -8,8 +8,13 @@ module Cure
8
8
  private
9
9
 
10
10
  # @param [Object] _source_value
11
- def _generate(_source_value)
12
- SecureRandom.uuid.to_s
11
+ # @param [RowCtx] _row_ctx
12
+ def _generate(_source_value, _row_ctx)
13
+ SecureRandom.uuid.to_s.slice(0, @options.fetch(:length, 36))
14
+ end
15
+
16
+ def _describe
17
+ "Will create a random GUID."
13
18
  end
14
19
  end
15
20
  end
@@ -8,9 +8,14 @@ module Cure
8
8
  private
9
9
 
10
10
  # @param [Object] _source_value
11
- def _generate(_source_value)
11
+ # @param [RowCtx] _row_ctx
12
+ def _generate(_source_value, _row_ctx)
12
13
  1.upto(length(rand(0..9))).map { rand(0..15).to_s(16) }.join("")
13
14
  end
15
+
16
+ def _describe
17
+ "Will create a random list of hex values matching the length of the source string."
18
+ end
14
19
  end
15
20
  end
16
21
  end
@@ -10,3 +10,7 @@ require "cure/generator/number_generator"
10
10
  require "cure/generator/placeholder_generator"
11
11
  require "cure/generator/redact_generator"
12
12
  require "cure/generator/variable_generator"
13
+ require "cure/generator/eval_generator"
14
+ require "cure/generator/erb_generator"
15
+ require "cure/generator/proc_generator"
16
+ require "cure/generator/static_generator"
@@ -8,9 +8,14 @@ module Cure
8
8
  private
9
9
 
10
10
  # @param [Object] _source_value
11
- def _generate(_source_value)
11
+ # @param [RowCtx] _row_ctx
12
+ def _generate(_source_value, _row_ctx)
12
13
  1.upto(length(rand(0..9))).map { rand(1..9) }.join("").to_i
13
14
  end
15
+
16
+ def _describe
17
+ "Will create a random list of numbers matching the length of the source string."
18
+ end
14
19
  end
15
20
  end
16
21
  end
@@ -11,10 +11,16 @@ module Cure
11
11
  private
12
12
 
13
13
  # @param [Object] _source_value
14
- def _generate(_source_value)
14
+ # @param [RowCtx] _row_ctx
15
+ def _generate(_source_value, _row_ctx)
15
16
  value = config.placeholders[property_name]
16
17
  value || raise("Missing placeholder value. Available candidates: [#{config.placeholders.keys.join(", ")}]")
17
18
  end
19
+
20
+ def _describe
21
+ "Will look up placeholders using '#{property_name}'. " \
22
+ "[Set as '#{config.placeholders[property_name]}']"
23
+ end
18
24
  end
19
25
  end
20
26
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/generator/base_generator"
4
+ require "erb"
5
+
6
+ module Cure
7
+ module Generator
8
+ class ProcGenerator < BaseGenerator
9
+ private
10
+
11
+ # @param [Cure::Transformation::RowCtx] source_value
12
+ # @param [RowCtx] row_ctx
13
+ def _generate(source_value, row_ctx)
14
+ proc = @options.fetch(:execute, nil)
15
+ proc.call(source_value, row_ctx)
16
+ end
17
+
18
+ def _describe; end
19
+ end
20
+ end
21
+ end
@@ -7,10 +7,16 @@ module Cure
7
7
  class RedactGenerator < BaseGenerator
8
8
  private
9
9
 
10
- # @param [Object] source_value
11
- def _generate(source_value)
12
- 1.upto(length(source_value&.length || 5)).map { "X" }.join("")
10
+ # @param [object] source_value
11
+ # @param [RowCtx] _row_ctx
12
+ def _generate(source_value, _row_ctx)
13
+ 1.upto(length(source_value&.length || 5)).map { @options.fetch(:character, "x") }.join("")
13
14
  end
15
+
16
+ def _describe
17
+ "Will replace the length of the source string with X."
18
+ end
19
+
14
20
  end
15
21
  end
16
22
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/generator/base_generator"
4
+
5
+ module Cure
6
+ module Generator
7
+ class StaticGenerator < BaseGenerator
8
+ private
9
+
10
+ # @param [Object] _source_value
11
+ # @param [RowCtx] _row_ctx
12
+ def _generate(_source_value, _row_ctx)
13
+ @options.fetch(:value, nil)
14
+ end
15
+
16
+ def _describe
17
+ "Will return the defined value [#{@options.fetch(:value, nil)}]"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -2,18 +2,24 @@
2
2
 
3
3
  require "cure/generator/base_generator"
4
4
  require "cure/config"
5
+ require "cure/database"
5
6
 
6
7
  module Cure
7
8
  module Generator
8
9
  class VariableGenerator < BaseGenerator
9
- include Cure::Configuration
10
+ include Database
10
11
 
11
12
  private
12
13
 
13
- # @param [Object] _source_value
14
- def _generate(_source_value)
15
- value = config.variables[property_name]
16
- value || raise("Missing placeholder value. Available candidates: [#{config.variables.keys.join(", ")}]")
14
+ # @param [object] _source_value
15
+ # @param [RowCtx] _row_ctx
16
+ def _generate(_source_value, _row_ctx)
17
+ value = database_service.find_variable(property_name)
18
+ value || raise("Missing placeholder value [#{property_name}]. Please check you are defining it correctly.")
19
+ end
20
+
21
+ def _describe
22
+ "Will look up the variables defined using '#{property_name}'."
17
23
  end
18
24
  end
19
25
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "pathname"
4
5
 
5
6
  module Cure
6
7
  module Helpers
@@ -21,13 +22,22 @@ module Cure
21
22
  end
22
23
 
23
24
  def read_file(file_location)
24
- result = file_location.start_with?("/") ? file_location : File.join(File.dirname(__FILE__), file_location)
25
+ result = file_location.start_with?("/") ? file_location : Pathname.new(file_location)
26
+ # result = file_location.start_with?("/") ? file_location : File.join(File.dirname(__FILE__), file_location)
25
27
 
26
- raise "No file found at [#{file_location}]" unless File.exist? result
28
+ raise "No file found at [#{file_location}]" unless File.exist? result.to_s
27
29
 
28
30
  File.read(result)
29
31
  end
30
32
 
33
+ def open_file(file_location)
34
+ result = file_location.start_with?("/") ? file_location : Pathname.new(file_location)
35
+
36
+ raise "No file found at [#{file_location}]" unless File.exist? result.to_s
37
+
38
+ File.open(result)
39
+ end
40
+
31
41
  def with_temp_dir(temp_dir, &_block)
32
42
  return unless block_given?
33
43
 
@@ -6,23 +6,11 @@ require "json"
6
6
  module Cure
7
7
  module Helpers
8
8
  module ObjectHelpers
9
- def attributes=(hash)
10
- hash.each do |key, value|
11
- send("#{key}=", value)
12
- rescue NoMethodError
13
- Cure.logger.warn("Error deserializing object: No property for #{key}")
14
- end
15
- end
16
-
17
- def from_json(json)
18
- return from_hash(json) if json.is_a?(Hash) # Just a guard in case serialisation is done
19
-
20
- from_hash(JSON.parse(json))
21
- end
22
-
23
- def from_hash(hash)
24
- self.attributes = hash
25
- self
9
+ def class_exists?(klass_name)
10
+ klass = Module.const_get(klass_name)
11
+ klass.is_a?(Class)
12
+ rescue NameError
13
+ false
26
14
  end
27
15
  end
28
16
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "benchmark"
5
+
6
+ module Cure
7
+ module Helpers
8
+ # This module uses some code sourced from here:
9
+ # https://torrocus.com/blog/different-ways-to-processing-large-csv-file-in-ruby/
10
+ module PerfHelpers
11
+ def print_memory_usage(process_name="default")
12
+ cmd = "ps -o rss= -p #{Process.pid}"
13
+ before_mem = `#{cmd}`.to_i
14
+ # before_gc = GC.stat(:total_allocated_objects)
15
+
16
+ yield
17
+ after_mem = `#{cmd}`.to_i
18
+ # after_gc = GC.stat(:total_allocated_objects)
19
+
20
+ log_info "Total Memory Usage [#{process_name}]: #{((after_mem - before_mem) / 1024.0).round(2)} MB"
21
+ # log_info "Total GC Objects Freed [#{process_name}]: #{after_gc - before_gc}"
22
+ end
23
+
24
+ def print_time_spent(process_name="default", &block)
25
+ time = Benchmark.realtime(&block)
26
+ log_info "Total Processing Time [#{process_name}]: #{time.round(2)}"
27
+ end
28
+ end
29
+ end
30
+ end