cure 0.1.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +16 -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 +59 -81
  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 -37
  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 +151 -13
  38. data/lib/cure/coordinator.rb +108 -0
  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 +43 -0
  53. data/lib/cure/extract/extractor.rb +80 -0
  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 +61 -0
  60. data/lib/cure/generator/case_generator.rb +32 -0
  61. data/lib/cure/generator/character_generator.rb +41 -0
  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 +31 -0
  65. data/lib/cure/generator/guid_generator.rb +21 -0
  66. data/lib/cure/generator/hex_generator.rb +21 -0
  67. data/lib/cure/generator/imports.rb +16 -0
  68. data/lib/cure/generator/number_generator.rb +21 -0
  69. data/lib/cure/generator/placeholder_generator.rb +26 -0
  70. data/lib/cure/generator/proc_generator.rb +21 -0
  71. data/lib/cure/generator/redact_generator.rb +22 -0
  72. data/lib/cure/generator/static_generator.rb +21 -0
  73. data/lib/cure/generator/variable_generator.rb +26 -0
  74. data/lib/cure/helpers/file_helpers.rb +50 -0
  75. data/lib/cure/helpers/object_helpers.rb +17 -0
  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 +10 -3
  80. data/lib/cure/planner.rb +136 -0
  81. data/lib/cure/strategy/append_strategy.rb +28 -0
  82. data/lib/cure/strategy/base_strategy.rb +98 -0
  83. data/lib/cure/strategy/contain_strategy.rb +51 -0
  84. data/lib/cure/strategy/end_with_strategy.rb +52 -0
  85. data/lib/cure/strategy/full_strategy.rb +28 -0
  86. data/lib/cure/strategy/history/history_cache.rb +82 -0
  87. data/lib/cure/strategy/imports.rb +12 -0
  88. data/lib/cure/strategy/match_strategy.rb +48 -0
  89. data/lib/cure/strategy/prepend_strategy.rb +28 -0
  90. data/lib/cure/strategy/regex_strategy.rb +55 -0
  91. data/lib/cure/strategy/split_strategy.rb +58 -0
  92. data/lib/cure/strategy/start_with_strategy.rb +53 -0
  93. data/lib/cure/transformation/candidate.rb +47 -36
  94. data/lib/cure/transformation/transform.rb +29 -71
  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 +71 -0
  99. data/lib/cure/version.rb +1 -1
  100. data/lib/cure.rb +19 -6
  101. data/templates/dsl_example.rb +48 -0
  102. data/templates/empty_template.rb +31 -0
  103. metadata +161 -23
  104. data/lib/cure/csv_helpers.rb +0 -6
  105. data/lib/cure/export/exporter.rb +0 -49
  106. data/lib/cure/file_helpers.rb +0 -38
  107. data/lib/cure/generator/base.rb +0 -148
  108. data/lib/cure/main.rb +0 -63
  109. data/lib/cure/object_helpers.rb +0 -27
  110. data/lib/cure/strategy/base.rb +0 -223
  111. data/templates/aws_cur_template.json +0 -143
  112. data/templates/example_template.json +0 -38
@@ -0,0 +1,36 @@
1
+ **Sources** > Extract > Validate > Build > **Query** > Transform > Export
2
+
3
+ Sources
4
+ =======
5
+
6
+ ### About
7
+
8
+ The sources step allows you to define the files that will be extracted. You can supply it in the template,
9
+ or you can provide it when you are initializing `Cure::Launcher`.
10
+
11
+ As Cure templates are just Ruby, you can define the files dynamically, and provide them to the template DSL.
12
+
13
+ ---
14
+
15
+ **When you should use this**: You want to load a CSV file, but you don't want to do it at initialize.
16
+
17
+ See below an example configuration block:
18
+
19
+ ### Example
20
+
21
+ ```ruby
22
+ sources do
23
+ csv :pathname, Pathname.new("loc/to/my/file_1.csv"), ref_name: "file_1"
24
+ csv :pathname, Pathname.new("loc/to/my/file_2.csv"), ref_name: "file_2"
25
+ end
26
+ ```
27
+
28
+ or using Ruby (this is a contrived example)
29
+
30
+ ```ruby
31
+ [Pathname.new("loc/to/my/file_1.csv"), Pathname.new("loc/to/my/file_2.csv")].each_with_index do |file_path, idx|
32
+ sources do
33
+ csv :pathname, file_path, ref_name: "file_#{idx}"
34
+ end
35
+ end
36
+ ```
@@ -0,0 +1,53 @@
1
+ Source > Extract > Validate > Build > Query > **Transform** > Export
2
+
3
+ Transform
4
+ =======
5
+
6
+ ### About
7
+
8
+ The transform step is the arguably the most powerful step in the chain. It is used when you want to *change* a value.
9
+ The process can be undertaken in multiple steps, performing transforms as you go. It is quite sophisticated, and does
10
+ not require homologous data for each value.
11
+
12
+ **When you should use this**: You have a value in a row, and you wish to change it.
13
+
14
+ Transform is made up of many translations. There are two main parts that make up a translations step, the
15
+ **strategy** and the **generator** which are defined on a specific column.
16
+
17
+ **Strategy** is the means in which a value will be transformed. It is responsible for how and what part of the value
18
+ will be transformed. The strategy will first look to extract the value that you wish to transform. This can either
19
+ be simple, like a full replacement, or complex using regex. Once it has the value extracted, it will attempt to find
20
+ any previously translated mapping before running it through the generator step.
21
+
22
+ **Generator** is the process of creating that transformed value. It could be as simple as replace all characters with
23
+ the character "x", fill it with Faker data, set it to a random guid, modify the date format, inject a value from
24
+ elsewhere on the spreadsheet (see [Build](../builder/main.md)).
25
+
26
+ Once this value has been generated, it is stored against the existing value so any other translation will get the same.
27
+ This is particularly useful if you want to keep data integrity. For example, if you had two columns, one was `account
28
+ number`, and the other was `reference_id` (which was constructed by the format `account_number/identifier`). Once you
29
+ had set a replacement value for the account number (`abcde => defgh`), you wouldn't want a different value for the
30
+ account number part of the `reference_id` column.
31
+
32
+ ```
33
+ +----------------+--------------+
34
+ | account_number | reference_id |
35
+ +----------------+--------------+
36
+ | abcde | abcde/123456 |
37
+ +----------------+--------------+
38
+ ```
39
+ changes to
40
+ ```
41
+ +----------------+--------------+
42
+ | account_number | reference_id |
43
+ +----------------+--------------+
44
+ | defgh | defgh/123456 |
45
+ +----------------+--------------+
46
+ ```
47
+
48
+ ---
49
+
50
+ See below an example configuration block:
51
+
52
+ ### Example
53
+
@@ -0,0 +1,42 @@
1
+ Source > Extract > **Validate** > Build > Query > Transform > Export
2
+
3
+ Validate
4
+ =======
5
+
6
+ ### About
7
+
8
+ The validator step checks that data in the spreadsheet is what is expected. This may involve checking
9
+ that data is of a certain format, and if it isn't, it can either fail or warn.
10
+
11
+ ---
12
+
13
+ **When you should use this**: You have a spreadsheet that has data that needs to be in a certain format
14
+ before transforming or exporting.
15
+
16
+ See below an example configuration block:
17
+
18
+ ### Example
19
+
20
+ ```ruby
21
+ validate do
22
+ candidate column: "new_column", named_range: "section_1", options: { fail_on_error: false } do
23
+ with_rule :not_null
24
+ with_rule :length, { min: 0, max: 5 }
25
+ with_rule :custom, { proc: Proc.new { |x| x.size >= 0 && x.size <= 5 } } # Proc version of above.
26
+ end
27
+ end
28
+ ```
29
+
30
+ Original input:
31
+ ```
32
+ +------------+
33
+ | new_column |
34
+ +------------+
35
+ | test | <- Is valid
36
+ | test value | <- Is invalid (too long)
37
+ | | <- Is invalid (null)
38
+ +------------+
39
+ ```
40
+
41
+ As `fail_on_error` is set to false, this would warn that 2 values are invalid.
42
+
data/exe/cure CHANGED
@@ -1,42 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
2
 
4
- require "cure"
5
- require "optparse"
3
+ require "cure/cli/command"
4
+ require "cure/cli/new_command"
5
+ require "cure/cli/run_command"
6
+ require "cure/cli/generate_command"
6
7
 
7
- include Cure::Log
8
-
9
- class CureConfig
10
- include Cure::Configuration
11
- end
12
-
13
- conf = {}
14
-
15
- OptionParser.new do |opts|
16
- opts.banner = "Usage: cure_cli [options]"
17
-
18
- opts.on("-t", "--template_file=template_file", "Template definition file") do |t|
19
- conf[:template_file_location] = t
20
- end
21
-
22
- opts.on("-s", "--source_file=source_file", "Source file") do |s|
23
- conf[:source_file_location] = s
24
- end
25
-
26
- opts.on("-o", "--output_dir=output_dir", "Output directory") do |o|
27
- conf[:output_dir] = o
28
- end
29
-
30
- opts.on("-h", "--help", "Prints this help") do
31
- puts opts
32
- exit
33
- end
34
- end.parse!
35
-
36
- log_info "Config loaded successfully, initialising environment ..."
37
- main = Cure::Main.init(conf[:template_file_location], conf[:source_file_location], conf[:output_dir])
38
-
39
- log_info "... set up complete. Beginning process"
40
- main.process
8
+ short_hand = {
9
+ "n" => "new",
10
+ "r" => "run",
11
+ "g" => "generate"
12
+ }
41
13
 
14
+ command = ARGV.shift
15
+ command = short_hand[command] || command
42
16
 
17
+ Cure::Cli::Command.invoke(command, ARGV)
data/exe/cure.old ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ # rubocop:disable Style/MixinUsage
6
+
7
+ require "cure"
8
+ require "optparse"
9
+
10
+ include Cure::Log
11
+
12
+ class CureConfig
13
+ include Cure::Configuration
14
+ end
15
+
16
+ conf = {}
17
+
18
+ opts_parser = OptionParser.new do |opts|
19
+ opts.banner = "Usage: cure [options]"
20
+
21
+ opts.on("-t", "--template_file=template_file", "Template definition file") do |t|
22
+ conf[:template_file_location] = t
23
+ end
24
+
25
+ opts.on("-s", "--source_file=source_file", "Source file") do |s|
26
+ conf[:source_file_location] = s
27
+ end
28
+
29
+ opts.on("-l", "--list_plugins=list_plugins", "List plugins") do
30
+ ObjectSpace.each_object(Class) do |klass|
31
+ log_info "Generator: #{klass.name}" if klass.ancestors.include?(Cure::Generator::BaseGenerator)
32
+ log_info "Strategy: #{klass.name}" if klass.ancestors.include?(Cure::Strategy::BaseStrategy)
33
+ end
34
+
35
+ exit
36
+ end
37
+
38
+ opts.on("-h", "--help", "Prints this help") do
39
+ puts opts
40
+ exit
41
+ end
42
+ end
43
+
44
+ opts_parser.parse!
45
+
46
+ if conf.empty?
47
+ puts opts_parser
48
+ exit
49
+ end
50
+
51
+ log_info "Config loaded successfully, initialising environment ..."
52
+ main = Cure.init_from_file(conf[:template_file_location])
53
+ .with_csv_file(:pathname, Pathname.new(conf[:source_file_location]))
54
+ .setup
55
+
56
+ log_info "... set up complete. Beginning process"
57
+ main.run_export
58
+
59
+ # rubocop:enable Style/MixinUsage
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/log"
4
+ require "cure/config"
5
+ require "cure/database"
6
+ require "cure/helpers/file_helpers"
7
+ require "cure/extract/extractor"
8
+
9
+ require "rcsv"
10
+
11
+ module Cure
12
+ module Builder
13
+ include Database
14
+
15
+ class BaseBuilder
16
+ include Database
17
+
18
+ # @param [String] named_range
19
+ # @param [String] column
20
+ # @param [Hash] opts
21
+ def initialize(named_range, column, opts)
22
+ @named_range = named_range
23
+ @column = column
24
+ @opts = opts
25
+ end
26
+
27
+ # @return [void]
28
+ def call
29
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
30
+ end
31
+
32
+ # @return [String (frozen)]
33
+ def to_s
34
+ "Base Builder"
35
+ end
36
+
37
+ # @yield [DatabaseService]
38
+ # @raise StandardError
39
+ def with_database(&block)
40
+ raise "Missing block" unless block
41
+
42
+ yield database_service
43
+ end
44
+ end
45
+
46
+ class AddBuilder < BaseBuilder
47
+
48
+ # @return [void]
49
+ def call
50
+ with_database do |db_svc|
51
+ db_svc.add_column(@named_range.to_sym, @column.to_sym, default: @opts.fetch(:default_value, nil))
52
+ end
53
+ end
54
+
55
+ # @return [String (frozen)]
56
+ def to_s
57
+ "Add Builder"
58
+ end
59
+ end
60
+
61
+ class RemoveBuilder < BaseBuilder
62
+
63
+ # @return [void]
64
+ def call
65
+ with_database do |db_svc|
66
+ db_svc.remove_column(@named_range.to_sym, @column.to_sym)
67
+ end
68
+ end
69
+
70
+ # @return [String (frozen)]
71
+ def to_s
72
+ "Remove Builder"
73
+ end
74
+ end
75
+
76
+ class RenameBuilder < BaseBuilder
77
+
78
+ # @return [void]
79
+ def call
80
+ with_database do |db_svc|
81
+ db_svc.rename_column(@named_range.to_sym, @column.to_sym, @opts.fetch("new_name"))
82
+ end
83
+ end
84
+
85
+ # @return [String (frozen)]
86
+ def to_s
87
+ "Rename Builder"
88
+ end
89
+ end
90
+
91
+ class CopyBuilder < BaseBuilder
92
+
93
+ # @return [void]
94
+ def call
95
+ with_database do |db_svc|
96
+ to_column = @opts.fetch(:to_column)
97
+ db_svc.copy_column(
98
+ @named_range.to_sym,
99
+ @column.to_sym,
100
+ to_column
101
+ )
102
+ end
103
+ end
104
+
105
+ # @return [String (frozen)]
106
+ def to_s
107
+ "Copy Builder"
108
+ end
109
+ end
110
+
111
+ class BlacklistBuilder < BaseBuilder
112
+
113
+ # @return [void]
114
+ def call
115
+ @opts[:columns].each do |column|
116
+ with_database do |db_svc|
117
+ db_svc.remove_column(@named_range.to_sym, column.to_sym)
118
+ end
119
+ end
120
+ end
121
+
122
+ # @return [String (frozen)]
123
+ def to_s
124
+ "Blacklist Builder"
125
+ end
126
+ end
127
+
128
+ class WhitelistBuilder < BaseBuilder
129
+
130
+ # @return [void]
131
+ def call
132
+ with_database do |db_svc|
133
+ whitelist_columns = (@opts[:columns]).map(&:to_sym)
134
+ all_columns = db_svc.list_columns(@named_range.to_sym)
135
+
136
+ # Remove cols that aren't defined in white list or sys columns
137
+ candidate_cols = all_columns - whitelist_columns - [:_id]
138
+
139
+ candidate_cols.each do |column|
140
+ db_svc.remove_column(@named_range.to_sym, column.to_sym)
141
+ end
142
+ end
143
+ end
144
+
145
+ # @return [String (frozen)]
146
+ def to_s
147
+ "Whitelist Builder"
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/helpers/object_helpers"
4
+ require "cure/builder/base_builder"
5
+ require "cure/extract/extractor"
6
+ require "cure/log"
7
+
8
+ module Cure
9
+ module Builder
10
+ # Per row, we will have a candidate for each transformation that needs to be made
11
+ class Candidate
12
+ include Helpers::ObjectHelpers
13
+ include Log
14
+
15
+ # Named range that column exists in
16
+ # @return [String]
17
+ attr_reader :named_range
18
+
19
+ # Lookup column name for CSV.
20
+ # @return [String,nil]
21
+ attr_reader :column
22
+
23
+ # What sort of data needs to be generated.
24
+ # @return [Cure::Builder::BaseBuilder]
25
+ attr_reader :action
26
+
27
+ # @param [String,nil] column
28
+ # @param [String] named_range
29
+ def initialize(column, named_range)
30
+ @column = column
31
+ @named_range = named_range || "_default"
32
+ end
33
+
34
+ # @return [void]
35
+ def perform
36
+ @action.call
37
+ end
38
+
39
+ # @param [String,Symbol] _method_name
40
+ # @param [TrueClass,FalseClass] _include_private
41
+ # @return [TrueClass]
42
+ def respond_to_missing?(_method_name, _include_private=false)
43
+ true
44
+ end
45
+
46
+ # @param [Symbol] method_name
47
+ # @return [Cure::Builder::BaseBuilder]
48
+ def method_missing(method_name, args)
49
+ klass_name = "Cure::Builder::#{method_name.to_s.capitalize}Builder"
50
+ raise "#{method_name} is not valid" unless class_exists?(klass_name)
51
+
52
+ @action = Kernel.const_get(klass_name).new(@named_range, @column, args[:options] || {})
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure"
4
+ require "cure/log"
5
+
6
+ module Cure
7
+ module Cli
8
+ class Command
9
+ include Log
10
+
11
+ # @param [String] command
12
+ # @param [Array] argv
13
+ def self.invoke(command, argv)
14
+ extend Log
15
+
16
+ handler = nil
17
+
18
+ case command
19
+ when "new"
20
+ handler = NewCommand.new(argv)
21
+ when "generate"
22
+ handler = GenerateCommand.new(argv)
23
+ when "run"
24
+ handler = RunCommand.new(argv)
25
+ else
26
+ new.help
27
+ return
28
+ end
29
+
30
+ handler.call
31
+ rescue ArgumentError => aex
32
+ handler ? handler.help(ex: aex) : new.help
33
+ rescue StandardError => ex
34
+ log_error(ex.message)
35
+ end
36
+
37
+ # @param [Array] argv
38
+ def initialize(argv = [])
39
+ @argv = argv
40
+ end
41
+
42
+ def call
43
+ validate
44
+ execute
45
+ end
46
+
47
+ def help(ex: nil)
48
+ log_error ex.message if ex && ex.is_a?(StandardError)
49
+ log_error "Error: unknown request"
50
+ log_info "\nUsage: cure <command> [options]"
51
+ log_info "\tRun: cure run -t [template] -f [file]"
52
+ log_info "\tNew Project: cure new [name]"
53
+ end
54
+
55
+ private
56
+
57
+ # @return [void]
58
+ def execute
59
+ raise NotImplementedError
60
+ end
61
+
62
+ # @return [void]
63
+ # @raise [ArgumentError, NotImplementedError]
64
+ def validate
65
+ raise NotImplementedError
66
+ end
67
+
68
+ protected
69
+
70
+
71
+ def make_directory(dir_name)
72
+ log_info "Creating directory #{dir_name}"
73
+
74
+ FileUtils.mkdir_p(dir_name)
75
+ end
76
+
77
+ def make_file(dir_name, file_name, template: nil, binding: nil)
78
+ file = File.join(dir_name, file_name)
79
+
80
+ log_info "Creating file #{file}"
81
+
82
+ unless template
83
+ FileUtils.touch(file)
84
+ return
85
+ end
86
+
87
+ content = retrieve_template(template)
88
+ if binding
89
+ erb = ERB.new(content)
90
+ content = erb.result_with_hash(binding)
91
+ end
92
+
93
+ File.open(file, "w") do |f|
94
+ f.write(content)
95
+ end
96
+ end
97
+
98
+ def retrieve_template(template)
99
+ File.read(
100
+ File.join(File.dirname(__FILE__), "templates", "#{template}.erb")
101
+ )
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/cli/command"
4
+
5
+ # TODO: Generate script - inc File and init and Run export.
6
+
7
+ module Cure
8
+ module Cli
9
+ class GenerateCommand < Command
10
+ def initialize(args)
11
+ super(args)
12
+ end
13
+
14
+ def help(ex: nil)
15
+ log_error ex.message if ex && ex.is_a?(StandardError)
16
+ log_info "\nUsage: cure generate template [name]"
17
+ end
18
+
19
+ private
20
+
21
+ def execute
22
+ raise "You are not in a Cure project! Cannot create template." unless cure_project?
23
+
24
+ log_info "Creating new template: #{params[:name]}"
25
+
26
+ root_dir = File.join(Dir.pwd, "templates")
27
+ make_file(root_dir, "#{params[:name]}_template.rb", template: "new_template.rb")
28
+ end
29
+
30
+ def validate
31
+ # Theres a nicer way to do this, I'll do it later.
32
+ raise ArgumentError, "Missing arguments" if @argv.empty?
33
+ raise ArgumentError, "Invalid operation #{@argv.first}" if @argv.first != "template"
34
+ raise ArgumentError, "Invalid name #{@argv.last}" if @argv.last.nil? || @argv.last.empty?
35
+ end
36
+
37
+ attr_reader :params
38
+
39
+ def params
40
+ {
41
+ operation: @argv.first,
42
+ name: @argv.last
43
+ }
44
+ end
45
+
46
+ # Naive implementation, could do better. Who cares for now.
47
+ # @return [TrueClass, FalseClass]
48
+ def cure_project?
49
+ gf = File.join(Dir.pwd, 'Gemfile')
50
+ File.exist?(gf) && File.read(gf).include?('cure')
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/version"
4
+ require "cure/cli/command"
5
+
6
+ require "fileutils"
7
+ require "erb"
8
+
9
+ module Cure
10
+ module Cli
11
+ class NewCommand < Command
12
+ def initialize(argv)
13
+ super(argv)
14
+ end
15
+
16
+ def help(ex: nil)
17
+ log_error ex.message if ex && ex.is_a?(StandardError)
18
+ log_info "\nUsage: cure new [name]"
19
+ end
20
+
21
+ private
22
+
23
+ def execute
24
+ root_dir = File.join(Dir.pwd, @argv[0])
25
+
26
+ raise "Project already exists in directory!" if File.exist?(root_dir)
27
+
28
+ log_info "Creating new project: #{@argv[0]}"
29
+
30
+ make_directory(root_dir)
31
+ make_directory(File.join(root_dir, "input"))
32
+ make_directory(File.join(root_dir, "output"))
33
+ make_directory(File.join(root_dir, "scripts"))
34
+ make_directory(File.join(root_dir, "templates"))
35
+ make_directory(File.join(root_dir, "utilities"))
36
+
37
+ make_file(root_dir, ".gitignore", template: "gitignore")
38
+ make_file(root_dir, ".tool-versions", template: "tool-versions")
39
+ make_file(root_dir, "README.md", template: "README.md")
40
+ make_file(root_dir, "Gemfile", template: "gemfile", binding: { version: Cure::VERSION })
41
+ make_file("#{root_dir}/input", ".gitkeep")
42
+ make_file("#{root_dir}/output", ".gitkeep")
43
+ end
44
+
45
+ # @return [void]
46
+ # @raise [ArgumentError]
47
+ def validate
48
+ raise ArgumentError, "Error: No project name given" if @argv.empty?
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/cli/command"
4
+
5
+ module Cure
6
+ module Cli
7
+ class RunCommand < Command
8
+ def initialize(args)
9
+ super(args)
10
+ end
11
+
12
+ private
13
+
14
+ def execute
15
+ log_info "Not implemented yet!"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1 @@
1
+ ## New Cure Project
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "cure", "~> <%= version %>"