thor_enhance 0.4.0 → 0.5.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/Gemfile +2 -1
- data/Gemfile.lock +36 -8
- data/README.md +4 -2
- data/bin/thor_enhance +22 -0
- data/docs/autogenerate/Readme.md +152 -0
- data/lib/thor_enhance/autogenerate/command.rb +240 -0
- data/lib/thor_enhance/autogenerate/configuration.rb +72 -0
- data/lib/thor_enhance/autogenerate/option.rb +44 -0
- data/lib/thor_enhance/autogenerate/templates/aggregate_options.rb.erb +12 -0
- data/lib/thor_enhance/autogenerate/templates/command.rb.erb +49 -0
- data/lib/thor_enhance/autogenerate/templates/footer.rb.erb +3 -0
- data/lib/thor_enhance/autogenerate/templates/option.rb.erb +14 -0
- data/lib/thor_enhance/autogenerate/templates/root.rb.erb +9 -0
- data/lib/thor_enhance/autogenerate/validate.rb +95 -0
- data/lib/thor_enhance/autogenerate.rb +78 -0
- data/lib/thor_enhance/command_method.rb +35 -5
- data/lib/thor_enhance/configuration.rb +47 -4
- data/lib/thor_enhance/option.rb +0 -2
- data/lib/thor_enhance/sample.rb +40 -0
- data/lib/thor_enhance/thor_auto_generate_inject.rb +64 -0
- data/lib/thor_enhance/tree.rb +4 -2
- data/lib/thor_enhance/version.rb +1 -1
- data/lib/thor_enhance.rb +12 -4
- data/thor_enhance.gemspec +1 -0
- metadata +30 -2
@@ -0,0 +1,49 @@
|
|
1
|
+
# <%= title.to_s.titlecase %>
|
2
|
+
|
3
|
+
## Description
|
4
|
+
<%= command.long_description || command.description %>
|
5
|
+
|
6
|
+
```bash
|
7
|
+
# Base command for `<%= command.usage %>`
|
8
|
+
<%= basename_string %>
|
9
|
+
```
|
10
|
+
|
11
|
+
<%= custom_headers %>
|
12
|
+
|
13
|
+
<% headers.each do |header| %>
|
14
|
+
## <%= header[:name] %>
|
15
|
+
<%= header[:desc]%>
|
16
|
+
<% end %>
|
17
|
+
<% if drawn_out_examples %>
|
18
|
+
---
|
19
|
+
|
20
|
+
## Examples
|
21
|
+
<% drawn_out_examples.each do |ex|%>
|
22
|
+
```bash
|
23
|
+
<%= ex %>
|
24
|
+
```
|
25
|
+
<% end %>
|
26
|
+
<% end %>
|
27
|
+
<% if !method_options_erb.strip.empty? %>
|
28
|
+
---
|
29
|
+
|
30
|
+
<% if children_descriptors.empty? %>
|
31
|
+
## Method Options
|
32
|
+
|
33
|
+
<%= method_options_erb %>
|
34
|
+
<% end %>
|
35
|
+
<% else %>
|
36
|
+
<% children_descriptors.each do |child| %>
|
37
|
+
### [<%= child[:title]%>](<%= child[:link] %>)
|
38
|
+
|
39
|
+
<%= child[:description] %>
|
40
|
+
|
41
|
+
```bash
|
42
|
+
<%= child[:basename_string]%> <options>
|
43
|
+
<%= child[:examples].map { _1 }.join("\n") %>
|
44
|
+
```
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
47
|
+
---
|
48
|
+
|
49
|
+
<%= footer_erb %>
|
@@ -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,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
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor_enhance/autogenerate/configuration"
|
4
|
+
require "thor_enhance/autogenerate/validate"
|
5
|
+
require "thor_enhance/autogenerate/option"
|
6
|
+
require "thor_enhance/autogenerate/command"
|
7
|
+
|
8
|
+
module ThorEnhance
|
9
|
+
module Autogenerate
|
10
|
+
module_function
|
11
|
+
|
12
|
+
ROOT_ERB = "#{File.dirname(__FILE__)}/autogenerate/templates/root.rb.erb"
|
13
|
+
ROOT_TEMPLATE = ERB.new(File.read(ROOT_ERB))
|
14
|
+
|
15
|
+
def execute!(options:, basename: File.basename($0), root: nil)
|
16
|
+
validate_result = Validate.validate(options: options, root: root)
|
17
|
+
return validate_result if validate_result[:status] != :pass
|
18
|
+
|
19
|
+
command = validate_result[:command]
|
20
|
+
trunk = validate_result[:trunk]
|
21
|
+
leaves =
|
22
|
+
if command
|
23
|
+
{ options.command => command }
|
24
|
+
elsif Hash === trunk
|
25
|
+
# Parent trunk is a hash -- structure needs to change
|
26
|
+
trunk
|
27
|
+
else
|
28
|
+
# if not parent, grab the children
|
29
|
+
trunk.children
|
30
|
+
end
|
31
|
+
|
32
|
+
command_structure = leaves.map do |name, leaf|
|
33
|
+
parent = Command.new(name: name, leaf: leaf, basename: basename)
|
34
|
+
end
|
35
|
+
|
36
|
+
# flatten_children returns all kids, grandkids, great grandkids etc of the commands returned from the above mapping
|
37
|
+
youthful_kids = command_structure.map(&:flatten_children).flatten
|
38
|
+
|
39
|
+
# this is a flat map of the entire family tree. Each node knows where it is so we can flatten it
|
40
|
+
family_tree = command_structure + youthful_kids
|
41
|
+
|
42
|
+
save_generated_readmes!(commands: family_tree, generated_root: options.generated_root, apply: options.apply)
|
43
|
+
end
|
44
|
+
|
45
|
+
def save_generated_readmes!(commands:, generated_root:, apply:)
|
46
|
+
parent = generated_root || ENV["THOR_ENHANCE_GENERATED_ROOT_PATH"]
|
47
|
+
full_root = "#{parent}/commands"
|
48
|
+
saved_status = commands.map do |command|
|
49
|
+
command.save_self!(root: full_root, apply: apply)
|
50
|
+
end
|
51
|
+
self_for_roots = saved_status.collect { _1[:self_for_root] }
|
52
|
+
saved_status << root_savior!(apply: apply, full_root: full_root, self_for_roots: self_for_roots)
|
53
|
+
|
54
|
+
{ status: :pass, saved_status: saved_status }
|
55
|
+
end
|
56
|
+
|
57
|
+
def root_savior!(full_root:, self_for_roots:, apply:)
|
58
|
+
full_path = "#{full_root}/Readme.md"
|
59
|
+
root_erb_result = self_for_roots.map do |root_child|
|
60
|
+
ROOT_TEMPLATE.result_with_hash({ root_child: root_child })
|
61
|
+
end.join("\n")
|
62
|
+
|
63
|
+
FileUtils.mkdir_p(full_root)
|
64
|
+
if File.exist?(full_path)
|
65
|
+
content = File.read(full_path)
|
66
|
+
diff = root_erb_result == content ? :same : :overwite
|
67
|
+
else
|
68
|
+
diff = :new
|
69
|
+
end
|
70
|
+
|
71
|
+
if apply
|
72
|
+
File.write(full_path, root_erb_result)
|
73
|
+
end
|
74
|
+
|
75
|
+
{ path: full_path, diff: diff, apply: apply }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -14,16 +14,18 @@ module ThorEnhance
|
|
14
14
|
ThorEnhance.configuration.command_method_enhance.each do |name, object|
|
15
15
|
# Define a new method based on the name of each enhanced command method
|
16
16
|
# Method takes care all validation except requirment -- Requirement is done during command initialization
|
17
|
-
ClassMethods.define_method("#{name}") do |input|
|
17
|
+
ClassMethods.define_method("#{name}") do |input = nil, *args, **kwargs|
|
18
18
|
return nil unless ThorEnhance.configuration.allowed?(self)
|
19
19
|
|
20
|
-
value = instance_variable_get("@#{name}")
|
21
|
-
value ||= {}
|
22
20
|
# Usage is nil when the `desc` has not been defined yet -- Under normal circumstance this will never happen
|
23
21
|
if @usage.nil?
|
24
22
|
raise ArgumentError, "Usage is not set. Please ensure `#{name}` is defined after usage is set"
|
25
23
|
end
|
26
24
|
|
25
|
+
__thor_enhance_validate_arguments!(object, input, args, kwargs)
|
26
|
+
value = instance_variable_get("@#{name}")
|
27
|
+
value ||= {}
|
28
|
+
|
27
29
|
# Required check gets done on command initialization (defined below in ClassMethods)
|
28
30
|
if input.nil?
|
29
31
|
# no op when it is nil
|
@@ -39,14 +41,14 @@ module ThorEnhance
|
|
39
41
|
|
40
42
|
if object[:repeatable]
|
41
43
|
value[@usage] ||= []
|
42
|
-
value[@usage] << input
|
44
|
+
value[@usage] << { input: input, arguments: { kwargs: kwargs, positional: args } }
|
43
45
|
else
|
44
46
|
if value[@usage]
|
45
47
|
raise ValidationFailed, "#{@usage} recieved command method `#{name}` with repeated invocations of " \
|
46
48
|
"`#{name}`. Please remove the secondary invocation. Or set `#{name}` as a repeatable command method"
|
47
49
|
end
|
48
50
|
|
49
|
-
value[@usage] = input
|
51
|
+
value[@usage] = { input: input, arguments: { kwargs: kwargs, positional: args } }
|
50
52
|
end
|
51
53
|
|
52
54
|
instance_variable_set("@#{name}", value)
|
@@ -106,6 +108,10 @@ module ThorEnhance
|
|
106
108
|
# Skip if the method is part of the ignore list
|
107
109
|
next if ThorEnhance::Tree.ignore_commands.include?(meth.to_s)
|
108
110
|
|
111
|
+
# subcommands/subtasks need not require things that regular commands need
|
112
|
+
# If user wants them on the sucommand, thats cool, but we will never enforce it
|
113
|
+
next if subcommands.map(&:to_s).include?(meth.to_s)
|
114
|
+
|
109
115
|
# At this point, the command method is missing, we are not in disable mode, and the command method was required
|
110
116
|
# raise all hell
|
111
117
|
raise ThorEnhance::RequiredOption, "`#{meth}` does not have required command method #{name} invoked. " \
|
@@ -131,6 +137,30 @@ module ThorEnhance
|
|
131
137
|
|
132
138
|
private
|
133
139
|
|
140
|
+
|
141
|
+
def __thor_enhance_validate_arguments!(object, input, args, kwargs)
|
142
|
+
expected_arity = object.dig(:arity)
|
143
|
+
if args.length != expected_arity
|
144
|
+
raise ArgumentError, "Excluding #{input}, the expected arity command method `#{name}` for #{@usage} is #{expected_arity}. Provided #{args.length}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# checks if there are extra kwargs present that are not expected
|
148
|
+
available_kwargs = object.dig(:kwargs).keys
|
149
|
+
extra_keys = kwargs.keys - available_kwargs
|
150
|
+
unless extra_keys.empty?
|
151
|
+
raise ArgumentError, "#{@usage} received command method `#{name}` with unknown KWargs #{extra_keys}"
|
152
|
+
end
|
153
|
+
|
154
|
+
# Checks if all the required kwargs are present
|
155
|
+
req_kwargs = object.dig(:kwargs).select { _2 }.keys
|
156
|
+
missing_required_kwargs = req_kwargs - kwargs.keys
|
157
|
+
|
158
|
+
# binding.pry if expected_arity > 0 || available_kwargs.length > 0
|
159
|
+
return if missing_required_kwargs.empty?
|
160
|
+
|
161
|
+
raise ArgumentError, "#{@usage} received command method `#{name}` with missing KWargs #{missing_required_kwargs}"
|
162
|
+
end
|
163
|
+
|
134
164
|
def __thor_enhance_access(type:, &block)
|
135
165
|
raise ArgumentError, "Expected to receive block. No block given" unless block_given?
|
136
166
|
|
@@ -58,6 +58,30 @@ module ThorEnhance
|
|
58
58
|
self.class.disallow_changes!
|
59
59
|
end
|
60
60
|
|
61
|
+
def readme_enhance!(required: false, &block)
|
62
|
+
# only inject readme things if it is required client side
|
63
|
+
require "thor_enhance/thor_auto_generate_inject"
|
64
|
+
require "thor_enhance/autogenerate"
|
65
|
+
|
66
|
+
if defined?(@autogenerated_config)
|
67
|
+
raise ValidationFailed, "ReadMe Enhance has already been initialized."
|
68
|
+
end
|
69
|
+
|
70
|
+
autogenerated_config.set_default_required(required)
|
71
|
+
autogenerated_config.default
|
72
|
+
yield(autogenerated_config) if block_given?
|
73
|
+
|
74
|
+
autogenerated_config.configuration.each do |meth, object|
|
75
|
+
object.each do |name, args|
|
76
|
+
public_send(meth, name, **args)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def autogenerated_config
|
82
|
+
@autogenerated_config ||= Autogenerate::Configuration.new
|
83
|
+
end
|
84
|
+
|
61
85
|
def command_method_enhance
|
62
86
|
@command_method_enhance ||= {}
|
63
87
|
end
|
@@ -66,6 +90,14 @@ module ThorEnhance
|
|
66
90
|
@klass_procs ||= []
|
67
91
|
end
|
68
92
|
|
93
|
+
def basename
|
94
|
+
@basename
|
95
|
+
end
|
96
|
+
|
97
|
+
def basename=(name)
|
98
|
+
@basename = name
|
99
|
+
end
|
100
|
+
|
69
101
|
def allowed
|
70
102
|
@allowed ||= DEFAULT_ALLOWED
|
71
103
|
end
|
@@ -92,14 +124,21 @@ module ThorEnhance
|
|
92
124
|
end
|
93
125
|
|
94
126
|
# Adding a new method to enhance the overall command
|
95
|
-
def add_command_method_enhance(name, allowed_klasses: nil, enums: nil, required: false, repeatable: false)
|
127
|
+
def add_command_method_enhance(name, allowed_klasses: nil, enums: nil, required: false, repeatable: false, arity: 0, required_kwargs: [], optional_kwargs: [])
|
128
|
+
return if ENV["SKIP_TESTING_METHOD"]
|
129
|
+
|
96
130
|
self.class.allow_changes?
|
97
131
|
|
98
|
-
|
132
|
+
kwargs = {}
|
133
|
+
required_kwargs.each { kwargs[_1.to_sym] = true }
|
134
|
+
optional_kwargs.each { kwargs[_1.to_sym] = false }
|
135
|
+
add_to_variable(command_method_enhance, ::Thor::Command.instance_methods, name, allowed_klasses, enums, required, repeatable, arity, kwargs)
|
99
136
|
end
|
100
137
|
|
101
138
|
# add a new flag on the command option
|
102
139
|
def add_option_enhance(name, allowed_klasses: nil, enums: nil, required: false)
|
140
|
+
return if ENV["SKIP_TESTING_METHOD"]
|
141
|
+
|
103
142
|
self.class.allow_changes?
|
104
143
|
|
105
144
|
add_to_variable(option_enhance, ::Thor::Option.instance_methods, name, allowed_klasses, enums, required)
|
@@ -107,7 +146,7 @@ module ThorEnhance
|
|
107
146
|
|
108
147
|
private
|
109
148
|
|
110
|
-
def add_to_variable(storage, methods, name, allowed_klasses, enums, required, repeatable = false)
|
149
|
+
def add_to_variable(storage, methods, name, allowed_klasses, enums, required, repeatable = false, arity = 0, kwargs = false)
|
111
150
|
# Reject if the name is not a Symbol or a string
|
112
151
|
if [String, Symbol].none? { _1 === name }
|
113
152
|
raise ArgumentError, "Invalid name type received. Received [#{name}] of type [#{name.class}]. Expected to be of type String or Symbol"
|
@@ -136,7 +175,11 @@ module ThorEnhance
|
|
136
175
|
raise ArgumentError, "Recieved allowed_klasses with #{allowed_klasses}. When present, it is expected to be an Array"
|
137
176
|
end
|
138
177
|
|
139
|
-
|
178
|
+
if arity < 0
|
179
|
+
raise ArgumentError, "Recieved arity with #{arity}. When present, it is expected to be greater than 0"
|
180
|
+
end
|
181
|
+
|
182
|
+
storage[name.to_sym] = { allowed_klasses: allowed_klasses, enums: enums, required: required, repeatable: repeatable, kwargs: kwargs, arity: arity }
|
140
183
|
end
|
141
184
|
end
|
142
185
|
end
|
data/lib/thor_enhance/option.rb
CHANGED
@@ -5,8 +5,6 @@ require "thor"
|
|
5
5
|
module ThorEnhance
|
6
6
|
module Option
|
7
7
|
def self.thor_enhance_injection!
|
8
|
-
return false unless ThorEnhance::Configuration.allow_changes?
|
9
|
-
|
10
8
|
# Create getter method for the enhance instance variable
|
11
9
|
ThorEnhance.configuration.option_enhance.each do |name, object|
|
12
10
|
define_method(name) { instance_variable_get("@#{name}") }
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor_enhance"
|
4
|
+
|
5
|
+
module ThorEnhance
|
6
|
+
class Sample < Thor
|
7
|
+
thor_enhance_allow!
|
8
|
+
|
9
|
+
class SubCommand < Thor
|
10
|
+
thor_enhance_allow!
|
11
|
+
|
12
|
+
desc "innard", "Validate that a subcommand works. This description can be as long as you want it to be."
|
13
|
+
long_desc "Wow, This longer description will take precedence over the desc above. This is what will be shown in the readme autogenerated page. Try me out!"
|
14
|
+
example "innard --count 5", desc: "Innard sub command with a count of 5"
|
15
|
+
example "innard --count 35", desc: "Innard sub command with a count of 35"
|
16
|
+
header name: "Deprecation warning", desc: "This command will get deprecated in the next major version"
|
17
|
+
when_should_i_use_this "Use sub command task to validate that subocommands work as expected"
|
18
|
+
method_option :count, type: :numeric, readme: :skip
|
19
|
+
def innard;end;
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "sub", "Thor sub command validation for thor enhance"
|
23
|
+
subcommand "sub", SubCommand
|
24
|
+
|
25
|
+
desc "sample", "This Sample command does a lot of nothing"
|
26
|
+
when_should_i_use_this <<~README
|
27
|
+
Have you ever wanted your code to be useless?
|
28
|
+
Well, this command does absolutely nothing.
|
29
|
+
This output is to say that this command does absolutely nothing
|
30
|
+
README
|
31
|
+
how_does_this_help "Honestly, this does not help at all", tag: "h4"
|
32
|
+
how_does_this_help "But its cool because it is a repatable command", tag: "h1"
|
33
|
+
example "sample", desc: "yo yo ma"
|
34
|
+
example "sample --boolean", desc: "yo yo ma"
|
35
|
+
method_option :boolean, aliases: "-b", type: :boolean, desc: "Just a normal boolean", readme: :important
|
36
|
+
def sample
|
37
|
+
Kernel.puts "Executed Sample method"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
####################
|
4
|
+
#
|
5
|
+
# Injects a method directly into the thor base class
|
6
|
+
# This allows the developer to have this convenience method
|
7
|
+
# Just by utilizing ThorEnhance
|
8
|
+
#
|
9
|
+
####################
|
10
|
+
require "thor"
|
11
|
+
|
12
|
+
class Thor
|
13
|
+
desc "thor_enhance_autogenerate", "Auto Generate ReadMe material for your Thor commands"
|
14
|
+
method_option :subcommand, aliases: "-s", type: :string, repeatable: true, desc: "When provided, autogeneration will execute on the subcommand"
|
15
|
+
method_option :command, aliases: "-c", type: :string, desc: "When provided, autogeneration will occur only on this method. Note: When used with subcommand, method must exist on subcommand"
|
16
|
+
method_option :basename, aliases: "-b", type: :string, desc: "The name of the file that executes the Thor script"
|
17
|
+
method_option :generated_root, aliases: "-r", type: :string, default: File.expand_path("generated_readme"), desc: "The root location to store autogenerated files"
|
18
|
+
method_option :apply, aliases: "-a", type: :boolean, desc: "When comfortable with the changes made, enabling apply will save changes to generated files"
|
19
|
+
|
20
|
+
def thor_enhance_autogenerate
|
21
|
+
require "thor_enhance/autogenerate"
|
22
|
+
basename = options.basename || ThorEnhance.basename || File.basename($0)
|
23
|
+
|
24
|
+
result = ThorEnhance::Autogenerate.execute!(options: options, root: self.class, basename: basename)
|
25
|
+
|
26
|
+
if result[:status] == :pass
|
27
|
+
__auto_generate_success!(result[:saved_status])
|
28
|
+
else
|
29
|
+
__auto_generate_fail!(result[:msg_array])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
no_tasks do
|
34
|
+
def __auto_generate_success!(statuses)
|
35
|
+
if statuses.all? { _1[:apply] == true }
|
36
|
+
say "Readme changes are enabled", [:green, :bold], true
|
37
|
+
else
|
38
|
+
say "Readme changes are not enabled. To apply changes, add `--apply` to the command", [:on_white, :black], true
|
39
|
+
end
|
40
|
+
statuses.each do |status|
|
41
|
+
case status[:diff]
|
42
|
+
when :new
|
43
|
+
say " Added : #{status[:path]}", [:green, :bold], true
|
44
|
+
when :same
|
45
|
+
say " No Change: #{status[:path]}", [:yellow, :bold], true
|
46
|
+
when :overwite
|
47
|
+
say " Changes : #{status[:path]}", [:cyan, :bold], true
|
48
|
+
else
|
49
|
+
say " : #{status[:path]}", [:bold], true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def __auto_generate_fail!(msg_array)
|
55
|
+
say_error set_color("*********************** FAILED OPERATION ***********************", :red, :bold)
|
56
|
+
say_error set_color("FAIL: Unable to continue", :red, :bold)
|
57
|
+
msg_array.each do |line|
|
58
|
+
say_error set_color("FAIL: #{line}", :red, :bold)
|
59
|
+
end
|
60
|
+
say_error set_color("*********************** FAILED OPERATION ***********************", :red, :bold)
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/thor_enhance/tree.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ThorEnhance
|
4
4
|
class Tree
|
5
|
-
DEFAULT_IGNORE_COMMANDS = ["help"]
|
5
|
+
DEFAULT_IGNORE_COMMANDS = ["help", "thor_enhance_autogenerate"]
|
6
6
|
|
7
7
|
def self.add_ignore_commands(command)
|
8
8
|
return false if ignore_commands.include?(command)
|
@@ -20,6 +20,8 @@ module ThorEnhance
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.tree(base:, parent: nil)
|
23
|
+
raise TreeFailure, "#{base} does not respond to all_commands. Unable to continue" unless base.respond_to?(:all_commands)
|
24
|
+
|
23
25
|
base.all_commands.map do |k, command|
|
24
26
|
next if ignore_commands.include?(k)
|
25
27
|
|
@@ -36,7 +38,7 @@ module ThorEnhance
|
|
36
38
|
@parent = parent
|
37
39
|
@base = base
|
38
40
|
@command = command
|
39
|
-
@children =
|
41
|
+
@children = {}
|
40
42
|
|
41
43
|
if !base.subcommand_classes.nil? && base.subcommand_classes[command.name]
|
42
44
|
@children = self.class.tree(parent: self, base: base.subcommand_classes[command.name])
|
data/lib/thor_enhance/version.rb
CHANGED
data/lib/thor_enhance.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support"
|
4
|
+
|
3
5
|
require "thor_enhance/base"
|
4
6
|
require "thor_enhance/command"
|
5
7
|
require "thor_enhance/command_hook"
|
@@ -10,10 +12,16 @@ require "thor_enhance/tree"
|
|
10
12
|
|
11
13
|
module ThorEnhance
|
12
14
|
class BaseError < StandardError; end
|
13
|
-
class OptionNotAllowed <
|
14
|
-
class ValidationFailed <
|
15
|
-
class RequiredOption <
|
16
|
-
class OptionDeprecated <
|
15
|
+
class OptionNotAllowed < BaseError; end
|
16
|
+
class ValidationFailed < BaseError; end
|
17
|
+
class RequiredOption < BaseError; end
|
18
|
+
class OptionDeprecated < BaseError; end
|
19
|
+
class TreeFailure < BaseError; end
|
20
|
+
class AutoGenerateFailure < BaseError; end
|
21
|
+
|
22
|
+
def self.basename
|
23
|
+
configuration.basename
|
24
|
+
end
|
17
25
|
|
18
26
|
def self.configure
|
19
27
|
yield configuration if block_given?
|