thor_enhance 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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