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