thor_enhance 0.4.0 → 0.5.1

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +36 -8
  5. data/README.md +4 -2
  6. data/bin/thor_enhance +22 -0
  7. data/docs/autogenerate/Readme.md +166 -0
  8. data/generated_readme/commands/Readme.md +34 -0
  9. data/generated_readme/commands/autobots.md +69 -0
  10. data/generated_readme/commands/sample.md +84 -0
  11. data/generated_readme/commands/sub/Readme.md +37 -0
  12. data/generated_readme/commands/sub/innard.md +46 -0
  13. data/lib/thor_enhance/autogenerate/command.rb +334 -0
  14. data/lib/thor_enhance/autogenerate/configuration.rb +72 -0
  15. data/lib/thor_enhance/autogenerate/option.rb +44 -0
  16. data/lib/thor_enhance/autogenerate/templates/aggregate_options.rb.erb +11 -0
  17. data/lib/thor_enhance/autogenerate/templates/class_options.rb.erb +7 -0
  18. data/lib/thor_enhance/autogenerate/templates/command.rb.erb +54 -0
  19. data/lib/thor_enhance/autogenerate/templates/footer.rb.erb +2 -0
  20. data/lib/thor_enhance/autogenerate/templates/option.rb.erb +14 -0
  21. data/lib/thor_enhance/autogenerate/templates/root.rb.erb +9 -0
  22. data/lib/thor_enhance/autogenerate/validate.rb +95 -0
  23. data/lib/thor_enhance/autogenerate.rb +87 -0
  24. data/lib/thor_enhance/command_method.rb +42 -6
  25. data/lib/thor_enhance/configuration.rb +47 -4
  26. data/lib/thor_enhance/option.rb +0 -2
  27. data/lib/thor_enhance/sample.rb +58 -0
  28. data/lib/thor_enhance/thor_auto_generate_inject.rb +64 -0
  29. data/lib/thor_enhance/tree.rb +4 -2
  30. data/lib/thor_enhance/version.rb +1 -1
  31. data/lib/thor_enhance.rb +12 -4
  32. data/thor_enhance.gemspec +1 -0
  33. metadata +36 -2
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/inflections"
4
+ require "thor_enhance/autogenerate/option"
5
+ require "erb"
6
+
7
+ module ThorEnhance
8
+ module Autogenerate
9
+ class Command
10
+ COMMAND_ERB = "#{File.dirname(__FILE__)}/templates/command.rb.erb"
11
+ COMMAND_TEMPLATE = ERB.new(File.read(COMMAND_ERB))
12
+
13
+ AGGREGATE_OPTIONS_ERB = "#{File.dirname(__FILE__)}/templates/aggregate_options.rb.erb"
14
+ AGGREGATE_OPTIONS_TEMPLATE = ERB.new(File.read(AGGREGATE_OPTIONS_ERB))
15
+
16
+ CLASS_OPTIONS_ERB = "#{File.dirname(__FILE__)}/templates/class_options.rb.erb"
17
+ CLASS_OPTIONS_TEMPLATE = ERB.new(File.read(CLASS_OPTIONS_ERB))
18
+
19
+ FOOTER_ERB = "#{File.dirname(__FILE__)}/templates/footer.rb.erb"
20
+ FOOTER_TEMPLATE = ERB.new(File.read(FOOTER_ERB))
21
+
22
+ attr_reader :root, :leaf, :name, :basename, :child_commands, :parent
23
+
24
+ def initialize(leaf:, name:, basename:, root: , parent: nil)
25
+ @leaf = leaf
26
+ @name = name
27
+ @basename = basename
28
+ @child_commands = []
29
+ @parent = parent
30
+ @root = root
31
+ initialize_children!
32
+ end
33
+
34
+ def initialize_children!
35
+ return unless children?
36
+
37
+ @child_commands = leaf.children.map do |name, child_leaf|
38
+ self.class.new(root: root, leaf: child_leaf, name: name, basename: basename, parent: self)
39
+ end
40
+ end
41
+
42
+ def method_options
43
+ @method_options ||= begin
44
+ _options = options.map { |name, option| Option.new(name: name, option: option) }
45
+ _options = _options.group_by { _1.readme_type }
46
+ unless _options.empty?
47
+ _options.delete(ThorEnhance.configuration.autogenerated_config.readme_skip_key)
48
+ end
49
+
50
+ _options
51
+ end
52
+ end
53
+
54
+ def command_erb
55
+ @command_erb ||= begin
56
+ params = {
57
+ all_bases: all_bases,
58
+ basename_string: basename_string,
59
+ children_descriptors: children_descriptors,
60
+ class_options_erb: class_options_erb,
61
+ command: command,
62
+ command_source: command_source,
63
+ custom_headers: custom_headers,
64
+ default_command: default_command,
65
+ default_command_string: default_command_string,
66
+ description: description,
67
+ drawn_out_examples: drawn_out_examples,
68
+ footer_erb: footer_erb,
69
+ headers: headers,
70
+ method_options_erb: method_options_erb,
71
+ parent_basename_string: parent_basename_string,
72
+ preferred_basename_string: preferred_basename_string,
73
+ title: title,
74
+ }
75
+ COMMAND_TEMPLATE.result_with_hash(params)
76
+ end
77
+ end
78
+
79
+ def command_source
80
+ # if children exists, it is a subcommand
81
+ # source location for a command does not accurately find the correct source
82
+ # Return nil and skip output
83
+ return nil if children?
84
+
85
+ file, line = leaf.base.instance_method(name).source_location
86
+ # This value returns the length of the root up to generated_readme
87
+ # This allows us to understand how many directories to remove from the local `source_location`
88
+ remove_length = root.split("/")[0..-2].length
89
+
90
+ # this will return the relative location of the command source line
91
+ relative_source = file.split("/")[remove_length..-1]
92
+ relative_link = "/#{relative_source.join("/")}#L#{line}"
93
+ "Source code for this command can be found at: [#{leaf.base.name}##{name}](#{relative_link})"
94
+ end
95
+
96
+ def footer_erb
97
+ @footer_erb ||= begin
98
+ regenerate_thor_command = "#{basename} thor_enhance_autogenerate --apply"
99
+ FOOTER_TEMPLATE.result_with_hash({ regenerate_thor_command: regenerate_thor_command })
100
+ end
101
+ end
102
+
103
+ def drawn_out_examples(with_desc: true)
104
+ case command.example
105
+ when nil
106
+ when Array
107
+ command.example.map do |example|
108
+ value = []
109
+ value << "# #{example[:arguments][:kwargs][:desc]}" if with_desc
110
+ value << "#{parent_basename_string} #{example[:input]}"
111
+ value.join("\n")
112
+ end
113
+ else
114
+ value = []
115
+ value << "# #{example[:arguments][:kwargs][:desc]}" if with_desc
116
+ value << "#{parent_basename_string} #{example[:input]}"
117
+ [value.join("\n")]
118
+ end
119
+ end
120
+
121
+ def method_options_erb
122
+ @method_options_erb ||= AGGREGATE_OPTIONS_TEMPLATE.result_with_hash({ method_options: method_options })
123
+ end
124
+
125
+ def class_options_erb
126
+ @class_options_erb ||= begin
127
+ if class_options.empty?
128
+ nil
129
+ else
130
+ CLASS_OPTIONS_TEMPLATE.result_with_hash({ method_options_text_array: class_options.map(&:template_text) })
131
+ end
132
+ end
133
+ end
134
+
135
+ def class_options
136
+ leaf.base.class_options.map do |name, class_option|
137
+ class_option.hide ? nil : Option.new(name: name, option: class_option)
138
+ end.compact
139
+ end
140
+
141
+ def basename_string
142
+ "#{parent_basename_string} #{command.usage}"
143
+ end
144
+
145
+ def default_command_string
146
+ parent_basename_string if name == default_command
147
+ end
148
+
149
+ def all_bases
150
+ [default_command_string, basename_string].compact
151
+ end
152
+
153
+ def preferred_basename_string
154
+ default_command_string || basename_string
155
+ end
156
+
157
+ def parent_basename_string
158
+ parent_names = [basename]
159
+ temp_leaf = leaf
160
+ while parent = temp_leaf.parent
161
+ temp_leaf = parent
162
+ parent_names << parent.command.usage
163
+ end
164
+ parent_names.join(" ")
165
+ end
166
+
167
+ def parent_root
168
+ if parent
169
+ # Remove the last index of parent because that will be the Readme.md file
170
+ # We just want the directory of the parent file
171
+ parent.relative_readme_path[0..-2]
172
+ else
173
+ []
174
+ end
175
+ end
176
+
177
+ def relative_readme_path
178
+ if children?
179
+ # If children exist, this is a subcommand and needs to be a root ReadMe
180
+ [*parent_root, name, "Readme.md"]
181
+ else
182
+ [*parent_root, "#{name}.md"]
183
+ end
184
+ end
185
+
186
+ # this only returns children and its children
187
+ # Call this on top most parent to retreive family tree for subcommands
188
+ def flatten_children
189
+ return [] if child_commands.empty?
190
+
191
+ child_commands.map do |child|
192
+ [child, child.flatten_children]
193
+ end.flatten
194
+ end
195
+
196
+ def save_self!(root:, apply:)
197
+ absolute_path = "#{root}/#{relative_readme_path.join("/")}"
198
+ pathname = Pathname.new(absolute_path)
199
+ FileUtils.mkdir_p(pathname.dirname)
200
+ if File.exist?(absolute_path)
201
+ content = File.read(absolute_path)
202
+ diff = command_erb == content ? :same : :overwite
203
+ else
204
+ diff = :new
205
+ end
206
+
207
+ if apply
208
+ File.write(absolute_path, command_erb)
209
+ end
210
+
211
+ { path: absolute_path, diff: diff, apply: apply, self_for_root: self_for_root }
212
+ end
213
+
214
+ def description
215
+ command.long_description || command.description
216
+ end
217
+
218
+ def title
219
+ command.title&.dig(:input) || command.usage
220
+ end
221
+
222
+ def self_for_root
223
+ params_for_child(self)
224
+ end
225
+
226
+ def default_command
227
+ if children?
228
+ leaf.base.subcommand_classes[name].default_task
229
+ else
230
+ leaf.base.default_task
231
+ end
232
+ rescue
233
+ nil
234
+ end
235
+
236
+ private
237
+
238
+ def custom_headers
239
+ ThorEnhance.configuration.autogenerated_config.custom_headers.map do |header|
240
+ next unless command.respond_to?(header.to_sym)
241
+
242
+ header_value = command.public_send(header.to_sym)
243
+ next if header_value.nil?
244
+ case header_value
245
+ when Array
246
+ header_value.map { header_string(_1, header) }
247
+ else
248
+ header_string(header_value, header)
249
+ end
250
+
251
+ end.compact.flatten.join("\n\n")
252
+ end
253
+
254
+ def header_string(header_value, header)
255
+ header_name = header.to_s.gsub(/[_-]/, " ").titlecase
256
+ header_name += "?" if ThorEnhance.configuration.autogenerated_config.question_headers.include?(header)
257
+ header_tag = decipher_header_tag(header_value[:arguments][:kwargs][:tag])
258
+ header_input = header_value[:input]
259
+ "#{header_tag} #{header_name}\n\n#{header_input}"
260
+ end
261
+
262
+ def decipher_header_tag(input)
263
+ case input
264
+ when String
265
+ begin
266
+ input[0..1][-1].to_i.to_s == input[0..1][-1] ? "#" * input[0..1][-1].to_i : "##"
267
+ rescue
268
+ "##"
269
+ end
270
+ when Integer
271
+ "#" * input
272
+ else
273
+ "##"
274
+ end
275
+ end
276
+
277
+ def children_descriptors
278
+ default = child_commands.map { params_for_child(_1) }.select { _1[:default_command] }
279
+ not_default = child_commands.map { params_for_child(_1) }.select { !_1[:default_command] }
280
+ default + not_default
281
+ end
282
+
283
+ def params_for_child(child)
284
+ relative_link = find_iterations_back(child)
285
+ link = child.relative_readme_path[relative_link..-1].join("/")
286
+ {
287
+ all_bases: child.all_bases,
288
+ basename_string: child.basename_string,
289
+ default_command: child.name == default_command,
290
+ default_command_string: default_command_string,
291
+ description: child.description,
292
+ examples: child.drawn_out_examples(with_desc: false) || [],
293
+ link: link,
294
+ preferred_basename_string: child.preferred_basename_string,
295
+ title: child.title,
296
+ }
297
+ end
298
+
299
+ def find_iterations_back(child)
300
+ # if parent is not present, then start from 0th position
301
+ return 0 if child.parent.nil?
302
+ # When there is no children, just take the last
303
+ return -1 unless child.leaf.children?
304
+
305
+ # when parent exists, find out how many parents there are
306
+ # Return how many parents there are
307
+ iterations = -1
308
+ temp_child = child
309
+ while temp = temp_child.parent
310
+ iterations -= 1
311
+ temp_child = temp_child.parent
312
+ end
313
+
314
+ iterations
315
+ end
316
+
317
+ def headers
318
+ (command.header || []).map { _1[:arguments][:kwargs] }
319
+ end
320
+
321
+ def children?
322
+ leaf.children?
323
+ end
324
+
325
+ def command
326
+ leaf.command
327
+ end
328
+
329
+ def options
330
+ command.options.reject { |name, option| option.hide}
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThorEnhance
4
+ module Autogenerate
5
+ class Configuration
6
+ attr_reader :question_headers, :custom_headers, :configuration, :readme_empty_group, :readme_skip_key, :readme_enums
7
+
8
+ DEFAULT_SKIP_KEY = :skip
9
+
10
+ def initialize(required: false)
11
+ @required = required
12
+ @configuration = { add_option_enhance: {}, add_command_method_enhance: {} }
13
+ @readme_enums = []
14
+ @custom_headers = []
15
+ @question_headers = []
16
+ end
17
+
18
+ def set_default_required(value)
19
+ @required = value
20
+ end
21
+
22
+ def default
23
+ ThorEnhance::Configuration.allow_changes?
24
+
25
+ example
26
+ header
27
+ title
28
+ readme
29
+ end
30
+
31
+ def title(required: false)
32
+ ThorEnhance::Configuration.allow_changes?
33
+
34
+ required = required.nil? ? @required : required
35
+ configuration[:add_command_method_enhance][:title] = { repeatable: false, required: required }
36
+ end
37
+
38
+ def example(required: nil, repeatable: true)
39
+ ThorEnhance::Configuration.allow_changes?
40
+
41
+ required = required.nil? ? @required : required
42
+ configuration[:add_command_method_enhance][:example] = { repeatable: repeatable, required: required, required_kwargs: [:desc] }
43
+ end
44
+
45
+ def header
46
+ ThorEnhance::Configuration.allow_changes?
47
+
48
+ configuration[:add_command_method_enhance][:header] = { repeatable: true, required: false, required_kwargs: [:name, :desc], optional_kwargs: [:tag] }
49
+ end
50
+
51
+ def custom_header(name, question: false, repeatable: false, required: false)
52
+ ThorEnhance::Configuration.allow_changes?
53
+
54
+ raise ArgumentError, "Custom Header name must be unique. #{name} is already defined as a custom header. " if custom_headers.include?(name.to_sym)
55
+
56
+ custom_headers << name.to_sym
57
+ question_headers << name.to_sym if question
58
+ configuration[:add_command_method_enhance][name.to_sym] = { repeatable: repeatable, required: required, optional_kwargs: [:tag] }
59
+ end
60
+
61
+ def readme(required: nil, empty_group: :unassigned, skip_key: DEFAULT_SKIP_KEY, enums: [:important, :advanced, skip_key.to_sym].compact)
62
+ ThorEnhance::Configuration.allow_changes?
63
+
64
+ @readme_empty_group = empty_group.to_sym
65
+ @readme_skip_key = skip_key
66
+ @readme_enums = enums.map(&:to_sym) << empty_group.to_sym
67
+ required = required.nil? ? @required : required
68
+ configuration[:add_option_enhance][:readme] = { enums: enums, required: required }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ThorEnhance
6
+ module Autogenerate
7
+ class Option
8
+ TEMPLATE_ERB = "#{File.dirname(__FILE__)}/templates/option.rb.erb"
9
+ OPTION_TEMPLATE = ERB.new(File.read(TEMPLATE_ERB))
10
+
11
+ attr_reader :name, :option
12
+
13
+ def initialize(name:, option:)
14
+ @name = name
15
+ @option = option
16
+ end
17
+
18
+ def template_text
19
+ text = []
20
+ text << "# What: #{option.description}"
21
+ text << "# Type: #{option.type}"
22
+ text << "# Required: #{option.required}"
23
+ text << "# Allowed Inputs: #{option.enum}" if option.enum
24
+ text << invocations.map { "#{_1}"}.join(" | ")
25
+
26
+ text.join("\n")
27
+ end
28
+
29
+ def invocations
30
+ base = [option.switch_name] + option.aliases
31
+ if option.type == :boolean
32
+ counter = option.switch_name.sub("--", "--no-")
33
+ base << counter
34
+ end
35
+
36
+ base
37
+ end
38
+
39
+ def readme_type
40
+ option.readme || ThorEnhance.configuration.autogenerated_config.readme_empty_group
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ <% ThorEnhance.configuration.autogenerated_config.readme_enums.each_with_index do |group_name, index| %>
2
+ <% next if method_options[group_name].nil? %>
3
+ <details <%= "open" if index == 0 %>>
4
+ <summary> <h3> <%= group_name.to_s.titlecase %> options </h3> </summary>
5
+
6
+ ```bash
7
+ <%= method_options[group_name].map { "#{_1.template_text}\n" }.join("\n") %>
8
+ ```
9
+
10
+ </details>
11
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <details>
2
+ <summary> <h3> Class Options </h3> </summary>
3
+
4
+ ```bash
5
+ <%= method_options_text_array.join("\n\n") %>
6
+ ```
7
+ </details>
@@ -0,0 +1,54 @@
1
+ # <%= title.to_s.titlecase %>
2
+
3
+ <%= command_source %>
4
+
5
+ ## Description
6
+ <%= command.long_description || command.description %>
7
+
8
+ ```bash
9
+ # Base command for `<%= command.usage %>`
10
+ <%= all_bases.join(" <options> \n") %> <options>
11
+ ```
12
+
13
+ <%= custom_headers %>
14
+
15
+ <% headers.each do |header| %>
16
+ ## <%= header[:name] %>
17
+ <%= header[:desc]%>
18
+ <% end %>
19
+ <% if drawn_out_examples %>
20
+ ---
21
+
22
+ ## Examples
23
+ <% drawn_out_examples.each do |ex|%>
24
+ ```bash
25
+ <%= ex %>
26
+ ```
27
+ <% end %>
28
+ <% end %>
29
+ <% if !method_options_erb.strip.empty? %>
30
+ ---
31
+
32
+ <% if children_descriptors.empty? %>
33
+ ## Method Options
34
+
35
+ <%= method_options_erb %>
36
+ <% end %>
37
+ <% else %>
38
+ <% children_descriptors.each do |child| %>
39
+ ### [<%= child[:title]%>](<%= child[:link] %>)
40
+
41
+ <%= "**Default Command** <br>" if child[:default_command] %>
42
+ <%= child[:description] %>
43
+
44
+ ```bash
45
+ <%= child[:all_bases].join(" <options> \n") %> <options>
46
+ <%= child[:examples].map { _1 }.join("\n") %>
47
+ ```
48
+ <% end %>
49
+ <% end %>
50
+
51
+ <%= class_options_erb %>
52
+ ---
53
+
54
+ <%= footer_erb %>
@@ -0,0 +1,2 @@
1
+ > AutoGenerateted by [ThorEnhance](https://github.com/matt-taylor/thor_enhance) <br>
2
+ > Regenerate readme files with: <%= regenerate_thor_command %>
@@ -0,0 +1,14 @@
1
+ # What: <%= option.description %>
2
+ # Type: <%= option.type %>
3
+ # Required: <%= option.required %>
4
+ # <%= "**Allowed inputs:** #{option.enum}" if option.enum %>
5
+ # <%= name.to_s.titlecase %>
6
+ <%= invocations.map { "`#{_1}`"}.join(" | ") %>
7
+
8
+ **What:** <%= option.description %><br>
9
+ **Invocation:** <br>
10
+ **Type:** <%= option.type %><br>
11
+ **Default:** <%= option.default || "none" %><br>
12
+ **Required:** <%= option.required %><br>
13
+ <%= "**Allowed inputs:** #{option.enum}" if option.enum %>
14
+
@@ -0,0 +1,9 @@
1
+ ## [<%= root_child[:title]%>](<%= root_child[:link] %>)
2
+ <%= root_child[:description] %>
3
+
4
+ ```bash
5
+ # Help Command
6
+ <%= root_child[:basename_string]%> <options>
7
+
8
+ <%= root_child[:examples].map { _1 }.join("\n") %>
9
+ ```
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThorEnhance
4
+ module Autogenerate
5
+ module Validate
6
+ module_function
7
+
8
+ def validate(options:, root: nil)
9
+ root_result = validate_root(options: options, root: root)
10
+ return root_result if root_result[:status] != :pass
11
+
12
+ trunk = root_result[:trunk]
13
+ constant = root_result[:constant]
14
+
15
+ subcommand_result = validate_subcommand(options: options, trunk: trunk)
16
+ return subcommand_result if subcommand_result[:status] != :pass
17
+
18
+ trunk = subcommand_result[:trunk]
19
+ command_result = validate_command(options: options, trunk: trunk, constant: constant)
20
+ return command_result if command_result[:status] != :pass
21
+ command = command_result[:command]
22
+ { command: command, trunk: trunk, constant: constant, status: :pass }
23
+ end
24
+
25
+ def validate_root(options:, root:)
26
+ begin
27
+ constant = root || Object.const_get(options.root.to_s)
28
+ rescue NameError => e
29
+ msg_array = [
30
+ "Unable to load provided --root|-r option `#{options.root}`",
31
+ "Please check the spelling and ensure the klass has loaded"
32
+ ]
33
+ return { error: e, msg_array: msg_array, status: :fail }
34
+ end
35
+
36
+ begin
37
+ trunk = ThorEnhance::Tree.tree(base: constant)
38
+ rescue TreeFailure => e
39
+ msg_array = [
40
+ "--root|-r option is not a Thor klass.",
41
+ "Please ensure that the provided klass is a child of Thor"
42
+ ]
43
+ return { error: e, msg_array: msg_array, status: :fail }
44
+ end
45
+
46
+ { trunk: trunk, constant: constant, status: :pass }
47
+ end
48
+
49
+ def validate_command(options:, trunk:, constant:)
50
+ # Return early when command is not present in the options object
51
+ command = options.command
52
+ return { status: :pass, trunk: trunk, command: nil } if command.nil?
53
+
54
+ # Return early when command is found in the tree trunk
55
+ command = trunk.children[options.command] rescue trunk[options.command]
56
+ return { status: :pass, trunk: trunk, command: command } if command
57
+
58
+ # Command option was available but command was not found in the trunk
59
+ msg_array = ["Failed to find --command|-c `#{options.command}`"]
60
+ msg_array << "Provided root command `#{constant}`"
61
+ msg_array << "With Provided subcommand `#{options.subcommand}`" if options.subcommand
62
+ msg_array << "does not have command `#{options.command}` as a child" if options.subcommand
63
+
64
+ { msg_array: msg_array, status: :fail }
65
+ end
66
+
67
+ def validate_subcommand(options:, trunk:)
68
+ subcommands = options.subcommand
69
+ return { trunk: trunk, status: :pass } if subcommands.nil?
70
+
71
+ subcommands = subcommands.dup
72
+ subcommand = subcommands.shift
73
+ temp_trunk = trunk[subcommand]
74
+ while subcommand != nil
75
+ if temp_trunk.nil? || !temp_trunk.children?
76
+ msg_array = [
77
+ "Order is important with --subcommands|-s options",
78
+ "Provided with: #{options.subcommand}",
79
+ "Subcommand `#{subcommand}` does not have any child commands",
80
+ "Every provided subcommand must have children",
81
+ "If the subcommand `#{subcommand}` is meant to be a command",
82
+ "Pass `#{subcommand}` in as `--command|-c #{subcommand}` instead",
83
+ ]
84
+ return { msg_array: msg_array, status: :fail }
85
+ end
86
+ subcommand = subcommands.shift
87
+ # Will always be in the child hash at this point if subcommand exists
88
+ temp_trunk = temp_trunk.children[subcommand] if subcommand
89
+ end
90
+
91
+ { trunk: temp_trunk, subcommands: options.subcommand, status: :pass }
92
+ end
93
+ end
94
+ end
95
+ end