thor 0.20.3 → 1.3.2
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/README.md +3 -9
- data/lib/thor/actions/create_file.rb +4 -3
- data/lib/thor/actions/create_link.rb +3 -2
- data/lib/thor/actions/directory.rb +8 -18
- data/lib/thor/actions/empty_directory.rb +1 -1
- data/lib/thor/actions/file_manipulation.rb +22 -24
- data/lib/thor/actions/inject_into_file.rb +34 -13
- data/lib/thor/actions.rb +39 -30
- data/lib/thor/base.rb +196 -49
- data/lib/thor/command.rb +34 -18
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
- data/lib/thor/error.rb +14 -22
- data/lib/thor/group.rb +13 -2
- data/lib/thor/invocation.rb +2 -1
- data/lib/thor/line_editor/basic.rb +1 -1
- data/lib/thor/line_editor/readline.rb +6 -6
- data/lib/thor/line_editor.rb +2 -2
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/argument.rb +17 -1
- data/lib/thor/parser/arguments.rb +35 -15
- data/lib/thor/parser/option.rb +45 -13
- data/lib/thor/parser/options.rb +79 -11
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +3 -2
- data/lib/thor/runner.rb +43 -32
- data/lib/thor/shell/basic.rb +68 -162
- data/lib/thor/shell/color.rb +9 -43
- data/lib/thor/shell/column_printer.rb +29 -0
- data/lib/thor/shell/html.rb +7 -49
- data/lib/thor/shell/lcs_diff.rb +49 -0
- data/lib/thor/shell/table_printer.rb +118 -0
- data/lib/thor/shell/terminal.rb +42 -0
- data/lib/thor/shell/wrapped_printer.rb +38 -0
- data/lib/thor/shell.rb +5 -5
- data/lib/thor/util.rb +25 -8
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +182 -17
- data/thor.gemspec +22 -10
- metadata +25 -11
- data/CHANGELOG.md +0 -204
- data/lib/thor/core_ext/io_binary_read.rb +0 -12
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
@@ -0,0 +1,29 @@
|
|
1
|
+
class Thor
|
2
|
+
class NestedContext
|
3
|
+
def initialize
|
4
|
+
@depth = 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def enter
|
8
|
+
push
|
9
|
+
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
pop
|
13
|
+
end
|
14
|
+
|
15
|
+
def entered?
|
16
|
+
@depth.positive?
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def push
|
22
|
+
@depth += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def pop
|
26
|
+
@depth -= 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/thor/parser/argument.rb
CHANGED
@@ -24,6 +24,14 @@ class Thor
|
|
24
24
|
validate! # Trigger specific validations
|
25
25
|
end
|
26
26
|
|
27
|
+
def print_default
|
28
|
+
if @type == :array and @default.is_a?(Array)
|
29
|
+
@default.map(&:dump).join(" ")
|
30
|
+
else
|
31
|
+
@default
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
27
35
|
def usage
|
28
36
|
required? ? banner : "[#{banner}]"
|
29
37
|
end
|
@@ -41,11 +49,19 @@ class Thor
|
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
52
|
+
def enum_to_s
|
53
|
+
if enum.respond_to? :join
|
54
|
+
enum.join(", ")
|
55
|
+
else
|
56
|
+
"#{enum.first}..#{enum.last}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
44
60
|
protected
|
45
61
|
|
46
62
|
def validate!
|
47
63
|
raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
|
48
|
-
raise ArgumentError, "An argument cannot have an enum other than an
|
64
|
+
raise ArgumentError, "An argument cannot have an enum other than an enumerable." if @enum && !@enum.is_a?(Enumerable)
|
49
65
|
end
|
50
66
|
|
51
67
|
def valid_type?(type)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Thor
|
2
|
-
class Arguments #:nodoc:
|
2
|
+
class Arguments #:nodoc:
|
3
3
|
NUMERIC = /[-+]?(\d*\.\d+|\d+)/
|
4
4
|
|
5
5
|
# Receives an array of args and returns two arrays, one with arguments
|
@@ -9,7 +9,7 @@ class Thor
|
|
9
9
|
arguments = []
|
10
10
|
|
11
11
|
args.each do |item|
|
12
|
-
break if item =~ /^-/
|
12
|
+
break if item.is_a?(String) && item =~ /^-/
|
13
13
|
arguments << item
|
14
14
|
end
|
15
15
|
|
@@ -30,7 +30,7 @@ class Thor
|
|
30
30
|
|
31
31
|
arguments.each do |argument|
|
32
32
|
if !argument.default.nil?
|
33
|
-
@assigns[argument.human_name] = argument.default
|
33
|
+
@assigns[argument.human_name] = argument.default.dup
|
34
34
|
elsif argument.required?
|
35
35
|
@non_assigned_required << argument
|
36
36
|
end
|
@@ -82,7 +82,7 @@ class Thor
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def current_is_value?
|
85
|
-
peek && peek.to_s !~
|
85
|
+
peek && peek.to_s !~ /^-{1,2}\S+/
|
86
86
|
end
|
87
87
|
|
88
88
|
# Runs through the argument array getting strings that contains ":" and
|
@@ -117,8 +117,18 @@ class Thor
|
|
117
117
|
#
|
118
118
|
def parse_array(name)
|
119
119
|
return shift if peek.is_a?(Array)
|
120
|
+
|
120
121
|
array = []
|
121
|
-
|
122
|
+
|
123
|
+
while current_is_value?
|
124
|
+
value = shift
|
125
|
+
|
126
|
+
if !value.empty?
|
127
|
+
validate_enum_value!(name, value, "Expected all values of '%s' to be one of %s; got %s")
|
128
|
+
end
|
129
|
+
|
130
|
+
array << value
|
131
|
+
end
|
122
132
|
array
|
123
133
|
end
|
124
134
|
|
@@ -134,11 +144,9 @@ class Thor
|
|
134
144
|
end
|
135
145
|
|
136
146
|
value = $&.index(".") ? shift.to_f : shift.to_i
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
end
|
141
|
-
end
|
147
|
+
|
148
|
+
validate_enum_value!(name, value, "Expected '%s' to be one of %s; got %s")
|
149
|
+
|
142
150
|
value
|
143
151
|
end
|
144
152
|
|
@@ -152,15 +160,27 @@ class Thor
|
|
152
160
|
nil
|
153
161
|
else
|
154
162
|
value = shift
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
159
|
-
end
|
163
|
+
|
164
|
+
validate_enum_value!(name, value, "Expected '%s' to be one of %s; got %s")
|
165
|
+
|
160
166
|
value
|
161
167
|
end
|
162
168
|
end
|
163
169
|
|
170
|
+
# Raises an error if the switch is an enum and the values aren't included on it.
|
171
|
+
#
|
172
|
+
def validate_enum_value!(name, value, message)
|
173
|
+
return unless @switches.is_a?(Hash)
|
174
|
+
|
175
|
+
switch = @switches[name]
|
176
|
+
|
177
|
+
return unless switch
|
178
|
+
|
179
|
+
if switch.enum && !switch.enum.include?(value)
|
180
|
+
raise MalformattedArgumentError, message % [name, switch.enum_to_s, value]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
164
184
|
# Raises an error if @non_assigned_required array is not empty.
|
165
185
|
#
|
166
186
|
def check_requirement!
|
data/lib/thor/parser/option.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
class Thor
|
2
2
|
class Option < Argument #:nodoc:
|
3
|
-
attr_reader :aliases, :group, :lazy_default, :hide
|
3
|
+
attr_reader :aliases, :group, :lazy_default, :hide, :repeatable
|
4
4
|
|
5
5
|
VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
|
6
6
|
|
7
7
|
def initialize(name, options = {})
|
8
8
|
@check_default_type = options[:check_default_type]
|
9
9
|
options[:required] = false unless options.key?(:required)
|
10
|
+
@repeatable = options.fetch(:repeatable, false)
|
10
11
|
super
|
11
|
-
@lazy_default
|
12
|
-
@group
|
13
|
-
@aliases
|
14
|
-
@hide
|
12
|
+
@lazy_default = options[:lazy_default]
|
13
|
+
@group = options[:group].to_s.capitalize if options[:group]
|
14
|
+
@aliases = normalize_aliases(options[:aliases])
|
15
|
+
@hide = options[:hide]
|
15
16
|
end
|
16
17
|
|
17
18
|
# This parse quick options given as method_options. It makes several
|
@@ -57,7 +58,7 @@ class Thor
|
|
57
58
|
default = nil
|
58
59
|
if VALID_TYPES.include?(value)
|
59
60
|
value
|
60
|
-
elsif required = (value == :required) # rubocop:disable AssignmentInCondition
|
61
|
+
elsif required = (value == :required) # rubocop:disable Lint/AssignmentInCondition
|
61
62
|
:string
|
62
63
|
end
|
63
64
|
when TrueClass, FalseClass
|
@@ -68,7 +69,7 @@ class Thor
|
|
68
69
|
value.class.name.downcase.to_sym
|
69
70
|
end
|
70
71
|
|
71
|
-
new(name.to_s, :
|
72
|
+
new(name.to_s, required: required, type: type, default: default, aliases: aliases)
|
72
73
|
end
|
73
74
|
|
74
75
|
def switch_name
|
@@ -88,14 +89,27 @@ class Thor
|
|
88
89
|
|
89
90
|
sample = "[#{sample}]".dup unless required?
|
90
91
|
|
91
|
-
if boolean?
|
92
|
-
sample << ", [#{dasherize('no-' + human_name)}]
|
92
|
+
if boolean? && name != "force" && !name.match(/\A(no|skip)[\-_]/)
|
93
|
+
sample << ", [#{dasherize('no-' + human_name)}], [#{dasherize('skip-' + human_name)}]"
|
93
94
|
end
|
94
95
|
|
96
|
+
aliases_for_usage.ljust(padding) + sample
|
97
|
+
end
|
98
|
+
|
99
|
+
def aliases_for_usage
|
95
100
|
if aliases.empty?
|
96
|
-
|
101
|
+
""
|
102
|
+
else
|
103
|
+
"#{aliases.join(', ')}, "
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def show_default?
|
108
|
+
case default
|
109
|
+
when TrueClass, FalseClass
|
110
|
+
true
|
97
111
|
else
|
98
|
-
|
112
|
+
super
|
99
113
|
end
|
100
114
|
end
|
101
115
|
|
@@ -111,7 +125,7 @@ class Thor
|
|
111
125
|
|
112
126
|
def validate!
|
113
127
|
raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
|
114
|
-
validate_default_type!
|
128
|
+
validate_default_type!
|
115
129
|
end
|
116
130
|
|
117
131
|
def validate_default_type!
|
@@ -128,7 +142,19 @@ class Thor
|
|
128
142
|
@default.class.name.downcase.to_sym
|
129
143
|
end
|
130
144
|
|
131
|
-
|
145
|
+
expected_type = (@repeatable && @type != :hash) ? :array : @type
|
146
|
+
|
147
|
+
if default_type != expected_type
|
148
|
+
err = "Expected #{expected_type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})"
|
149
|
+
|
150
|
+
if @check_default_type
|
151
|
+
raise ArgumentError, err
|
152
|
+
elsif @check_default_type == nil
|
153
|
+
Thor.deprecation_warning "#{err}.\n" +
|
154
|
+
"This will be rejected in the future unless you explicitly pass the options `check_default_type: false`" +
|
155
|
+
" or call `allow_incompatible_default_type!` in your code"
|
156
|
+
end
|
157
|
+
end
|
132
158
|
end
|
133
159
|
|
134
160
|
def dasherized?
|
@@ -142,5 +168,11 @@ class Thor
|
|
142
168
|
def dasherize(str)
|
143
169
|
(str.length > 1 ? "--" : "-") + str.tr("_", "-")
|
144
170
|
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def normalize_aliases(aliases)
|
175
|
+
Array(aliases).map { |short| short.to_s.sub(/^(?!\-)/, "-") }
|
176
|
+
end
|
145
177
|
end
|
146
178
|
end
|
data/lib/thor/parser/options.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Thor
|
2
|
-
class Options < Arguments #:nodoc:
|
2
|
+
class Options < Arguments #:nodoc:
|
3
3
|
LONG_RE = /^(--\w+(?:-\w+)*)$/
|
4
4
|
SHORT_RE = /^(-[a-z])$/i
|
5
5
|
EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i
|
@@ -29,8 +29,10 @@ class Thor
|
|
29
29
|
#
|
30
30
|
# If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
|
31
31
|
# an unknown option or a regular argument.
|
32
|
-
def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false)
|
32
|
+
def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false, relations = {})
|
33
33
|
@stop_on_unknown = stop_on_unknown
|
34
|
+
@exclusives = (relations[:exclusive_option_names] || []).select{|array| !array.empty?}
|
35
|
+
@at_least_ones = (relations[:at_least_one_option_names] || []).select{|array| !array.empty?}
|
34
36
|
@disable_required_check = disable_required_check
|
35
37
|
options = hash_options.values
|
36
38
|
super(options)
|
@@ -45,12 +47,12 @@ class Thor
|
|
45
47
|
@switches = {}
|
46
48
|
@extra = []
|
47
49
|
@stopped_parsing_after_extra_index = nil
|
50
|
+
@is_treated_as_value = false
|
48
51
|
|
49
52
|
options.each do |option|
|
50
53
|
@switches[option.switch_name] = option
|
51
54
|
|
52
|
-
option.aliases.each do |
|
53
|
-
name = short.to_s.sub(/^(?!\-)/, "-")
|
55
|
+
option.aliases.each do |name|
|
54
56
|
@shorts[name] ||= option.switch_name
|
55
57
|
end
|
56
58
|
end
|
@@ -74,8 +76,19 @@ class Thor
|
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
|
-
def
|
79
|
+
def shift
|
80
|
+
@is_treated_as_value = false
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
def unshift(arg, is_value: false)
|
85
|
+
@is_treated_as_value = is_value
|
86
|
+
super(arg)
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse(args) # rubocop:disable Metrics/MethodLength
|
78
90
|
@pile = args.dup
|
91
|
+
@is_treated_as_value = false
|
79
92
|
@parsing_options = true
|
80
93
|
|
81
94
|
while peek
|
@@ -88,7 +101,10 @@ class Thor
|
|
88
101
|
when SHORT_SQ_RE
|
89
102
|
unshift($1.split("").map { |f| "-#{f}" })
|
90
103
|
next
|
91
|
-
when EQ_RE
|
104
|
+
when EQ_RE
|
105
|
+
unshift($2, is_value: true)
|
106
|
+
switch = $1
|
107
|
+
when SHORT_NUM
|
92
108
|
unshift($2)
|
93
109
|
switch = $1
|
94
110
|
when LONG_RE, SHORT_RE
|
@@ -97,7 +113,8 @@ class Thor
|
|
97
113
|
|
98
114
|
switch = normalize_switch(switch)
|
99
115
|
option = switch_option(switch)
|
100
|
-
|
116
|
+
result = parse_peek(switch, option)
|
117
|
+
assign_result!(option, result)
|
101
118
|
elsif @stop_on_unknown
|
102
119
|
@parsing_options = false
|
103
120
|
@extra << shifted
|
@@ -116,12 +133,38 @@ class Thor
|
|
116
133
|
end
|
117
134
|
|
118
135
|
check_requirement! unless @disable_required_check
|
136
|
+
check_exclusive!
|
137
|
+
check_at_least_one!
|
119
138
|
|
120
139
|
assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
|
121
140
|
assigns.freeze
|
122
141
|
assigns
|
123
142
|
end
|
124
143
|
|
144
|
+
def check_exclusive!
|
145
|
+
opts = @assigns.keys
|
146
|
+
# When option A and B are exclusive, if A and B are given at the same time,
|
147
|
+
# the diffrence of argument array size will decrease.
|
148
|
+
found = @exclusives.find{ |ex| (ex - opts).size < ex.size - 1 }
|
149
|
+
if found
|
150
|
+
names = names_to_switch_names(found & opts).map{|n| "'#{n}'"}
|
151
|
+
class_name = self.class.name.split("::").last.downcase
|
152
|
+
fail ExclusiveArgumentError, "Found exclusive #{class_name} #{names.join(", ")}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def check_at_least_one!
|
157
|
+
opts = @assigns.keys
|
158
|
+
# When at least one is required of the options A and B,
|
159
|
+
# if the both options were not given, none? would be true.
|
160
|
+
found = @at_least_ones.find{ |one_reqs| one_reqs.none?{ |o| opts.include? o} }
|
161
|
+
if found
|
162
|
+
names = names_to_switch_names(found).map{|n| "'#{n}'"}
|
163
|
+
class_name = self.class.name.split("::").last.downcase
|
164
|
+
fail AtLeastOneRequiredArgumentError, "Not found at least one of required #{class_name} #{names.join(", ")}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
125
168
|
def check_unknown!
|
126
169
|
to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra
|
127
170
|
|
@@ -132,11 +175,33 @@ class Thor
|
|
132
175
|
|
133
176
|
protected
|
134
177
|
|
178
|
+
# Option names changes to swith name or human name
|
179
|
+
def names_to_switch_names(names = [])
|
180
|
+
@switches.map do |_, o|
|
181
|
+
if names.include? o.name
|
182
|
+
o.respond_to?(:switch_name) ? o.switch_name : o.human_name
|
183
|
+
else
|
184
|
+
nil
|
185
|
+
end
|
186
|
+
end.compact
|
187
|
+
end
|
188
|
+
|
189
|
+
def assign_result!(option, result)
|
190
|
+
if option.repeatable && option.type == :hash
|
191
|
+
(@assigns[option.human_name] ||= {}).merge!(result)
|
192
|
+
elsif option.repeatable
|
193
|
+
(@assigns[option.human_name] ||= []) << result
|
194
|
+
else
|
195
|
+
@assigns[option.human_name] = result
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
135
199
|
# Check if the current value in peek is a registered switch.
|
136
200
|
#
|
137
201
|
# Two booleans are returned. The first is true if the current value
|
138
202
|
# starts with a hyphen; the second is true if it is a registered switch.
|
139
203
|
def current_is_switch?
|
204
|
+
return [false, false] if @is_treated_as_value
|
140
205
|
case peek
|
141
206
|
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
|
142
207
|
[true, switch?($1)]
|
@@ -148,6 +213,7 @@ class Thor
|
|
148
213
|
end
|
149
214
|
|
150
215
|
def current_is_switch_formatted?
|
216
|
+
return false if @is_treated_as_value
|
151
217
|
case peek
|
152
218
|
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
|
153
219
|
true
|
@@ -157,15 +223,16 @@ class Thor
|
|
157
223
|
end
|
158
224
|
|
159
225
|
def current_is_value?
|
226
|
+
return true if @is_treated_as_value
|
160
227
|
peek && (!parsing_options? || super)
|
161
228
|
end
|
162
229
|
|
163
230
|
def switch?(arg)
|
164
|
-
switch_option(normalize_switch(arg))
|
231
|
+
!switch_option(normalize_switch(arg)).nil?
|
165
232
|
end
|
166
233
|
|
167
234
|
def switch_option(arg)
|
168
|
-
if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition
|
235
|
+
if match = no_or_skip?(arg) # rubocop:disable Lint/AssignmentInCondition
|
169
236
|
@switches[arg] || @switches["--#{match}"]
|
170
237
|
else
|
171
238
|
@switches[arg]
|
@@ -183,7 +250,8 @@ class Thor
|
|
183
250
|
@parsing_options
|
184
251
|
end
|
185
252
|
|
186
|
-
# Parse boolean values which can be given as --foo=true
|
253
|
+
# Parse boolean values which can be given as --foo=true or --foo for true values, or
|
254
|
+
# --foo=false, --no-foo or --skip-foo for false values.
|
187
255
|
#
|
188
256
|
def parse_boolean(switch)
|
189
257
|
if current_is_value?
|
@@ -194,7 +262,7 @@ class Thor
|
|
194
262
|
shift
|
195
263
|
false
|
196
264
|
else
|
197
|
-
!no_or_skip?(switch)
|
265
|
+
@switches.key?(switch) || !no_or_skip?(switch)
|
198
266
|
end
|
199
267
|
else
|
200
268
|
@switches.key?(switch) || !no_or_skip?(switch)
|
data/lib/thor/parser.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require_relative "parser/argument"
|
2
|
+
require_relative "parser/arguments"
|
3
|
+
require_relative "parser/option"
|
4
|
+
require_relative "parser/options"
|
data/lib/thor/rake_compat.rb
CHANGED
@@ -25,6 +25,7 @@ class Thor
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def self.included(base)
|
28
|
+
super(base)
|
28
29
|
# Hack. Make rakefile point to invoker, so rdoc task is generated properly.
|
29
30
|
rakefile = File.basename(caller[0].match(/(.*):\d+/)[1])
|
30
31
|
Rake.application.instance_variable_set(:@rakefile, rakefile)
|
@@ -40,7 +41,7 @@ instance_eval do
|
|
40
41
|
def task(*)
|
41
42
|
task = super
|
42
43
|
|
43
|
-
if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
|
44
|
+
if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable Lint/AssignmentInCondition
|
44
45
|
non_namespaced_name = task.name.split(":").last
|
45
46
|
|
46
47
|
description = non_namespaced_name
|
@@ -58,7 +59,7 @@ instance_eval do
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def namespace(name)
|
61
|
-
if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
|
62
|
+
if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable Lint/AssignmentInCondition
|
62
63
|
const_name = Thor::Util.camel_case(name.to_s).to_sym
|
63
64
|
klass.const_set(const_name, Class.new(Thor))
|
64
65
|
new_klass = klass.const_get(const_name)
|
data/lib/thor/runner.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require "thor/core_ext/io_binary_read"
|
1
|
+
require_relative "../thor"
|
2
|
+
require_relative "group"
|
4
3
|
|
5
4
|
require "yaml"
|
6
|
-
require "digest/
|
5
|
+
require "digest/sha2"
|
7
6
|
require "pathname"
|
8
7
|
|
9
|
-
class Thor::Runner < Thor #:nodoc:
|
8
|
+
class Thor::Runner < Thor #:nodoc:
|
10
9
|
map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
|
11
10
|
|
12
11
|
def self.banner(command, all = false, subcommand = false)
|
@@ -24,7 +23,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
|
24
23
|
initialize_thorfiles(meth)
|
25
24
|
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
26
25
|
self.class.handle_no_command_error(command, false) if klass.nil?
|
27
|
-
klass.start(["-h", command].compact, :
|
26
|
+
klass.start(["-h", command].compact, shell: shell)
|
28
27
|
else
|
29
28
|
super
|
30
29
|
end
|
@@ -39,30 +38,42 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
|
39
38
|
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
40
39
|
self.class.handle_no_command_error(command, false) if klass.nil?
|
41
40
|
args.unshift(command) if command
|
42
|
-
klass.start(args, :
|
41
|
+
klass.start(args, shell: shell)
|
43
42
|
end
|
44
43
|
|
45
44
|
desc "install NAME", "Install an optionally named Thor file into your system commands"
|
46
|
-
method_options :
|
47
|
-
def install(name) # rubocop:disable MethodLength
|
45
|
+
method_options as: :string, relative: :boolean, force: :boolean
|
46
|
+
def install(name) # rubocop:disable Metrics/MethodLength
|
48
47
|
initialize_thorfiles
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
49
|
+
is_uri = name =~ %r{^https?\://}
|
50
|
+
|
51
|
+
if is_uri
|
52
|
+
base = name
|
53
|
+
package = :file
|
54
|
+
require "open-uri"
|
55
|
+
begin
|
56
|
+
contents = URI.open(name, &:read)
|
57
|
+
rescue OpenURI::HTTPError
|
58
|
+
raise Error, "Error opening URI '#{name}'"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
# If a directory name is provided as the argument, look for a 'main.thor'
|
62
|
+
# command in said directory.
|
63
|
+
begin
|
64
|
+
if File.directory?(File.expand_path(name))
|
65
|
+
base = File.join(name, "main.thor")
|
66
|
+
package = :directory
|
67
|
+
contents = File.open(base, &:read)
|
68
|
+
else
|
69
|
+
base = name
|
70
|
+
package = :file
|
71
|
+
require "open-uri"
|
72
|
+
contents = URI.open(name, &:read)
|
73
|
+
end
|
74
|
+
rescue Errno::ENOENT
|
75
|
+
raise Error, "Error opening file '#{name}'"
|
61
76
|
end
|
62
|
-
rescue OpenURI::HTTPError
|
63
|
-
raise Error, "Error opening URI '#{name}'"
|
64
|
-
rescue Errno::ENOENT
|
65
|
-
raise Error, "Error opening file '#{name}'"
|
66
77
|
end
|
67
78
|
|
68
79
|
say "Your Thorfile contains:"
|
@@ -83,16 +94,16 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
|
83
94
|
as = basename if as.empty?
|
84
95
|
end
|
85
96
|
|
86
|
-
location = if options[:relative] ||
|
97
|
+
location = if options[:relative] || is_uri
|
87
98
|
name
|
88
99
|
else
|
89
100
|
File.expand_path(name)
|
90
101
|
end
|
91
102
|
|
92
103
|
thor_yaml[as] = {
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
104
|
+
filename: Digest::SHA256.hexdigest(name + as),
|
105
|
+
location: location,
|
106
|
+
namespaces: Thor::Util.namespaces_in_content(contents, base)
|
96
107
|
}
|
97
108
|
|
98
109
|
save_yaml(thor_yaml)
|
@@ -111,7 +122,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
|
111
122
|
|
112
123
|
desc "version", "Show Thor version"
|
113
124
|
def version
|
114
|
-
|
125
|
+
require_relative "version"
|
115
126
|
say "Thor #{Thor::VERSION}"
|
116
127
|
end
|
117
128
|
|
@@ -153,14 +164,14 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
|
153
164
|
end
|
154
165
|
|
155
166
|
desc "installed", "List the installed Thor modules and commands"
|
156
|
-
method_options :
|
167
|
+
method_options internal: :boolean
|
157
168
|
def installed
|
158
169
|
initialize_thorfiles(nil, true)
|
159
170
|
display_klasses(true, options["internal"])
|
160
171
|
end
|
161
172
|
|
162
173
|
desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
|
163
|
-
method_options :
|
174
|
+
method_options substring: :boolean, group: :string, all: :boolean, debug: :boolean
|
164
175
|
def list(search = "")
|
165
176
|
initialize_thorfiles
|
166
177
|
|
@@ -302,7 +313,7 @@ private
|
|
302
313
|
say shell.set_color(namespace, :blue, true)
|
303
314
|
say "-" * namespace.size
|
304
315
|
|
305
|
-
print_table(list, :
|
316
|
+
print_table(list, truncate: true)
|
306
317
|
say
|
307
318
|
end
|
308
319
|
alias_method :display_tasks, :display_commands
|