cuprum-cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +10 -0
- data/lib/cuprum/cli/argument.rb +2 -2
- data/lib/cuprum/cli/command.rb +2 -4
- data/lib/cuprum/cli/commands/ci/rspec_each_command.rb +34 -8
- data/lib/cuprum/cli/commands/ci.rb +8 -0
- data/lib/cuprum/cli/commands/file/new_command.rb +42 -49
- data/lib/cuprum/cli/commands/file.rb +8 -5
- data/lib/cuprum/cli/dependencies/clock/mock.rb +28 -0
- data/lib/cuprum/cli/dependencies/clock.rb +29 -0
- data/lib/cuprum/cli/dependencies/file_system/mock.rb +150 -18
- data/lib/cuprum/cli/dependencies/file_system.rb +145 -10
- data/lib/cuprum/cli/dependencies.rb +2 -0
- data/lib/cuprum/cli/errors.rb +0 -1
- data/lib/cuprum/cli/{commands/file/generate_file.rb → files/create_file.rb} +6 -48
- data/lib/cuprum/cli/{commands/file → files/engines}/render_erb.rb +6 -6
- data/lib/cuprum/cli/files/engines.rb +76 -0
- data/lib/cuprum/cli/{errors/files → files/errors}/file_not_writeable.rb +3 -3
- data/lib/cuprum/cli/files/errors/generator_error.rb +46 -0
- data/lib/cuprum/cli/{errors/files → files/errors}/missing_parameter.rb +5 -5
- data/lib/cuprum/cli/{errors/files → files/errors}/missing_template.rb +3 -3
- data/lib/cuprum/cli/{errors/files → files/errors}/template_error.rb +3 -3
- data/lib/cuprum/cli/files/errors.rb +19 -0
- data/lib/cuprum/cli/files/generator.rb +431 -0
- data/lib/cuprum/cli/files/generators/basic_generator.rb +15 -0
- data/lib/cuprum/cli/files/generators/rspec_generator.rb +19 -0
- data/lib/cuprum/cli/files/generators/ruby_generator.rb +32 -0
- data/lib/cuprum/cli/files/generators.rb +25 -0
- data/lib/cuprum/cli/files/template.rb +62 -0
- data/lib/cuprum/cli/files/templates/basic_template.rb +39 -0
- data/lib/cuprum/cli/files/templates/file_template.rb +60 -0
- data/lib/cuprum/cli/files/templates/string_template.rb +29 -0
- data/lib/cuprum/cli/files/templates.rb +59 -0
- data/lib/cuprum/cli/files.rb +18 -0
- data/lib/cuprum/cli/integrations/async/commands/rspec_each_command.rb +32 -0
- data/lib/cuprum/cli/integrations/async/commands.rb +11 -0
- data/lib/cuprum/cli/integrations/async.rb +21 -0
- data/lib/cuprum/cli/integrations/thor/registry.rb +7 -29
- data/lib/cuprum/cli/integrations/thor/task.rb +37 -7
- data/lib/cuprum/cli/metadata.rb +54 -5
- data/lib/cuprum/cli/option.rb +10 -4
- data/lib/cuprum/cli/options/class_methods.rb +19 -1
- data/lib/cuprum/cli/registry.rb +131 -55
- data/lib/cuprum/cli/rspec/deferred/dependencies/file_system_examples.rb +1303 -1001
- data/lib/cuprum/cli/rspec/deferred/generators_examples.rb +112 -0
- data/lib/cuprum/cli/rspec/deferred/metadata_examples.rb +300 -0
- data/lib/cuprum/cli/rspec/deferred/options_examples.rb +8 -2
- data/lib/cuprum/cli/rspec/deferred/registry_examples.rb +343 -108
- data/lib/cuprum/cli/rspec/deferred/templates_examples.rb +183 -0
- data/lib/cuprum/cli/version.rb +1 -1
- data/lib/cuprum/cli.rb +1 -0
- metadata +31 -14
- data/lib/cuprum/cli/commands/file/resolve_template.rb +0 -136
- data/lib/cuprum/cli/commands/file/templates.rb +0 -71
- data/lib/cuprum/cli/errors/files/template_not_resolved.rb +0 -54
- data/lib/cuprum/cli/errors/files.rb +0 -19
- /data/lib/cuprum/cli/{commands/file → files}/templates/rspec.rb.erb +0 -0
- /data/lib/cuprum/cli/{commands/file → files}/templates/ruby.rb.erb +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cuprum/cli/files'
|
|
4
|
+
|
|
5
|
+
module Cuprum::Cli::Files
|
|
6
|
+
# Namespace for commands that process raw templates into file contents.
|
|
7
|
+
module Engines
|
|
8
|
+
class << self
|
|
9
|
+
# @overload fetch(engine)
|
|
10
|
+
# Retrieves the render command for the given engine.
|
|
11
|
+
#
|
|
12
|
+
# @param engine [String] the engine identifier.
|
|
13
|
+
#
|
|
14
|
+
# @return [Cuprum::Command] the render command for the engine.
|
|
15
|
+
#
|
|
16
|
+
# @raise [KeyError] if there is no matching engine.
|
|
17
|
+
#
|
|
18
|
+
# @overload fetch(engine, default)
|
|
19
|
+
# Retrieves the render command for the given engine.
|
|
20
|
+
#
|
|
21
|
+
# @param engine [String] the engine identifier.
|
|
22
|
+
# @param default [Cuprum::Command] the command to return if there is no
|
|
23
|
+
# matching engine.
|
|
24
|
+
#
|
|
25
|
+
# @return [Cuprum::Command] the render command for the engine.
|
|
26
|
+
#
|
|
27
|
+
# @overload fetch(engine) { |engine| }
|
|
28
|
+
# Retrieves the render command for the given engine.
|
|
29
|
+
#
|
|
30
|
+
# If there is no matching engine. calls the block with the given engine.
|
|
31
|
+
#
|
|
32
|
+
# @param engine [String] the engine identifier.
|
|
33
|
+
#
|
|
34
|
+
# @return [Cuprum::Command] the render command for the engine.
|
|
35
|
+
#
|
|
36
|
+
# @yieldparam engine [String] the engine identifier.
|
|
37
|
+
#
|
|
38
|
+
# @yieldreturn [Cuprum::Command] the command to return if there is no
|
|
39
|
+
# matching engine.
|
|
40
|
+
def fetch(engine, default = UNDEFINED, &)
|
|
41
|
+
return engines.fetch(engine, &) if default == UNDEFINED
|
|
42
|
+
|
|
43
|
+
engines.fetch(engine, default)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Registers a render command for the specified engine.
|
|
47
|
+
#
|
|
48
|
+
# @param engine [String] the engine identifier.
|
|
49
|
+
# @param render_command [Cuprum::Command] the render command for the
|
|
50
|
+
# engine.
|
|
51
|
+
#
|
|
52
|
+
# @return [void]
|
|
53
|
+
def register(engine, render_command)
|
|
54
|
+
engines[engine] = render_command
|
|
55
|
+
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def default_engines
|
|
62
|
+
{ ERB => Cuprum::Cli::Files::Engines::RenderErb }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def engines = @engines ||= default_engines
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
autoload :RenderErb, 'cuprum/cli/files/engines/render_erb'
|
|
69
|
+
|
|
70
|
+
# Identifier for ERB files.
|
|
71
|
+
ERB = 'cuprum.cli.files.engines.erb'
|
|
72
|
+
|
|
73
|
+
UNDEFINED = SleepingKingStudios::Tools::UNDEFINED
|
|
74
|
+
private_constant :UNDEFINED
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require 'cuprum/error'
|
|
4
4
|
|
|
5
|
-
require 'cuprum/cli/errors
|
|
5
|
+
require 'cuprum/cli/files/errors'
|
|
6
6
|
|
|
7
|
-
module Cuprum::Cli::Errors
|
|
7
|
+
module Cuprum::Cli::Files::Errors
|
|
8
8
|
# Error returned when attempting to generate a file in an invalid location.
|
|
9
9
|
class FileNotWriteable < Cuprum::Error
|
|
10
10
|
# Short string used to identify the type of error.
|
|
11
|
-
TYPE = 'cuprum.cli.errors.
|
|
11
|
+
TYPE = 'cuprum.cli.files.errors.file_not_writeable'
|
|
12
12
|
|
|
13
13
|
# @param file_path [String] the path to the expected file.
|
|
14
14
|
# @param message [String] message describing the nature of the error.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cuprum/cli/files/errors'
|
|
4
|
+
|
|
5
|
+
module Cuprum::Cli::Files::Errors
|
|
6
|
+
# Error returned when an error occurs when calling a generator.
|
|
7
|
+
class GeneratorError < Cuprum::Error
|
|
8
|
+
# Short string used to identify the type of error.
|
|
9
|
+
TYPE = 'cuprum.cli.files.errors.generator_error'
|
|
10
|
+
|
|
11
|
+
# @param message [String] message describing the nature of the error.
|
|
12
|
+
# @param details [String] additional information about the error.
|
|
13
|
+
# @param file_path [String] the file path passed to the generator.
|
|
14
|
+
# @param options [Hash] additional options passed to the generator.
|
|
15
|
+
def initialize(message:, details: nil, file_path: nil, options: {}, **)
|
|
16
|
+
@details = details
|
|
17
|
+
@file_path = file_path
|
|
18
|
+
@options = options
|
|
19
|
+
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] additional information about the error.
|
|
24
|
+
attr_reader :details
|
|
25
|
+
|
|
26
|
+
# @return [String] the file path passed to the generator.
|
|
27
|
+
attr_reader :file_path
|
|
28
|
+
|
|
29
|
+
# @return [Hash] additional options passed to the generator.
|
|
30
|
+
attr_reader :options
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def as_json_data
|
|
35
|
+
options = self.options.empty? ? nil : self.options.transform_keys(&:to_s)
|
|
36
|
+
|
|
37
|
+
super
|
|
38
|
+
.merge(
|
|
39
|
+
'details' => details,
|
|
40
|
+
'file_path' => file_path,
|
|
41
|
+
'options' => options
|
|
42
|
+
)
|
|
43
|
+
.compact
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'cuprum/cli/errors
|
|
4
|
-
require 'cuprum/cli/errors/
|
|
3
|
+
require 'cuprum/cli/files/errors'
|
|
4
|
+
require 'cuprum/cli/files/errors/template_error'
|
|
5
5
|
|
|
6
|
-
module Cuprum::Cli::Errors
|
|
6
|
+
module Cuprum::Cli::Files::Errors
|
|
7
7
|
# Error returned when a required parameter is missing when rendering content.
|
|
8
|
-
class MissingParameter < Cuprum::Cli::Errors::
|
|
8
|
+
class MissingParameter < Cuprum::Cli::Files::Errors::TemplateError
|
|
9
9
|
# Short string used to identify the type of error.
|
|
10
|
-
TYPE = 'cuprum.cli.errors.
|
|
10
|
+
TYPE = 'cuprum.cli.files.errors.missing_parameter'
|
|
11
11
|
|
|
12
12
|
# @param parameter_name [String, Symbol] the name of the missing parameter.
|
|
13
13
|
# @param details [String] additional information about the error.
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require 'cuprum/error'
|
|
4
4
|
|
|
5
|
-
require 'cuprum/cli/errors
|
|
5
|
+
require 'cuprum/cli/files/errors'
|
|
6
6
|
|
|
7
|
-
module Cuprum::Cli::Errors
|
|
7
|
+
module Cuprum::Cli::Files::Errors
|
|
8
8
|
# Error returned when unable to load a template.
|
|
9
9
|
class MissingTemplate < Cuprum::Error
|
|
10
10
|
# Short string used to identify the type of error.
|
|
11
|
-
TYPE = 'cuprum.cli.errors.
|
|
11
|
+
TYPE = 'cuprum.cli.files.errors.missing_template'
|
|
12
12
|
|
|
13
13
|
# @param template_path [String] the expected template path.
|
|
14
14
|
# @param message [String] an optional message to display.
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require 'cuprum/error'
|
|
4
4
|
|
|
5
|
-
require 'cuprum/cli/errors
|
|
5
|
+
require 'cuprum/cli/files/errors'
|
|
6
6
|
|
|
7
|
-
module Cuprum::Cli::Errors
|
|
7
|
+
module Cuprum::Cli::Files::Errors
|
|
8
8
|
# Error returned when an error occurs when generating templated content.
|
|
9
9
|
class TemplateError < Cuprum::Error
|
|
10
10
|
# Short string used to identify the type of error.
|
|
11
|
-
TYPE = 'cuprum.cli.errors.
|
|
11
|
+
TYPE = 'cuprum.cli.files.errors.template_error'
|
|
12
12
|
|
|
13
13
|
# @param message [String] message describing the nature of the error.
|
|
14
14
|
# @param details [String] additional information about the error.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cuprum/cli/files'
|
|
4
|
+
|
|
5
|
+
module Cuprum::Cli::Files
|
|
6
|
+
# Namespace for errors returned when creating or managing files.
|
|
7
|
+
module Errors
|
|
8
|
+
autoload :FileNotWriteable,
|
|
9
|
+
'cuprum/cli/files/errors/file_not_writeable'
|
|
10
|
+
autoload :GeneratorError,
|
|
11
|
+
'cuprum/cli/files/errors/generator_error'
|
|
12
|
+
autoload :MissingParameter,
|
|
13
|
+
'cuprum/cli/files/errors/missing_parameter'
|
|
14
|
+
autoload :MissingTemplate,
|
|
15
|
+
'cuprum/cli/files/errors/missing_template'
|
|
16
|
+
autoload :TemplateError,
|
|
17
|
+
'cuprum/cli/files/errors/template_error'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cuprum'
|
|
4
|
+
require 'plumbum'
|
|
5
|
+
|
|
6
|
+
require 'cuprum/cli/files'
|
|
7
|
+
|
|
8
|
+
module Cuprum::Cli::Files
|
|
9
|
+
# Command for generating templated files.
|
|
10
|
+
class Generator < Cuprum::Command # rubocop:disable Metrics/ClassLength
|
|
11
|
+
include Plumbum::Consumer
|
|
12
|
+
prepend Plumbum::Parameters
|
|
13
|
+
include Cuprum::Cli::Dependencies::StandardIo::Helpers
|
|
14
|
+
extend Cuprum::Cli::Options::ClassMethods
|
|
15
|
+
include Cuprum::Cli::Options::Quiet
|
|
16
|
+
include Cuprum::Cli::Options::Verbose
|
|
17
|
+
|
|
18
|
+
# Raised when performing a protected operation on an abstract Generator.
|
|
19
|
+
class AbstractGeneratorError < StandardError; end
|
|
20
|
+
|
|
21
|
+
# Raised when performing a protected operation on an abstract Generator.
|
|
22
|
+
class OutputAlreadyExistsError < KeyError; end
|
|
23
|
+
|
|
24
|
+
# Data class representing a configured output file.
|
|
25
|
+
Output = Data.define(:key, :path, :template)
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
# Marks the generator as abstract.
|
|
29
|
+
def abstract = @abstract = true
|
|
30
|
+
|
|
31
|
+
# @return [true, false] true if the generator is abstract and should not
|
|
32
|
+
# be assigned matchers or outputs; otherwise false.
|
|
33
|
+
def abstract? = @abstract.nil? ? false : @abstract
|
|
34
|
+
|
|
35
|
+
# @overload match_file(pattern)
|
|
36
|
+
# Registers the pattern as a valid match for the generator.
|
|
37
|
+
#
|
|
38
|
+
# The registered matches are checked when the generator checks whether a
|
|
39
|
+
# file path can be generated by the generator.
|
|
40
|
+
#
|
|
41
|
+
# - If the given pattern is a regular expression, the generator will
|
|
42
|
+
# match the pattern against the file path.
|
|
43
|
+
# - If the pattern is a string, the generator will check if the file
|
|
44
|
+
# path ends with the pattern string.
|
|
45
|
+
#
|
|
46
|
+
# @param pattern [Regexp, String] the pattern to register.
|
|
47
|
+
#
|
|
48
|
+
# @return [void]
|
|
49
|
+
#
|
|
50
|
+
# @overload match_file(&block)
|
|
51
|
+
# Registers the block as a valid match for the generator.
|
|
52
|
+
#
|
|
53
|
+
# The registered matches are checked when the generator checks whether a
|
|
54
|
+
# file path can be generated by the generator. The given block will be
|
|
55
|
+
# called with the file path and generator options; if the block returns
|
|
56
|
+
# a truthy value, the match check returns true.
|
|
57
|
+
#
|
|
58
|
+
# @yieldparam file_path [String] the input file path provided by the
|
|
59
|
+
# user.
|
|
60
|
+
# @yieldparam options [Hash] additional options for the generator.
|
|
61
|
+
#
|
|
62
|
+
# @return [void]
|
|
63
|
+
def match_file(pattern = nil, &block)
|
|
64
|
+
if abstract?
|
|
65
|
+
raise AbstractGeneratorError,
|
|
66
|
+
abstract_generator_message('define matcher')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
own_matchers << (block_given? ? block : pattern)
|
|
70
|
+
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @overload matches?(file_path, **options)
|
|
75
|
+
# Checks if the generator matches the given file path and options.
|
|
76
|
+
#
|
|
77
|
+
# @param file_path [String] the input file path provided by the user.
|
|
78
|
+
# @param options [Hash] additional options for the generator.
|
|
79
|
+
#
|
|
80
|
+
# @return [true, false] true if the generator can generate files with
|
|
81
|
+
# the given file type, path, and options; otherwise false.
|
|
82
|
+
def matches?(file_path, **)
|
|
83
|
+
matchers.any? do |matcher|
|
|
84
|
+
case matcher
|
|
85
|
+
when Proc then matcher.call(file_path, **)
|
|
86
|
+
when Regexp then matcher.match?(file_path)
|
|
87
|
+
when String then file_path.end_with?(matcher)
|
|
88
|
+
else
|
|
89
|
+
# :nocov:
|
|
90
|
+
false
|
|
91
|
+
# :nocov:
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
alias match? matches?
|
|
96
|
+
|
|
97
|
+
# Registers an output file for the generator.
|
|
98
|
+
#
|
|
99
|
+
# The output path parameter is a String that can include format strings,
|
|
100
|
+
# which will be resolved from the options passed to the generator. In
|
|
101
|
+
# addition, the format strings can reference the following parameters
|
|
102
|
+
# derived from the input file_path. The following examples use an input
|
|
103
|
+
# file path of "lib/path/to/file.rb":
|
|
104
|
+
#
|
|
105
|
+
# - base_name: The last segment of the file path, including the file
|
|
106
|
+
# extension. Example: "file.rb".
|
|
107
|
+
# - dir_name: The directory path from the file path, relative to the
|
|
108
|
+
# working directory. Example: "lib/path/to".
|
|
109
|
+
# - ext_name: The extension of the file path, including the leading
|
|
110
|
+
# period character. Example: ".rb".
|
|
111
|
+
# - file_path: The full file path. Example: "lib/path/to/file.rb".
|
|
112
|
+
# - relative_path: The *second and later* segments of the directory path,
|
|
113
|
+
# or an empty String if the path is too short. Example: "path/to".
|
|
114
|
+
# - root_path: The *first* segment of the directory path, or an empty
|
|
115
|
+
# String if the path is too short. Example: "lib".
|
|
116
|
+
# - short_name: The last segment of the file path, excluding the file
|
|
117
|
+
# extension. Example: "file".
|
|
118
|
+
#
|
|
119
|
+
# @param path [String] the file path for the generated file.
|
|
120
|
+
# @param key [String, Symbol] a unique key used to identify the output.
|
|
121
|
+
# Defaults to :default.
|
|
122
|
+
# @param template [String] the path to the template file for the output.
|
|
123
|
+
# If not provided, the user must provide a template for that output.
|
|
124
|
+
#
|
|
125
|
+
# @return [void]
|
|
126
|
+
def output(path, key: :default, template: nil) # rubocop:disable Metrics/MethodLength
|
|
127
|
+
key = key.to_sym
|
|
128
|
+
|
|
129
|
+
if abstract?
|
|
130
|
+
raise AbstractGeneratorError,
|
|
131
|
+
abstract_generator_message("define output #{key.inspect}")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if own_outputs.key?(key)
|
|
135
|
+
raise OutputAlreadyExistsError, output_already_exists_message(key)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
template = resolve_template(template)
|
|
139
|
+
|
|
140
|
+
own_outputs[key] = Output.new(key:, path:, template:)
|
|
141
|
+
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# @return [Hash{Symbol => Output}] the configured outputs for the
|
|
146
|
+
# generator.
|
|
147
|
+
def outputs
|
|
148
|
+
return {} if abstract?
|
|
149
|
+
|
|
150
|
+
superclass.outputs.merge(own_outputs)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
protected
|
|
154
|
+
|
|
155
|
+
def matchers
|
|
156
|
+
return [] if abstract?
|
|
157
|
+
|
|
158
|
+
superclass.matchers.concat(own_matchers)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def own_matchers = @own_matchers ||= []
|
|
162
|
+
|
|
163
|
+
def own_outputs = @own_outputs ||= {}
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def abstract_generator_message(short_message)
|
|
168
|
+
class_name =
|
|
169
|
+
ancestors
|
|
170
|
+
.find { |ancestor| ancestor.is_a?(Class) && ancestor.name }
|
|
171
|
+
.name
|
|
172
|
+
|
|
173
|
+
"unable to #{short_message} - #{class_name} is an abstract class"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def output_already_exists_message(key)
|
|
177
|
+
class_name =
|
|
178
|
+
ancestors
|
|
179
|
+
.find { |ancestor| ancestor.is_a?(Class) && ancestor.name }
|
|
180
|
+
.name
|
|
181
|
+
|
|
182
|
+
"unable to define output #{key.inspect} - #{class_name} already " \
|
|
183
|
+
"defines output #{key.inspect}"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def resolve_template(maybe_template)
|
|
187
|
+
return if maybe_template.nil?
|
|
188
|
+
|
|
189
|
+
Cuprum::Cli::Files::Templates.resolve(maybe_template)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
abstract
|
|
194
|
+
|
|
195
|
+
provider Cuprum::Cli::Dependencies.provider
|
|
196
|
+
|
|
197
|
+
dependency :file_system
|
|
198
|
+
dependency :standard_io
|
|
199
|
+
|
|
200
|
+
option :directories, type: :boolean, default: true
|
|
201
|
+
option :dry_run, type: :boolean
|
|
202
|
+
|
|
203
|
+
# @overload initialize(file_path, **options)
|
|
204
|
+
# @param file_path [String, nil] the input file path provided by the user.
|
|
205
|
+
# @param options [Hash] additional options for the generator.
|
|
206
|
+
#
|
|
207
|
+
# @option options dry_run [true, false] if true, simulates file generation
|
|
208
|
+
# but does not perform the actual file system operations. Defaults to
|
|
209
|
+
# false.
|
|
210
|
+
# @option options quiet [true, false] if true, does not print generated
|
|
211
|
+
# file names status to STDOUT. Defaults to false.
|
|
212
|
+
# @option options verbose [true, false] if true, prints the contents of
|
|
213
|
+
# generated files to STDOUT. Defaults to false.
|
|
214
|
+
def initialize(file_path = nil, **)
|
|
215
|
+
super()
|
|
216
|
+
|
|
217
|
+
@file_path = file_path
|
|
218
|
+
@options = self.class.resolve_options(**)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# @return [String] the input file path provided by the user.
|
|
222
|
+
attr_reader :file_path
|
|
223
|
+
|
|
224
|
+
# @return [Hash] additional options for the generator.
|
|
225
|
+
attr_reader :options
|
|
226
|
+
|
|
227
|
+
# @return [Hash] parameters extracted from the input file name.
|
|
228
|
+
def file_parameters
|
|
229
|
+
@file_parameters ||=
|
|
230
|
+
if file_path
|
|
231
|
+
extract_file_parameters(file_path)
|
|
232
|
+
else
|
|
233
|
+
{}
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# @return [Hash] parameters used to resolve output file paths and contents.
|
|
238
|
+
def parameters
|
|
239
|
+
file_parameters.merge(options)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
private
|
|
243
|
+
|
|
244
|
+
def build_file_template(template_path)
|
|
245
|
+
Cuprum::Cli::Files::Templates::FileTemplate.build(template_path)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def check_if_file_already_exists(file_path:)
|
|
249
|
+
return unless file_system.file?(file_path)
|
|
250
|
+
|
|
251
|
+
error =
|
|
252
|
+
file_not_writeable_error(file_path:, reason: 'file already exists')
|
|
253
|
+
|
|
254
|
+
failure(error)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def check_if_file_is_directory(file_path:)
|
|
258
|
+
return unless file_system.directory?(file_path)
|
|
259
|
+
|
|
260
|
+
error =
|
|
261
|
+
file_not_writeable_error(file_path:, reason: 'file is a directory')
|
|
262
|
+
|
|
263
|
+
failure(error)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def create_file(contents:, file_path:)
|
|
267
|
+
Cuprum::Cli::Files::CreateFile
|
|
268
|
+
.new(
|
|
269
|
+
directories: directories?,
|
|
270
|
+
dry_run: dry_run?,
|
|
271
|
+
file_system:
|
|
272
|
+
)
|
|
273
|
+
.call(contents:, file_path:)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def duplicate_file_error(file_path:, key:)
|
|
277
|
+
details = "multiple outputs writing to output path #{file_path.inspect}"
|
|
278
|
+
|
|
279
|
+
generator_error(details:, key:)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def extract_file_parameters(file_path) # rubocop:disable Metrics/MethodLength
|
|
283
|
+
base_name = File.basename(file_path)
|
|
284
|
+
segments = file_path.split(File::SEPARATOR)
|
|
285
|
+
|
|
286
|
+
{
|
|
287
|
+
base_name:,
|
|
288
|
+
dir_name: File.dirname(file_path),
|
|
289
|
+
ext_name: File.extname(file_path),
|
|
290
|
+
file_path:,
|
|
291
|
+
relative_path: segments[1...-1].join(File::SEPARATOR),
|
|
292
|
+
root_path: segments[0...-1].first || '',
|
|
293
|
+
short_name: base_name.split('.').first
|
|
294
|
+
}
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def file_not_writeable_error(**)
|
|
298
|
+
Cuprum::Cli::Files::Errors::FileNotWriteable.new(**)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def filter_outputs # rubocop:disable Metrics/MethodLength
|
|
302
|
+
outputs = self.class.outputs
|
|
303
|
+
|
|
304
|
+
if outputs.empty?
|
|
305
|
+
error = generator_error(
|
|
306
|
+
details: 'generator does not define any outputs'
|
|
307
|
+
)
|
|
308
|
+
return failure(error)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
outputs = outputs.reject { |key, _| options[key] == false }
|
|
312
|
+
|
|
313
|
+
return outputs unless outputs.empty?
|
|
314
|
+
|
|
315
|
+
error = generator_error(details: 'all outputs have been disabled')
|
|
316
|
+
failure(error)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def generate_file(contents:, file_path:)
|
|
320
|
+
say "Generating file #{file_path}..."
|
|
321
|
+
|
|
322
|
+
report_file_contents(contents)
|
|
323
|
+
|
|
324
|
+
step { create_file(contents:, file_path:) }
|
|
325
|
+
|
|
326
|
+
file_path
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def generate_contents(outputs) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
330
|
+
outputs.each_value.with_object({}) do |output, hsh|
|
|
331
|
+
file_path = step { resolve_output_path(output) }
|
|
332
|
+
|
|
333
|
+
if hsh.key?(file_path)
|
|
334
|
+
return failure(duplicate_file_error(file_path:, key: output.key))
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
template = step { template_for(output) }
|
|
338
|
+
contents = step { render_template(template:) }
|
|
339
|
+
|
|
340
|
+
step { check_if_file_is_directory(file_path:) }
|
|
341
|
+
step { check_if_file_already_exists(file_path:) }
|
|
342
|
+
|
|
343
|
+
hsh[file_path] = contents
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def generator_error(details: nil, key: nil)
|
|
348
|
+
message = 'unable to generate output file'
|
|
349
|
+
message = "#{message}#{key ? " #{key.inspect}" : 's'}"
|
|
350
|
+
message = "#{message} - #{details}" if details
|
|
351
|
+
|
|
352
|
+
Cuprum::Cli::Files::Errors::GeneratorError.new(
|
|
353
|
+
details:,
|
|
354
|
+
file_path:,
|
|
355
|
+
message:,
|
|
356
|
+
options:
|
|
357
|
+
)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def process
|
|
361
|
+
outputs = step { filter_outputs }
|
|
362
|
+
|
|
363
|
+
generated_files = step { generate_contents(outputs) }
|
|
364
|
+
|
|
365
|
+
generated_files.each do |file_path, contents|
|
|
366
|
+
step { generate_file(contents:, file_path:) }
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
generated_files.keys
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def render_template(template:)
|
|
373
|
+
if template.members.include?(:file_system)
|
|
374
|
+
template = template.with(file_system:)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
template.call(**parameters)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def report_file_contents(contents)
|
|
381
|
+
say "\n", verbose: true
|
|
382
|
+
say(
|
|
383
|
+
contents
|
|
384
|
+
.each_line
|
|
385
|
+
.map { |line| line == "\n" ? "\n" : " #{line}" }.join,
|
|
386
|
+
verbose: true
|
|
387
|
+
)
|
|
388
|
+
say "\n", verbose: true
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def resolve_output_path(output) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
392
|
+
format(output.path, parameters.compact).gsub(%r{//+}, '/')
|
|
393
|
+
rescue ArgumentError => exception
|
|
394
|
+
raise unless exception.message.include?('malformed format string')
|
|
395
|
+
|
|
396
|
+
details = "#{exception.message} for output path #{output.path}"
|
|
397
|
+
error = generator_error(details:, key: output.key)
|
|
398
|
+
failure(error)
|
|
399
|
+
rescue KeyError => exception
|
|
400
|
+
details =
|
|
401
|
+
"missing parameter #{exception.key.inspect} for output path " \
|
|
402
|
+
"#{output.path}"
|
|
403
|
+
error = generator_error(details:, key: output.key)
|
|
404
|
+
failure(error)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def template_for(output)
|
|
408
|
+
template_path = template_path_from_options(output)
|
|
409
|
+
|
|
410
|
+
unless template_path.nil? || template_path.empty?
|
|
411
|
+
return build_file_template(template_path)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
return output.template if output.template
|
|
415
|
+
|
|
416
|
+
error = generator_error(
|
|
417
|
+
details: 'output does not define a template path',
|
|
418
|
+
key: output.key
|
|
419
|
+
)
|
|
420
|
+
failure(error)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def template_path_from_options(output)
|
|
424
|
+
if output.key == :default && options.key?(:template)
|
|
425
|
+
return options[:template]
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
options[:"#{output.key}_template"]
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cuprum/cli/files/generators'
|
|
4
|
+
|
|
5
|
+
module Cuprum::Cli::Files::Generators
|
|
6
|
+
# Generator for creating a file with specified file path and contents.
|
|
7
|
+
class BasicGenerator < Cuprum::Cli::Files::Generator
|
|
8
|
+
option :contents, type: :string, required: true
|
|
9
|
+
|
|
10
|
+
option :file_path, type: :string, required: true
|
|
11
|
+
|
|
12
|
+
output '%<file_path>s',
|
|
13
|
+
template: Cuprum::Cli::Files::Templates::BasicTemplate.new
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cuprum/cli/files/generators'
|
|
4
|
+
|
|
5
|
+
module Cuprum::Cli::Files::Generators
|
|
6
|
+
# Generator for creating an RSpec source file.
|
|
7
|
+
class RSpecGenerator < Cuprum::Cli::Files::Generator
|
|
8
|
+
match_file(/_spec\.rb\z/)
|
|
9
|
+
|
|
10
|
+
option :rspec_template
|
|
11
|
+
|
|
12
|
+
output '%<file_path>s',
|
|
13
|
+
key: :rspec,
|
|
14
|
+
template: File.join(
|
|
15
|
+
Cuprum::Cli::Files::Generators::TEMPLATES_PATH,
|
|
16
|
+
'rspec.rb.erb'
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|