cuprum-cli 0.1.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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +34 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/LICENSE +21 -0
  5. data/README.md +163 -0
  6. data/lib/cuprum/cli/argument.rb +172 -0
  7. data/lib/cuprum/cli/arguments/class_methods.rb +283 -0
  8. data/lib/cuprum/cli/arguments.rb +16 -0
  9. data/lib/cuprum/cli/coercion.rb +131 -0
  10. data/lib/cuprum/cli/command.rb +102 -0
  11. data/lib/cuprum/cli/commands/ci/report.rb +121 -0
  12. data/lib/cuprum/cli/commands/ci/rspec_command.rb +108 -0
  13. data/lib/cuprum/cli/commands/ci/rspec_each_command.rb +185 -0
  14. data/lib/cuprum/cli/commands/ci.rb +12 -0
  15. data/lib/cuprum/cli/commands/echo_command.rb +76 -0
  16. data/lib/cuprum/cli/commands/file/generate_file.rb +141 -0
  17. data/lib/cuprum/cli/commands/file/new_command.rb +86 -0
  18. data/lib/cuprum/cli/commands/file/render_erb.rb +88 -0
  19. data/lib/cuprum/cli/commands/file/resolve_template.rb +136 -0
  20. data/lib/cuprum/cli/commands/file/templates/rspec.rb.erb +14 -0
  21. data/lib/cuprum/cli/commands/file/templates/ruby.rb.erb +29 -0
  22. data/lib/cuprum/cli/commands/file/templates.rb +71 -0
  23. data/lib/cuprum/cli/commands/file.rb +14 -0
  24. data/lib/cuprum/cli/commands.rb +12 -0
  25. data/lib/cuprum/cli/dependencies/file_system/mock.rb +297 -0
  26. data/lib/cuprum/cli/dependencies/file_system.rb +247 -0
  27. data/lib/cuprum/cli/dependencies/standard_io/helpers.rb +138 -0
  28. data/lib/cuprum/cli/dependencies/standard_io/mock.rb +85 -0
  29. data/lib/cuprum/cli/dependencies/standard_io.rb +110 -0
  30. data/lib/cuprum/cli/dependencies/system_command/mock.rb +57 -0
  31. data/lib/cuprum/cli/dependencies/system_command.rb +147 -0
  32. data/lib/cuprum/cli/dependencies.rb +25 -0
  33. data/lib/cuprum/cli/errors/files/file_not_writeable.rb +42 -0
  34. data/lib/cuprum/cli/errors/files/missing_parameter.rb +71 -0
  35. data/lib/cuprum/cli/errors/files/missing_template.rb +36 -0
  36. data/lib/cuprum/cli/errors/files/template_error.rb +37 -0
  37. data/lib/cuprum/cli/errors/files/template_not_resolved.rb +54 -0
  38. data/lib/cuprum/cli/errors/files.rb +19 -0
  39. data/lib/cuprum/cli/errors/system_command_failure.rb +44 -0
  40. data/lib/cuprum/cli/errors.rb +11 -0
  41. data/lib/cuprum/cli/integrations/thor/arguments_parser.rb +99 -0
  42. data/lib/cuprum/cli/integrations/thor/registry.rb +42 -0
  43. data/lib/cuprum/cli/integrations/thor/task.rb +211 -0
  44. data/lib/cuprum/cli/integrations/thor.rb +14 -0
  45. data/lib/cuprum/cli/integrations.rb +8 -0
  46. data/lib/cuprum/cli/metadata.rb +215 -0
  47. data/lib/cuprum/cli/option.rb +165 -0
  48. data/lib/cuprum/cli/options/class_methods.rb +232 -0
  49. data/lib/cuprum/cli/options/quiet.rb +32 -0
  50. data/lib/cuprum/cli/options/verbose.rb +32 -0
  51. data/lib/cuprum/cli/options.rb +18 -0
  52. data/lib/cuprum/cli/registry.rb +141 -0
  53. data/lib/cuprum/cli/rspec/deferred/arguments_examples.rb +203 -0
  54. data/lib/cuprum/cli/rspec/deferred/ci/report_examples.rb +450 -0
  55. data/lib/cuprum/cli/rspec/deferred/ci.rb +8 -0
  56. data/lib/cuprum/cli/rspec/deferred/dependencies/file_system_examples.rb +1469 -0
  57. data/lib/cuprum/cli/rspec/deferred/dependencies.rb +8 -0
  58. data/lib/cuprum/cli/rspec/deferred/metadata_examples.rb +856 -0
  59. data/lib/cuprum/cli/rspec/deferred/options_examples.rb +234 -0
  60. data/lib/cuprum/cli/rspec/deferred/registry_examples.rb +451 -0
  61. data/lib/cuprum/cli/rspec/deferred.rb +8 -0
  62. data/lib/cuprum/cli/rspec.rb +8 -0
  63. data/lib/cuprum/cli/version.rb +59 -0
  64. data/lib/cuprum/cli.rb +47 -0
  65. metadata +173 -0
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/cli'
4
+ require 'cuprum/cli/options'
5
+
6
+ module Cuprum::Cli # rubocop:disable Metrics/ModuleLength
7
+ # Data object representing a command option.
8
+ Option = Data.define(
9
+ :aliases,
10
+ :default,
11
+ :description,
12
+ :name,
13
+ :parameter_name,
14
+ :required,
15
+ :type,
16
+ :variadic
17
+ ) do
18
+ # @param name [String, Symbol] the name of the option.
19
+ # @param aliases [Array<String, Symbol>] aliases for the option when parsing
20
+ # options from the command line.
21
+ # @param default [Object, Proc] the default value for the option. If given
22
+ # and the value of the option is nil, sets the option value to the default
23
+ # value.
24
+ # @param description [String] a short, human-readable description of the
25
+ # option.
26
+ # @param parameter_name [String] a representation of the possible values for
27
+ # the option.
28
+ # @param required [true, false] if true, raises an exception if the option
29
+ # is not provided to the command.
30
+ # @param type [Class, String, Symbol] the expected type of the option value
31
+ # as a Class or class name. If given, raises an exception if the option
32
+ # value is not an instance of the type. Defaults to :string.
33
+ # @param variadic [true, false] if true, the option is variadic and
34
+ # represents an hash of options provided to the command. Defaults to
35
+ # false.
36
+ def initialize( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
37
+ name:,
38
+ aliases: [],
39
+ default: nil,
40
+ description: nil,
41
+ parameter_name: nil,
42
+ required: false,
43
+ type: :string,
44
+ variadic: false
45
+ )
46
+ name = name.to_sym
47
+ aliases = Array(aliases).compact.map { |obj| obj.to_s.tr('_', '-') }
48
+ required = required ? true : false
49
+ type = type.to_sym if type.is_a?(String)
50
+ variadic = variadic ? true : false
51
+
52
+ super(
53
+ aliases:,
54
+ default:,
55
+ description:,
56
+ name:,
57
+ parameter_name:,
58
+ required:,
59
+ type:,
60
+ variadic:
61
+ )
62
+ end
63
+
64
+ alias_method :required?, :required
65
+
66
+ alias_method :variadic?, :variadic
67
+
68
+ # @overload def resolve(value)
69
+ # Validates the value for the current option.
70
+ #
71
+ # If the value is nil, applies the option default (if any).
72
+ #
73
+ # @param value [Object] the value to validate.
74
+ #
75
+ # @return [Object] the validated option value.
76
+ #
77
+ # @raise [Cuprum::Cli::Options::InvalidOptionError] if the value is
78
+ # missing (for a required option) or invalid.
79
+ def resolve(original_value)
80
+ value = original_value
81
+ value = default_value if blank?(value)
82
+ value = value.to_s if value.is_a?(Symbol)
83
+
84
+ return default_value_for_type if value.nil? && !required?
85
+ return value if valid_option?(value)
86
+
87
+ raise Cuprum::Cli::Options::InvalidOptionError,
88
+ invalid_option_message(original_value)
89
+ end
90
+
91
+ private
92
+
93
+ def blank?(value)
94
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
95
+ end
96
+
97
+ def default_value
98
+ default.is_a?(Proc) ? default.call : default
99
+ end
100
+
101
+ def default_value_for_type
102
+ return {} if variadic?
103
+
104
+ return false if type == :boolean && !required?
105
+
106
+ nil
107
+ end
108
+
109
+ def expected_hash_type
110
+ message = required? ? 'a non-empty Hash of' : 'a Hash of'
111
+
112
+ return "#{message} true or false values" if type == :boolean
113
+
114
+ plural_type =
115
+ tools.string_tools.pluralize(type.is_a?(Class) ? type.name : type)
116
+
117
+ "#{message} #{tools.string_tools.camelize(plural_type)}"
118
+ end
119
+
120
+ def expected_type
121
+ return expected_hash_type if variadic?
122
+
123
+ case type
124
+ when :boolean
125
+ 'true or false'
126
+ when Class
127
+ "an instance of #{type.name}"
128
+ else
129
+ "an instance of #{tools.string_tools.camelize(type.to_s)}"
130
+ end
131
+ end
132
+
133
+ def invalid_option_message(value)
134
+ "invalid value for option :#{name} - expected #{expected_type}, " \
135
+ "received #{value.inspect}"
136
+ end
137
+
138
+ def tools
139
+ SleepingKingStudios::Tools::Toolbelt.instance
140
+ end
141
+
142
+ def valid_option?(value)
143
+ return validate_option(value) unless variadic?
144
+
145
+ return false unless value.is_a?(Hash)
146
+ return false if required? && value.empty?
147
+
148
+ value.each_value.all? { |item| validate_option(item) }
149
+ end
150
+
151
+ def validate_option(value) # rubocop:disable Naming/PredicateMethod
152
+ case type
153
+ when :boolean
154
+ value == true || value == false # rubocop:disable Style/MultipleComparison
155
+ when Class
156
+ value.is_a?(type)
157
+ else
158
+ expected = tools.string_tools.camelize(type.to_s)
159
+ expected = Object.const_get(expected)
160
+
161
+ value.is_a?(expected)
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'weakref'
4
+
5
+ require 'cuprum/cli/option'
6
+ require 'cuprum/cli/options'
7
+
8
+ module Cuprum::Cli::Options
9
+ # Methods used to extend command class functionality for defining options.
10
+ module ClassMethods
11
+ # Helper class for defining command options.
12
+ class Builder
13
+ # @param command_class [Class] the command class.
14
+ # @param defined_options [Hash{Symbol => Cuprum::Cli::Option}] the options
15
+ # defined for the command.
16
+ def initialize(command_class:, defined_options:)
17
+ @command_class = command_class
18
+ @defined_options = defined_options
19
+ end
20
+
21
+ # @return [Class] the command class.
22
+ attr_reader :command_class
23
+
24
+ # @return [Hash{Symbol => Cuprum::Cli::Option}] the options
25
+ # defined for the command.
26
+ attr_reader :defined_options
27
+
28
+ # (see Cuprum::Cli::Options::ClassMethods#option)
29
+ def call(name, define_method: nil, define_predicate: nil, **options)
30
+ option = Cuprum::Cli::Option.new(name:, **options)
31
+
32
+ defined_options[option.name] = option
33
+
34
+ boolean_option = options[:type] == :boolean && !options[:variadic]
35
+
36
+ define_method = !boolean_option if define_method.nil?
37
+ define_predicate = boolean_option if define_predicate.nil?
38
+
39
+ define_method_for(option) if define_method
40
+ define_predicate_for(option) if define_predicate
41
+
42
+ option.name
43
+ end
44
+
45
+ private
46
+
47
+ def define_method_for(option)
48
+ command_class.define_method(option.name) { @options[option.name] }
49
+ end
50
+
51
+ def define_predicate_for(option)
52
+ command_class.define_method(:"#{option.name}?") do
53
+ value = @options[option.name]
54
+
55
+ return false if value.nil? || value == false
56
+
57
+ !(value.respond_to?(:empty?) && value.empty?)
58
+ end
59
+ end
60
+ end
61
+
62
+ # @overload option(name, aliases: [], default: nil, description: nil, required: false, type: :string, **options)
63
+ # Defines an option for the command class.
64
+ #
65
+ # @param name [String, Symbol] the name of the option.
66
+ # @param aliases [Array<String, Symbol>] aliases for the option when
67
+ # parsing options from the command line.
68
+ # @param default [Object, Proc] the default value for the option. If given
69
+ # and the value of the option is nil, sets the option value to the
70
+ # default value.
71
+ # @param description [String] a short, human-readable description of the
72
+ # option.
73
+ # @param required [true, false] if true, raises an exception if the option
74
+ # is not provided to the command.
75
+ # @param type [Class, String, Symbol] the expected type of the option
76
+ # value as a Class or class name. If given, raises an exception if the
77
+ # option value is not an instance of the type. Defaults to :string.
78
+ # @param options [Hash] additional options for defining the option.
79
+ #
80
+ # @option options define_method [true, false] if true, defines a reader
81
+ # method for the option. Defaults to false for boolean options and true
82
+ # for all other options.
83
+ # @option options define_predicate [true, false] if true, defines a
84
+ # predicate method for the option, which returns true if the option is
85
+ # not nil and not empty. Defaults to true for boolean options and false
86
+ # for all other options.
87
+ def option(name, define_method: nil, define_predicate: nil, **)
88
+ handle_multiple_variadic_options(**)
89
+
90
+ options_builder
91
+ .call(name, define_method:, define_predicate:, **)
92
+ end
93
+
94
+ # Assigns a predefined option value for the command.
95
+ #
96
+ # @param option [String, Symbol] the option to set.
97
+ # @param value [Object] the option value to append.
98
+ #
99
+ # @return [void]
100
+ def option_value(option, value)
101
+ defined_option_values[option.to_sym] = value
102
+
103
+ nil
104
+ end
105
+
106
+ # @return [Hash{Symbol => Object}] predefined option values for the command.
107
+ def option_values = inherited_option_values.merge(defined_option_values)
108
+
109
+ # The defined options, including options defined on ancestor classes.
110
+ #
111
+ # @return [Hash{Symbol => Cuprum::Cli::Option}] the defined options.
112
+ def options
113
+ ancestors.reduce({}) do |hsh, ancestor|
114
+ return hsh if ancestor == Cuprum::Cli::Command
115
+ next hsh unless ancestor.respond_to?(:defined_options, true)
116
+
117
+ hsh.merge(ancestor.defined_options)
118
+ end
119
+ end
120
+
121
+ # Validates the given option values against the defined class options.
122
+ #
123
+ # Also applies any default values from the defined options.
124
+ #
125
+ # @param values [Hash] the option values to resolve.
126
+ #
127
+ # @return [Hash] the option values with applied defaults.
128
+ #
129
+ # @raise [Cuprum::Cli::Options::UnknownOptionError] if any value does not
130
+ # have a corresponding defined option.
131
+ # @raise [Cuprum::Cli::Options::InvalidOptionError] if any value does not
132
+ # match the expected option type, or any required value is missing.
133
+ def resolve_options(**values)
134
+ values = option_values.merge(values)
135
+ defined_options = options
136
+ values, unknown_options = resolve_variadic_values(values)
137
+
138
+ unless unknown_options.empty?
139
+ raise Cuprum::Cli::Options::UnknownOptionError,
140
+ unknown_options_message(defined_options:, unknown_options:)
141
+ end
142
+
143
+ defined_options.to_h do |key, option|
144
+ [key, option.resolve(values[key])]
145
+ end
146
+ end
147
+
148
+ protected
149
+
150
+ def defined_options
151
+ @defined_options ||= {}
152
+ end
153
+
154
+ def defined_option_values = @defined_option_values ||= {}
155
+
156
+ def inherited_option_values
157
+ return {} unless superclass.respond_to?(:inherited_option_values, true)
158
+
159
+ superclass.inherited_option_values.merge(superclass.defined_option_values)
160
+ end
161
+
162
+ private
163
+
164
+ def handle_multiple_variadic_options(variadic: false, **)
165
+ return unless variadic
166
+
167
+ matching = defined_options.each_value.find(&:variadic?)
168
+
169
+ return unless matching
170
+
171
+ message = "command already defines variadic option :#{matching.name}"
172
+
173
+ raise ArgumentError, message
174
+ end
175
+
176
+ def options_builder
177
+ (
178
+ @options_builder ||= WeakRef.new(
179
+ Builder.new(command_class: self, defined_options:)
180
+ )
181
+ ).__getobj__
182
+ rescue RefError
183
+ # :nocov:
184
+ @options_builder = WeakRef.new(
185
+ Builder.new(command_class: self, defined_options:)
186
+ )
187
+ @arguments_builder.__getobj__
188
+ # :nocov:
189
+ end
190
+
191
+ def resolve_variadic_values(values) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
192
+ defined_options = options
193
+ unknown_options = values.keys - options.keys
194
+
195
+ return [values, unknown_options] if unknown_options.empty?
196
+
197
+ variadic_key = defined_options.find { |_, value| value.variadic? }&.first
198
+
199
+ return [values, unknown_options] unless variadic_key
200
+
201
+ variadic_value = values.fetch(variadic_key, {})
202
+
203
+ unless variadic_value.is_a?(Hash)
204
+ return [values.except(*unknown_options), []]
205
+ end
206
+
207
+ variadic_value = values.slice(*unknown_options).merge(variadic_value)
208
+
209
+ values =
210
+ values.except(*unknown_options).merge(variadic_key => variadic_value)
211
+
212
+ [values, []]
213
+ end
214
+
215
+ def tools
216
+ SleepingKingStudios::Tools::Toolbelt.instance
217
+ end
218
+
219
+ def unknown_options_message(defined_options:, unknown_options:)
220
+ counted =
221
+ tools.integer_tools.pluralize(unknown_options.size, 'option')
222
+ unknown = unknown_options.map(&:inspect).join(', ')
223
+ message = "unrecognized #{counted} #{unknown} for #{name}"
224
+
225
+ return message if defined_options.empty?
226
+
227
+ valid_options = defined_options.keys.sort.map(&:inspect).join(', ')
228
+
229
+ "#{message} - valid options are #{valid_options}"
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/cli/options'
4
+
5
+ module Cuprum::Cli::Options
6
+ # Defines --quiet option, which silences non-essential output.
7
+ module Quiet
8
+ DESCRIPTION = 'Silences non-essential console outputs.'
9
+ private_constant :DESCRIPTION
10
+
11
+ class << self
12
+ private
13
+
14
+ def included(other)
15
+ super
16
+
17
+ other.option :quiet,
18
+ type: :boolean,
19
+ aliases: :q,
20
+ default: false,
21
+ description: DESCRIPTION
22
+ end
23
+ end
24
+
25
+ # (see Cuprum::Cli::Dependencies::StandardIo::Helpers#say)
26
+ def say(message, quiet: false, **)
27
+ return if options.fetch(:quiet, false) && !quiet
28
+
29
+ super
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/cli/options'
4
+
5
+ module Cuprum::Cli::Options
6
+ # Defines --verbose option, which enables optional console outputs.
7
+ module Verbose
8
+ DESCRIPTION = 'Enables optional console outputs.'
9
+ private_constant :DESCRIPTION
10
+
11
+ class << self
12
+ private
13
+
14
+ def included(other)
15
+ super
16
+
17
+ other.option :verbose,
18
+ type: :boolean,
19
+ aliases: :v,
20
+ default: false,
21
+ description: DESCRIPTION
22
+ end
23
+ end
24
+
25
+ # (see Cuprum::Cli::Dependencies::StandardIo::Helpers#say)
26
+ def say(message, verbose: false, **)
27
+ return if verbose && !options.fetch(:verbose, false)
28
+
29
+ super
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/cli'
4
+
5
+ module Cuprum::Cli
6
+ # Namespace for functionality that implements command options.
7
+ module Options
8
+ autoload :ClassMethods, 'cuprum/cli/options/class_methods'
9
+ autoload :Quiet, 'cuprum/cli/options/quiet'
10
+ autoload :Verbose, 'cuprum/cli/options/verbose'
11
+
12
+ # Exception raised when a command receives an invalid value for an option.
13
+ class InvalidOptionError < StandardError; end
14
+
15
+ # Exception raised when a command receives an unrecognized option.
16
+ class UnknownOptionError < StandardError; end
17
+ end
18
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/cli'
4
+
5
+ module Cuprum::Cli
6
+ # Registers CLI commands by name.
7
+ class Registry
8
+ # @return [Class, nil] the command registered with the given name, if any.
9
+ def [](name)
10
+ tools.assertions.validate_name(name, as: 'full_name')
11
+
12
+ registered_commands[name]
13
+ end
14
+
15
+ # Returns a copy of the commands registered with the registry.
16
+ #
17
+ # @return [Hash{String => Class}] the registered commands.
18
+ def commands
19
+ registered_commands.dup.freeze
20
+ end
21
+
22
+ # Registers the command with the registry.
23
+ #
24
+ # @param command [Class] the command class to register.
25
+ # @param config [Hash] options for configuring the command.
26
+ #
27
+ # @option config arguments [Array] arguments to pass to the command on
28
+ # initialization.
29
+ # @option config description [String] the description for the command.
30
+ # @option config full_description [String] the full description for the
31
+ # command.
32
+ # @option config full_name [String] the name under which to register the
33
+ # command. Defaults to the value of command.full_name.
34
+ # @option config options [Hash] options to pass to the command on
35
+ # initialization.
36
+ #
37
+ # @raise [NameError] if a command is already registered with that name.
38
+ #
39
+ # @return [self]
40
+ def register(command, **config)
41
+ validate_command(command)
42
+
43
+ name = config.fetch(:full_name, command.full_name)
44
+
45
+ validate_name(name)
46
+
47
+ command = build_command(command, **config) if any_present?(config)
48
+
49
+ registered_commands[name] = command
50
+
51
+ self
52
+ end
53
+ alias add register
54
+
55
+ private
56
+
57
+ def any_present?(config)
58
+ return false if config.empty?
59
+
60
+ config.each_value.any? { |value| present?(value) }
61
+ end
62
+
63
+ def build_command( # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/ParameterLists
64
+ command,
65
+ arguments: nil,
66
+ description: nil,
67
+ full_description: nil,
68
+ full_name: nil,
69
+ options: nil,
70
+ **
71
+ )
72
+ Class.new(command).tap do |command_class|
73
+ command_class.description(description) if present?(description)
74
+
75
+ command_class.full_name(full_name) if present?(full_name)
76
+
77
+ if present?(full_description)
78
+ command_class.full_description(full_description)
79
+ end
80
+
81
+ arguments&.each do |argument|
82
+ command_class.argument_value(argument)
83
+ end
84
+
85
+ options&.each do |option, value|
86
+ command_class.option_value(option, value)
87
+ end
88
+ end
89
+ end
90
+
91
+ def command_name(command)
92
+ command
93
+ .ancestors
94
+ .find { |ancestor| ancestor.is_a?(Class) && ancestor.name }
95
+ .name
96
+ end
97
+
98
+ def invalid_name_format_message
99
+ 'full_name does not match format category:sub_category:do_something'
100
+ end
101
+
102
+ def present?(value)
103
+ return false if value.nil?
104
+
105
+ return false unless value.respond_to?(:empty?) && !value.empty?
106
+
107
+ true
108
+ end
109
+
110
+ def registered_commands = @registered_commands ||= {}
111
+
112
+ def tools = SleepingKingStudios::Tools::Toolbelt.instance
113
+
114
+ def validate_command(command)
115
+ tools.assertions.validate_class(command, as: 'command')
116
+ tools.assertions.validate_inherits_from(
117
+ command,
118
+ as: 'command',
119
+ expected: Cuprum::Cli::Command
120
+ )
121
+ end
122
+
123
+ def validate_name(name) # rubocop:disable Metrics/MethodLength
124
+ tools.assertions.validate_name(name, as: 'full_name')
125
+ tools.assertions.validate_matches(
126
+ name,
127
+ as: 'full_name',
128
+ expected: Cuprum::Cli::Metadata::FULL_NAME_FORMAT,
129
+ message: invalid_name_format_message
130
+ )
131
+
132
+ return unless registered_commands.key?(name)
133
+
134
+ command = registered_commands[name]
135
+ message =
136
+ "command already registered as #{name} - #{command_name(command)}"
137
+
138
+ raise NameError, message
139
+ end
140
+ end
141
+ end