thor 0.18.1 → 0.19.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 +7 -0
- data/README.md +13 -7
- data/Thorfile +4 -5
- data/bin/thor +1 -1
- data/lib/thor.rb +78 -67
- data/lib/thor/actions.rb +57 -56
- data/lib/thor/actions/create_file.rb +33 -35
- data/lib/thor/actions/create_link.rb +2 -3
- data/lib/thor/actions/directory.rb +37 -38
- data/lib/thor/actions/empty_directory.rb +67 -69
- data/lib/thor/actions/file_manipulation.rb +17 -15
- data/lib/thor/actions/inject_into_file.rb +27 -29
- data/lib/thor/base.rb +193 -189
- data/lib/thor/command.rb +20 -23
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +21 -24
- data/lib/thor/core_ext/io_binary_read.rb +2 -4
- data/lib/thor/core_ext/ordered_hash.rb +9 -11
- data/lib/thor/error.rb +5 -1
- data/lib/thor/group.rb +53 -54
- data/lib/thor/invocation.rb +44 -38
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/line_editor/basic.rb +35 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/parser/argument.rb +28 -29
- data/lib/thor/parser/arguments.rb +102 -98
- data/lib/thor/parser/option.rb +26 -22
- data/lib/thor/parser/options.rb +86 -86
- data/lib/thor/rake_compat.rb +9 -10
- data/lib/thor/runner.rb +141 -141
- data/lib/thor/shell.rb +27 -34
- data/lib/thor/shell/basic.rb +91 -63
- data/lib/thor/shell/color.rb +44 -43
- data/lib/thor/shell/html.rb +59 -60
- data/lib/thor/util.rb +24 -27
- data/lib/thor/version.rb +1 -1
- data/spec/actions/create_file_spec.rb +25 -27
- data/spec/actions/create_link_spec.rb +19 -18
- data/spec/actions/directory_spec.rb +31 -31
- data/spec/actions/empty_directory_spec.rb +18 -18
- data/spec/actions/file_manipulation_spec.rb +38 -28
- data/spec/actions/inject_into_file_spec.rb +13 -13
- data/spec/actions_spec.rb +43 -43
- data/spec/base_spec.rb +45 -38
- data/spec/command_spec.rb +13 -14
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +19 -19
- data/spec/core_ext/ordered_hash_spec.rb +6 -6
- data/spec/exit_condition_spec.rb +4 -4
- data/spec/fixtures/invoke.thor +19 -0
- data/spec/fixtures/script.thor +1 -1
- data/spec/group_spec.rb +30 -24
- data/spec/helper.rb +28 -15
- data/spec/invocation_spec.rb +39 -19
- data/spec/line_editor/basic_spec.rb +28 -0
- data/spec/line_editor/readline_spec.rb +69 -0
- data/spec/line_editor_spec.rb +43 -0
- data/spec/parser/argument_spec.rb +12 -12
- data/spec/parser/arguments_spec.rb +11 -11
- data/spec/parser/option_spec.rb +33 -25
- data/spec/parser/options_spec.rb +66 -52
- data/spec/quality_spec.rb +75 -0
- data/spec/rake_compat_spec.rb +10 -10
- data/spec/register_spec.rb +60 -30
- data/spec/runner_spec.rb +67 -62
- data/spec/sandbox/application.rb +2 -0
- data/spec/sandbox/app{1}/README +3 -0
- data/spec/sandbox/bundle/execute.rb +6 -0
- data/spec/sandbox/bundle/main.thor +1 -0
- data/spec/sandbox/command.thor +10 -0
- data/spec/sandbox/doc/%file_name%.rb.tt +1 -0
- data/spec/sandbox/doc/COMMENTER +11 -0
- data/spec/sandbox/doc/README +3 -0
- data/spec/sandbox/doc/block_helper.rb +3 -0
- data/spec/sandbox/doc/config.rb +1 -0
- data/spec/sandbox/doc/config.yaml.tt +1 -0
- data/spec/sandbox/doc/excluding/%file_name%.rb.tt +1 -0
- data/spec/sandbox/enum.thor +10 -0
- data/spec/sandbox/group.thor +128 -0
- data/spec/sandbox/invoke.thor +131 -0
- data/spec/sandbox/path with spaces b/data/spec/sandbox/path with → spaces +0 -0
- data/spec/sandbox/preserve/script.sh +3 -0
- data/spec/sandbox/script.thor +220 -0
- data/spec/sandbox/subcommand.thor +17 -0
- data/spec/shell/basic_spec.rb +107 -86
- data/spec/shell/color_spec.rb +32 -8
- data/spec/shell/html_spec.rb +3 -4
- data/spec/shell_spec.rb +7 -7
- data/spec/subcommand_spec.rb +20 -2
- data/spec/thor_spec.rb +111 -97
- data/spec/util_spec.rb +30 -30
- data/thor.gemspec +14 -14
- metadata +69 -25
data/lib/thor/parser/option.rb
CHANGED
@@ -4,7 +4,7 @@ class Thor
|
|
4
4
|
|
5
5
|
VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
|
6
6
|
|
7
|
-
def initialize(name, options={})
|
7
|
+
def initialize(name, options = {})
|
8
8
|
options[:required] = false unless options.key?(:required)
|
9
9
|
super
|
10
10
|
@lazy_default = options[:lazy_default]
|
@@ -40,7 +40,7 @@ class Thor
|
|
40
40
|
#
|
41
41
|
# By default all options are optional, unless :required is given.
|
42
42
|
#
|
43
|
-
def self.parse(key, value)
|
43
|
+
def self.parse(key, value) # rubocop:disable MethodLength
|
44
44
|
if key.is_a?(Array)
|
45
45
|
name, *aliases = key
|
46
46
|
else
|
@@ -51,21 +51,21 @@ class Thor
|
|
51
51
|
default = value
|
52
52
|
|
53
53
|
type = case value
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
54
|
+
when Symbol
|
55
|
+
default = nil
|
56
|
+
if VALID_TYPES.include?(value)
|
57
|
+
value
|
58
|
+
elsif required = (value == :required) # rubocop:disable AssignmentInCondition
|
59
|
+
:string
|
60
|
+
end
|
61
|
+
when TrueClass, FalseClass
|
62
|
+
:boolean
|
63
|
+
when Numeric
|
64
|
+
:numeric
|
65
|
+
when Hash, Array, String
|
66
|
+
value.class.name.downcase.to_sym
|
67
|
+
end
|
68
|
+
new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases)
|
69
69
|
end
|
70
70
|
|
71
71
|
def switch_name
|
@@ -76,7 +76,7 @@ class Thor
|
|
76
76
|
@human_name ||= dasherized? ? undasherize(name) : name
|
77
77
|
end
|
78
78
|
|
79
|
-
def usage(padding=0)
|
79
|
+
def usage(padding = 0)
|
80
80
|
sample = if banner && !banner.to_s.empty?
|
81
81
|
"#{switch_name}=#{banner}"
|
82
82
|
else
|
@@ -85,6 +85,10 @@ class Thor
|
|
85
85
|
|
86
86
|
sample = "[#{sample}]" unless required?
|
87
87
|
|
88
|
+
if boolean?
|
89
|
+
sample << ", [#{dasherize("no-" + human_name)}]" unless name == "force"
|
90
|
+
end
|
91
|
+
|
88
92
|
if aliases.empty?
|
89
93
|
(" " * padding) << sample
|
90
94
|
else
|
@@ -103,19 +107,19 @@ class Thor
|
|
103
107
|
protected
|
104
108
|
|
105
109
|
def validate!
|
106
|
-
|
110
|
+
fail ArgumentError, "An option cannot be boolean and required." if boolean? && required?
|
107
111
|
end
|
108
112
|
|
109
113
|
def dasherized?
|
110
|
-
name.index(
|
114
|
+
name.index("-") == 0
|
111
115
|
end
|
112
116
|
|
113
117
|
def undasherize(str)
|
114
|
-
str.sub(/^-{1,2}/,
|
118
|
+
str.sub(/^-{1,2}/, "")
|
115
119
|
end
|
116
120
|
|
117
121
|
def dasherize(str)
|
118
|
-
(str.length > 1 ? "--" : "-") + str.gsub(
|
122
|
+
(str.length > 1 ? "--" : "-") + str.gsub("_", "-")
|
119
123
|
end
|
120
124
|
end
|
121
125
|
end
|
data/lib/thor/parser/options.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
class Thor
|
2
|
-
class Options < Arguments #:nodoc:
|
2
|
+
class Options < Arguments #:nodoc: # rubocop:disable ClassLength
|
3
3
|
LONG_RE = /^(--\w+(?:-\w+)*)$/
|
4
4
|
SHORT_RE = /^(-[a-z])$/i
|
5
5
|
EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i
|
6
6
|
SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
|
7
7
|
SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
|
8
|
-
OPTS_END =
|
8
|
+
OPTS_END = "--".freeze
|
9
9
|
|
10
10
|
# Receives a hash and makes it switches.
|
11
11
|
def self.to_switches(options)
|
@@ -14,9 +14,9 @@ class Thor
|
|
14
14
|
when true
|
15
15
|
"--#{key}"
|
16
16
|
when Array
|
17
|
-
"--#{key} #{value.map{ |v| v.inspect }.join(' ')}"
|
17
|
+
"--#{key} #{value.map { |v| v.inspect }.join(' ')}"
|
18
18
|
when Hash
|
19
|
-
"--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}"
|
19
|
+
"--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}"
|
20
20
|
when nil, false
|
21
21
|
""
|
22
22
|
else
|
@@ -29,7 +29,7 @@ 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)
|
32
|
+
def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false)
|
33
33
|
@stop_on_unknown = stop_on_unknown
|
34
34
|
options = hash_options.values
|
35
35
|
super(options)
|
@@ -46,13 +46,13 @@ class Thor
|
|
46
46
|
@switches[option.switch_name] = option
|
47
47
|
|
48
48
|
option.aliases.each do |short|
|
49
|
-
name = short.to_s.sub(/^(?!\-)/,
|
49
|
+
name = short.to_s.sub(/^(?!\-)/, "-")
|
50
50
|
@shorts[name] ||= option.switch_name
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
def remaining
|
55
|
+
def remaining # rubocop:disable TrivialAccessors
|
56
56
|
@extra
|
57
57
|
end
|
58
58
|
|
@@ -69,7 +69,7 @@ class Thor
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
def parse(args)
|
72
|
+
def parse(args) # rubocop:disable MethodLength
|
73
73
|
@pile = args.dup
|
74
74
|
@parsing_options = true
|
75
75
|
|
@@ -81,7 +81,7 @@ class Thor
|
|
81
81
|
if is_switch
|
82
82
|
case shifted
|
83
83
|
when SHORT_SQ_RE
|
84
|
-
unshift($1.split(
|
84
|
+
unshift($1.split("").map { |f| "-#{f}" })
|
85
85
|
next
|
86
86
|
when EQ_RE, SHORT_NUM
|
87
87
|
unshift($2)
|
@@ -119,100 +119,100 @@ class Thor
|
|
119
119
|
def check_unknown!
|
120
120
|
# an unknown option starts with - or -- and has no more --'s afterward.
|
121
121
|
unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
|
122
|
-
|
122
|
+
fail UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
|
123
123
|
end
|
124
124
|
|
125
|
-
|
126
|
-
|
127
|
-
# Check if the current value in peek is a registered switch.
|
128
|
-
#
|
129
|
-
# Two booleans are returned. The first is true if the current value
|
130
|
-
# starts with a hyphen; the second is true if it is a registered switch.
|
131
|
-
def current_is_switch?
|
132
|
-
case peek
|
133
|
-
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
|
134
|
-
[true, switch?($1)]
|
135
|
-
when SHORT_SQ_RE
|
136
|
-
[true, $1.split('').any? { |f| switch?("-#{f}") }]
|
137
|
-
else
|
138
|
-
[false, false]
|
139
|
-
end
|
140
|
-
end
|
125
|
+
protected
|
141
126
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
127
|
+
# Check if the current value in peek is a registered switch.
|
128
|
+
#
|
129
|
+
# Two booleans are returned. The first is true if the current value
|
130
|
+
# starts with a hyphen; the second is true if it is a registered switch.
|
131
|
+
def current_is_switch?
|
132
|
+
case peek
|
133
|
+
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
|
134
|
+
[true, switch?($1)]
|
135
|
+
when SHORT_SQ_RE
|
136
|
+
[true, $1.split("").any? { |f| switch?("-#{f}") }]
|
137
|
+
else
|
138
|
+
[false, false]
|
149
139
|
end
|
140
|
+
end
|
150
141
|
|
151
|
-
|
152
|
-
|
142
|
+
def current_is_switch_formatted?
|
143
|
+
case peek
|
144
|
+
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
|
145
|
+
true
|
146
|
+
else
|
147
|
+
false
|
153
148
|
end
|
149
|
+
end
|
154
150
|
|
155
|
-
|
156
|
-
|
157
|
-
|
151
|
+
def current_is_value?
|
152
|
+
peek && (!parsing_options? || super)
|
153
|
+
end
|
158
154
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
else
|
163
|
-
@switches[arg]
|
164
|
-
end
|
165
|
-
end
|
155
|
+
def switch?(arg)
|
156
|
+
switch_option(normalize_switch(arg))
|
157
|
+
end
|
166
158
|
|
167
|
-
|
168
|
-
#
|
169
|
-
|
170
|
-
|
159
|
+
def switch_option(arg)
|
160
|
+
if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition
|
161
|
+
@switches[arg] || @switches["--#{match}"]
|
162
|
+
else
|
163
|
+
@switches[arg]
|
171
164
|
end
|
165
|
+
end
|
172
166
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
167
|
+
# Check if the given argument is actually a shortcut.
|
168
|
+
#
|
169
|
+
def normalize_switch(arg)
|
170
|
+
(@shorts[arg] || arg).tr("_", "-")
|
171
|
+
end
|
177
172
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
173
|
+
def parsing_options?
|
174
|
+
peek
|
175
|
+
@parsing_options
|
176
|
+
end
|
177
|
+
|
178
|
+
# Parse boolean values which can be given as --foo=true, --foo or --no-foo.
|
179
|
+
#
|
180
|
+
def parse_boolean(switch)
|
181
|
+
if current_is_value?
|
182
|
+
if ["true", "TRUE", "t", "T", true].include?(peek)
|
183
|
+
shift
|
184
|
+
true
|
185
|
+
elsif ["false", "FALSE", "f", "F", false].include?(peek)
|
186
|
+
shift
|
187
|
+
false
|
191
188
|
else
|
192
|
-
|
189
|
+
true
|
193
190
|
end
|
191
|
+
else
|
192
|
+
@switches.key?(switch) || !no_or_skip?(switch)
|
194
193
|
end
|
194
|
+
end
|
195
195
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
end
|
196
|
+
# Parse the value at the peek analyzing if it requires an input or not.
|
197
|
+
#
|
198
|
+
def parse_peek(switch, option)
|
199
|
+
if parsing_options? && (current_is_switch_formatted? || last?)
|
200
|
+
if option.boolean?
|
201
|
+
# No problem for boolean types
|
202
|
+
elsif no_or_skip?(switch)
|
203
|
+
return nil # User set value to nil
|
204
|
+
elsif option.string? && !option.required?
|
205
|
+
# Return the default if there is one, else the human name
|
206
|
+
return option.lazy_default || option.default || option.human_name
|
207
|
+
elsif option.lazy_default
|
208
|
+
return option.lazy_default
|
209
|
+
else
|
210
|
+
fail MalformattedArgumentError, "No value provided for option '#{switch}'"
|
212
211
|
end
|
213
|
-
|
214
|
-
@non_assigned_required.delete(option)
|
215
|
-
send(:"parse_#{option.type}", switch)
|
216
212
|
end
|
213
|
+
|
214
|
+
@non_assigned_required.delete(option)
|
215
|
+
send(:"parse_#{option.type}", switch)
|
216
|
+
end
|
217
217
|
end
|
218
218
|
end
|
data/lib/thor/rake_compat.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rake"
|
2
|
+
require "rake/dsl_definition"
|
3
3
|
|
4
4
|
class Thor
|
5
5
|
# Adds a compatibility layer to your Thor classes which allows you to use
|
@@ -12,7 +12,7 @@ class Thor
|
|
12
12
|
# include Thor::RakeCompat
|
13
13
|
#
|
14
14
|
# RSpec::Core::RakeTask.new(:spec) do |t|
|
15
|
-
# t.spec_opts = ['--options',
|
15
|
+
# t.spec_opts = ['--options', './.rspec']
|
16
16
|
# t.spec_files = FileList['spec/**/*_spec.rb']
|
17
17
|
# end
|
18
18
|
# end
|
@@ -28,23 +28,23 @@ class Thor
|
|
28
28
|
# Hack. Make rakefile point to invoker, so rdoc task is generated properly.
|
29
29
|
rakefile = File.basename(caller[0].match(/(.*):\d+/)[1])
|
30
30
|
Rake.application.instance_variable_set(:@rakefile, rakefile)
|
31
|
-
|
31
|
+
rake_classes << base
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
# override task on (main), for compatibility with Rake 0.9
|
37
|
-
|
37
|
+
instance_eval do
|
38
38
|
alias rake_namespace namespace
|
39
39
|
|
40
40
|
def task(*)
|
41
41
|
task = super
|
42
42
|
|
43
|
-
if klass = Thor::RakeCompat.rake_classes.last
|
44
|
-
non_namespaced_name = task.name.split(
|
43
|
+
if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
|
44
|
+
non_namespaced_name = task.name.split(":").last
|
45
45
|
|
46
46
|
description = non_namespaced_name
|
47
|
-
description << task.arg_names.map{ |n| n.to_s.upcase }.join(
|
47
|
+
description << task.arg_names.map { |n| n.to_s.upcase }.join(" ")
|
48
48
|
description.strip!
|
49
49
|
|
50
50
|
klass.desc description, Rake.application.last_description || non_namespaced_name
|
@@ -58,7 +58,7 @@ self.instance_eval do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def namespace(name)
|
61
|
-
if klass = Thor::RakeCompat.rake_classes.last
|
61
|
+
if klass = Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition
|
62
62
|
const_name = Thor::Util.camel_case(name.to_s).to_sym
|
63
63
|
klass.const_set(const_name, Class.new(Thor))
|
64
64
|
new_klass = klass.const_get(const_name)
|
@@ -69,4 +69,3 @@ self.instance_eval do
|
|
69
69
|
Thor::RakeCompat.rake_classes.pop
|
70
70
|
end
|
71
71
|
end
|
72
|
-
|
data/lib/thor/runner.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "thor"
|
2
|
+
require "thor/group"
|
3
|
+
require "thor/core_ext/io_binary_read"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
5
|
+
require "fileutils"
|
6
|
+
require "open-uri"
|
7
|
+
require "yaml"
|
8
|
+
require "digest/md5"
|
9
|
+
require "pathname"
|
10
10
|
|
11
|
-
class Thor::Runner < Thor #:nodoc:
|
11
|
+
class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
12
12
|
map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
|
13
13
|
|
14
14
|
# Override Thor#help so it can give information about any class and any method.
|
@@ -18,7 +18,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
18
18
|
initialize_thorfiles(meth)
|
19
19
|
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
20
20
|
self.class.handle_no_command_error(command, false) if klass.nil?
|
21
|
-
klass.start(["-h", command].compact, :shell =>
|
21
|
+
klass.start(["-h", command].compact, :shell => shell)
|
22
22
|
else
|
23
23
|
super
|
24
24
|
end
|
@@ -33,12 +33,12 @@ class Thor::Runner < Thor #:nodoc:
|
|
33
33
|
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
34
34
|
self.class.handle_no_command_error(command, false) if klass.nil?
|
35
35
|
args.unshift(command) if command
|
36
|
-
klass.start(args, :shell =>
|
36
|
+
klass.start(args, :shell => shell)
|
37
37
|
end
|
38
38
|
|
39
39
|
desc "install NAME", "Install an optionally named Thor file into your system commands"
|
40
40
|
method_options :as => :string, :relative => :boolean, :force => :boolean
|
41
|
-
def install(name)
|
41
|
+
def install(name) # rubocop:disable MethodLength
|
42
42
|
initialize_thorfiles
|
43
43
|
|
44
44
|
# If a directory name is provided as the argument, look for a 'main.thor'
|
@@ -46,15 +46,15 @@ class Thor::Runner < Thor #:nodoc:
|
|
46
46
|
begin
|
47
47
|
if File.directory?(File.expand_path(name))
|
48
48
|
base, package = File.join(name, "main.thor"), :directory
|
49
|
-
contents = open(base) {|input| input.read }
|
49
|
+
contents = open(base) { |input| input.read }
|
50
50
|
else
|
51
51
|
base, package = name, :file
|
52
|
-
contents = open(name) {|input| input.read }
|
52
|
+
contents = open(name) { |input| input.read }
|
53
53
|
end
|
54
54
|
rescue OpenURI::HTTPError
|
55
55
|
raise Error, "Error opening URI '#{name}'"
|
56
56
|
rescue Errno::ENOENT
|
57
|
-
|
57
|
+
fail Error, "Error opening file '#{name}'"
|
58
58
|
end
|
59
59
|
|
60
60
|
say "Your Thorfile contains:"
|
@@ -75,7 +75,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
75
75
|
as = basename if as.empty?
|
76
76
|
end
|
77
77
|
|
78
|
-
location = if options[:relative] || name =~
|
78
|
+
location = if options[:relative] || name =~ %r{^https?://}
|
79
79
|
name
|
80
80
|
else
|
81
81
|
File.expand_path(name)
|
@@ -102,13 +102,13 @@ class Thor::Runner < Thor #:nodoc:
|
|
102
102
|
|
103
103
|
desc "version", "Show Thor version"
|
104
104
|
def version
|
105
|
-
require
|
105
|
+
require "thor/version"
|
106
106
|
say "Thor #{Thor::VERSION}"
|
107
107
|
end
|
108
108
|
|
109
109
|
desc "uninstall NAME", "Uninstall a named Thor module"
|
110
110
|
def uninstall(name)
|
111
|
-
|
111
|
+
fail Error, "Can't find module '#{name}'" unless thor_yaml[name]
|
112
112
|
say "Uninstalling #{name}."
|
113
113
|
FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}"))
|
114
114
|
|
@@ -120,12 +120,12 @@ class Thor::Runner < Thor #:nodoc:
|
|
120
120
|
|
121
121
|
desc "update NAME", "Update a Thor file from its original location"
|
122
122
|
def update(name)
|
123
|
-
|
123
|
+
fail Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location]
|
124
124
|
|
125
125
|
say "Updating '#{name}' from #{thor_yaml[name][:location]}"
|
126
126
|
|
127
127
|
old_filename = thor_yaml[name][:filename]
|
128
|
-
self.options =
|
128
|
+
self.options = options.merge("as" => name)
|
129
129
|
|
130
130
|
if File.directory? File.expand_path(name)
|
131
131
|
FileUtils.rm_rf(File.join(thor_root, old_filename))
|
@@ -152,7 +152,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
152
152
|
|
153
153
|
desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
|
154
154
|
method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
|
155
|
-
def list(search="")
|
155
|
+
def list(search = "")
|
156
156
|
initialize_thorfiles
|
157
157
|
|
158
158
|
search = ".*#{search}" if options["substring"]
|
@@ -166,157 +166,157 @@ class Thor::Runner < Thor #:nodoc:
|
|
166
166
|
display_klasses(false, false, klasses)
|
167
167
|
end
|
168
168
|
|
169
|
-
|
169
|
+
private
|
170
170
|
|
171
|
-
|
172
|
-
|
173
|
-
|
171
|
+
def self.banner(command, all = false, subcommand = false)
|
172
|
+
"thor " + command.formatted_usage(self, all, subcommand)
|
173
|
+
end
|
174
174
|
|
175
|
-
|
176
|
-
|
177
|
-
|
175
|
+
def thor_root
|
176
|
+
Thor::Util.thor_root
|
177
|
+
end
|
178
178
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
end
|
179
|
+
def thor_yaml
|
180
|
+
@thor_yaml ||= begin
|
181
|
+
yaml_file = File.join(thor_root, "thor.yml")
|
182
|
+
yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file)
|
183
|
+
yaml || {}
|
185
184
|
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Save the yaml file. If none exists in thor root, creates one.
|
188
|
+
#
|
189
|
+
def save_yaml(yaml)
|
190
|
+
yaml_file = File.join(thor_root, "thor.yml")
|
186
191
|
|
187
|
-
|
188
|
-
|
189
|
-
def save_yaml(yaml)
|
192
|
+
unless File.exist?(yaml_file)
|
193
|
+
FileUtils.mkdir_p(thor_root)
|
190
194
|
yaml_file = File.join(thor_root, "thor.yml")
|
195
|
+
FileUtils.touch(yaml_file)
|
196
|
+
end
|
191
197
|
|
192
|
-
|
193
|
-
|
194
|
-
yaml_file = File.join(thor_root, "thor.yml")
|
195
|
-
FileUtils.touch(yaml_file)
|
196
|
-
end
|
198
|
+
File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
|
199
|
+
end
|
197
200
|
|
198
|
-
|
199
|
-
|
201
|
+
def self.exit_on_failure?
|
202
|
+
true
|
203
|
+
end
|
200
204
|
|
201
|
-
|
202
|
-
|
205
|
+
# Load the Thorfiles. If relevant_to is supplied, looks for specific files
|
206
|
+
# in the thor_root instead of loading them all.
|
207
|
+
#
|
208
|
+
# By default, it also traverses the current path until find Thor files, as
|
209
|
+
# described in thorfiles. This look up can be skipped by supplying
|
210
|
+
# skip_lookup true.
|
211
|
+
#
|
212
|
+
def initialize_thorfiles(relevant_to = nil, skip_lookup = false)
|
213
|
+
thorfiles(relevant_to, skip_lookup).each do |f|
|
214
|
+
Thor::Util.load_thorfile(f, nil, options[:debug]) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f))
|
203
215
|
end
|
216
|
+
end
|
204
217
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
218
|
+
# Finds Thorfiles by traversing from your current directory down to the root
|
219
|
+
# directory of your system. If at any time we find a Thor file, we stop.
|
220
|
+
#
|
221
|
+
# We also ensure that system-wide Thorfiles are loaded first, so local
|
222
|
+
# Thorfiles can override them.
|
223
|
+
#
|
224
|
+
# ==== Example
|
225
|
+
#
|
226
|
+
# If we start at /Users/wycats/dev/thor ...
|
227
|
+
#
|
228
|
+
# 1. /Users/wycats/dev/thor
|
229
|
+
# 2. /Users/wycats/dev
|
230
|
+
# 3. /Users/wycats <-- we find a Thorfile here, so we stop
|
231
|
+
#
|
232
|
+
# Suppose we start at c:\Documents and Settings\james\dev\thor ...
|
233
|
+
#
|
234
|
+
# 1. c:\Documents and Settings\james\dev\thor
|
235
|
+
# 2. c:\Documents and Settings\james\dev
|
236
|
+
# 3. c:\Documents and Settings\james
|
237
|
+
# 4. c:\Documents and Settings
|
238
|
+
# 5. c:\ <-- no Thorfiles found!
|
239
|
+
#
|
240
|
+
def thorfiles(relevant_to = nil, skip_lookup = false)
|
241
|
+
thorfiles = []
|
217
242
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
# Thorfiles can override them.
|
223
|
-
#
|
224
|
-
# ==== Example
|
225
|
-
#
|
226
|
-
# If we start at /Users/wycats/dev/thor ...
|
227
|
-
#
|
228
|
-
# 1. /Users/wycats/dev/thor
|
229
|
-
# 2. /Users/wycats/dev
|
230
|
-
# 3. /Users/wycats <-- we find a Thorfile here, so we stop
|
231
|
-
#
|
232
|
-
# Suppose we start at c:\Documents and Settings\james\dev\thor ...
|
233
|
-
#
|
234
|
-
# 1. c:\Documents and Settings\james\dev\thor
|
235
|
-
# 2. c:\Documents and Settings\james\dev
|
236
|
-
# 3. c:\Documents and Settings\james
|
237
|
-
# 4. c:\Documents and Settings
|
238
|
-
# 5. c:\ <-- no Thorfiles found!
|
239
|
-
#
|
240
|
-
def thorfiles(relevant_to=nil, skip_lookup=false)
|
241
|
-
thorfiles = []
|
242
|
-
|
243
|
-
unless skip_lookup
|
244
|
-
Pathname.pwd.ascend do |path|
|
245
|
-
thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
|
246
|
-
break unless thorfiles.empty?
|
247
|
-
end
|
243
|
+
unless skip_lookup
|
244
|
+
Pathname.pwd.ascend do |path|
|
245
|
+
thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
|
246
|
+
break unless thorfiles.empty?
|
248
247
|
end
|
248
|
+
end
|
249
249
|
|
250
|
-
|
251
|
-
|
252
|
-
|
250
|
+
files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob)
|
251
|
+
files += thorfiles
|
252
|
+
files -= ["#{thor_root}/thor.yml"]
|
253
253
|
|
254
|
-
|
255
|
-
|
256
|
-
end
|
254
|
+
files.map! do |file|
|
255
|
+
File.directory?(file) ? File.join(file, "main.thor") : file
|
257
256
|
end
|
257
|
+
end
|
258
258
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
files = thor_yaml.select do |k, v|
|
267
|
-
v[:namespaces] && !(v[:namespaces] & lookup).empty?
|
268
|
-
end
|
259
|
+
# Load Thorfiles relevant to the given method. If you provide "foo:bar" it
|
260
|
+
# will load all thor files in the thor.yaml that has "foo" e "foo:bar"
|
261
|
+
# namespaces registered.
|
262
|
+
#
|
263
|
+
def thorfiles_relevant_to(meth)
|
264
|
+
lookup = [meth, meth.split(":")[0...-1].join(":")]
|
269
265
|
|
270
|
-
|
266
|
+
files = thor_yaml.select do |k, v|
|
267
|
+
v[:namespaces] && !(v[:namespaces] & lookup).empty?
|
271
268
|
end
|
272
269
|
|
273
|
-
|
274
|
-
|
275
|
-
#
|
276
|
-
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
|
277
|
-
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
270
|
+
files.map { |k, v| File.join(thor_root, "#{v[:filename]}") }
|
271
|
+
end
|
278
272
|
|
279
|
-
|
280
|
-
|
273
|
+
# Display information about the given klasses. If with_module is given,
|
274
|
+
# it shows a table with information extracted from the yaml file.
|
275
|
+
#
|
276
|
+
def display_klasses(with_modules = false, show_internal = false, klasses = Thor::Base.subclasses)
|
277
|
+
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
281
278
|
|
282
|
-
|
283
|
-
|
279
|
+
fail Error, "No Thor commands available" if klasses.empty?
|
280
|
+
show_modules if with_modules && !thor_yaml.empty?
|
284
281
|
|
285
|
-
|
286
|
-
|
282
|
+
list = Hash.new { |h, k| h[k] = [] }
|
283
|
+
groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
|
287
284
|
|
288
|
-
|
289
|
-
|
290
|
-
list["root"] = groups
|
285
|
+
# Get classes which inherit from Thor
|
286
|
+
(klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) }
|
291
287
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
end
|
288
|
+
# Get classes which inherit from Thor::Base
|
289
|
+
groups.map! { |k| k.printable_commands(false).first }
|
290
|
+
list["root"] = groups
|
296
291
|
|
297
|
-
|
298
|
-
|
292
|
+
# Order namespaces with default coming first
|
293
|
+
list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") }
|
294
|
+
list.each { |n, commands| display_commands(n, commands) unless commands.empty? }
|
295
|
+
end
|
299
296
|
|
300
|
-
|
301
|
-
|
297
|
+
def display_commands(namespace, list) #:nodoc:
|
298
|
+
list.sort! { |a, b| a[0] <=> b[0] }
|
302
299
|
|
303
|
-
|
304
|
-
|
305
|
-
end
|
306
|
-
alias display_tasks display_commands
|
300
|
+
say shell.set_color(namespace, :blue, true)
|
301
|
+
say "-" * namespace.size
|
307
302
|
|
308
|
-
|
309
|
-
|
310
|
-
|
303
|
+
print_table(list, :truncate => true)
|
304
|
+
say
|
305
|
+
end
|
306
|
+
alias_method :display_tasks, :display_commands
|
311
307
|
|
312
|
-
|
313
|
-
|
308
|
+
def show_modules #:nodoc:
|
309
|
+
info = []
|
310
|
+
labels = %w[Modules Namespaces]
|
314
311
|
|
315
|
-
|
316
|
-
|
317
|
-
end
|
312
|
+
info << labels
|
313
|
+
info << ["-" * labels[0].size, "-" * labels[1].size]
|
318
314
|
|
319
|
-
|
320
|
-
|
315
|
+
thor_yaml.each do |name, hash|
|
316
|
+
info << [name, hash[:namespaces].join(", ")]
|
321
317
|
end
|
318
|
+
|
319
|
+
print_table info
|
320
|
+
say ""
|
321
|
+
end
|
322
322
|
end
|