engineyard 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/engineyard/version.rb +2 -2
- data/lib/vendor/thor/README.md +10 -3
- data/lib/vendor/thor/lib/thor.rb +247 -153
- data/lib/vendor/thor/lib/thor/actions.rb +18 -18
- data/lib/vendor/thor/lib/thor/actions/create_link.rb +3 -0
- data/lib/vendor/thor/lib/thor/actions/directory.rb +31 -10
- data/lib/vendor/thor/lib/thor/actions/empty_directory.rb +3 -19
- data/lib/vendor/thor/lib/thor/actions/file_manipulation.rb +9 -3
- data/lib/vendor/thor/lib/thor/base.rb +99 -88
- data/lib/vendor/thor/lib/thor/{task.rb → command.rb} +19 -15
- data/lib/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +5 -0
- data/lib/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/vendor/thor/lib/thor/error.rb +4 -11
- data/lib/vendor/thor/lib/thor/group.rb +35 -38
- data/lib/vendor/thor/lib/thor/invocation.rb +28 -26
- data/lib/vendor/thor/lib/thor/parser/options.rb +66 -26
- data/lib/vendor/thor/lib/thor/rake_compat.rb +3 -2
- data/lib/vendor/thor/lib/thor/runner.rb +21 -20
- data/lib/vendor/thor/lib/thor/shell/basic.rb +21 -17
- data/lib/vendor/thor/lib/thor/shell/color.rb +13 -9
- data/lib/vendor/thor/lib/thor/shell/html.rb +13 -9
- data/lib/vendor/thor/lib/thor/util.rb +214 -210
- data/lib/vendor/thor/lib/thor/version.rb +1 -1
- data/lib/vendor/thor/thor.gemspec +22 -24
- metadata +8 -9
- data/lib/vendor/thor/lib/thor/core_ext/dir_escape.rb +0 -0
- data/lib/vendor/thor/lib/thor/core_ext/file_binary_read.rb +0 -9
@@ -5,27 +5,32 @@ class Thor
|
|
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 = '--'.freeze
|
8
9
|
|
9
10
|
# Receives a hash and makes it switches.
|
10
11
|
def self.to_switches(options)
|
11
12
|
options.map do |key, value|
|
12
13
|
case value
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
14
|
+
when true
|
15
|
+
"--#{key}"
|
16
|
+
when Array
|
17
|
+
"--#{key} #{value.map{ |v| v.inspect }.join(' ')}"
|
18
|
+
when Hash
|
19
|
+
"--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}"
|
20
|
+
when nil, false
|
21
|
+
""
|
22
|
+
else
|
23
|
+
"--#{key} #{value.inspect}"
|
23
24
|
end
|
24
25
|
end.join(" ")
|
25
26
|
end
|
26
27
|
|
27
28
|
# Takes a hash of Thor::Option and a hash with defaults.
|
28
|
-
|
29
|
+
#
|
30
|
+
# If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
|
31
|
+
# an unknown option or a regular argument.
|
32
|
+
def initialize(hash_options={}, defaults={}, stop_on_unknown=false)
|
33
|
+
@stop_on_unknown = stop_on_unknown
|
29
34
|
options = hash_options.values
|
30
35
|
super(options)
|
31
36
|
|
@@ -41,7 +46,8 @@ class Thor
|
|
41
46
|
@switches[option.switch_name] = option
|
42
47
|
|
43
48
|
option.aliases.each do |short|
|
44
|
-
|
49
|
+
name = short.to_s.sub(/^(?!\-)/, '-')
|
50
|
+
@shorts[name] ||= option.switch_name
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
@@ -50,15 +56,30 @@ class Thor
|
|
50
56
|
@extra
|
51
57
|
end
|
52
58
|
|
59
|
+
def peek
|
60
|
+
return super unless @parsing_options
|
61
|
+
|
62
|
+
result = super
|
63
|
+
if result == OPTS_END
|
64
|
+
shift
|
65
|
+
@parsing_options = false
|
66
|
+
super
|
67
|
+
else
|
68
|
+
result
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
53
72
|
def parse(args)
|
54
73
|
@pile = args.dup
|
74
|
+
@parsing_options = true
|
55
75
|
|
56
76
|
while peek
|
57
|
-
|
58
|
-
|
77
|
+
if parsing_options?
|
78
|
+
match, is_switch = current_is_switch?
|
79
|
+
shifted = shift
|
59
80
|
|
60
|
-
|
61
|
-
|
81
|
+
if is_switch
|
82
|
+
case shifted
|
62
83
|
when SHORT_SQ_RE
|
63
84
|
unshift($1.split('').map { |f| "-#{f}" })
|
64
85
|
next
|
@@ -67,16 +88,24 @@ class Thor
|
|
67
88
|
switch = $1
|
68
89
|
when LONG_RE, SHORT_RE
|
69
90
|
switch = $1
|
91
|
+
end
|
92
|
+
|
93
|
+
switch = normalize_switch(switch)
|
94
|
+
option = switch_option(switch)
|
95
|
+
@assigns[option.human_name] = parse_peek(switch, option)
|
96
|
+
elsif @stop_on_unknown
|
97
|
+
@parsing_options = false
|
98
|
+
@extra << shifted
|
99
|
+
@extra << shift while peek
|
100
|
+
break
|
101
|
+
elsif match
|
102
|
+
@extra << shifted
|
103
|
+
@extra << shift while peek && peek !~ /^-/
|
104
|
+
else
|
105
|
+
@extra << shifted
|
70
106
|
end
|
71
|
-
|
72
|
-
switch = normalize_switch(switch)
|
73
|
-
option = switch_option(switch)
|
74
|
-
@assigns[option.human_name] = parse_peek(switch, option)
|
75
|
-
elsif match
|
76
|
-
@extra << shifted
|
77
|
-
@extra << shift while peek && peek !~ /^-/
|
78
107
|
else
|
79
|
-
@extra <<
|
108
|
+
@extra << shift
|
80
109
|
end
|
81
110
|
end
|
82
111
|
|
@@ -95,8 +124,10 @@ class Thor
|
|
95
124
|
|
96
125
|
protected
|
97
126
|
|
98
|
-
#
|
127
|
+
# Check if the current value in peek is a registered switch.
|
99
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.
|
100
131
|
def current_is_switch?
|
101
132
|
case peek
|
102
133
|
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
|
@@ -117,6 +148,10 @@ class Thor
|
|
117
148
|
end
|
118
149
|
end
|
119
150
|
|
151
|
+
def current_is_value?
|
152
|
+
peek && (!parsing_options? || super)
|
153
|
+
end
|
154
|
+
|
120
155
|
def switch?(arg)
|
121
156
|
switch_option(normalize_switch(arg))
|
122
157
|
end
|
@@ -135,6 +170,11 @@ class Thor
|
|
135
170
|
(@shorts[arg] || arg).tr('_', '-')
|
136
171
|
end
|
137
172
|
|
173
|
+
def parsing_options?
|
174
|
+
peek
|
175
|
+
@parsing_options
|
176
|
+
end
|
177
|
+
|
138
178
|
# Parse boolean values which can be given as --foo=true, --foo or --no-foo.
|
139
179
|
#
|
140
180
|
def parse_boolean(switch)
|
@@ -156,7 +196,7 @@ class Thor
|
|
156
196
|
# Parse the value at the peek analyzing if it requires an input or not.
|
157
197
|
#
|
158
198
|
def parse_peek(switch, option)
|
159
|
-
if current_is_switch_formatted? || last?
|
199
|
+
if parsing_options? && (current_is_switch_formatted? || last?)
|
160
200
|
if option.boolean?
|
161
201
|
# No problem for boolean types
|
162
202
|
elsif no_or_skip?(switch)
|
@@ -6,12 +6,13 @@ class Thor
|
|
6
6
|
# rake package tasks. For example, to use rspec rake tasks, one can do:
|
7
7
|
#
|
8
8
|
# require 'thor/rake_compat'
|
9
|
+
# require 'rspec/core/rake_task'
|
9
10
|
#
|
10
11
|
# class Default < Thor
|
11
12
|
# include Thor::RakeCompat
|
12
13
|
#
|
13
|
-
#
|
14
|
-
# t.spec_opts = ['--options', "
|
14
|
+
# RSpec::Core::RakeTask.new(:spec) do |t|
|
15
|
+
# t.spec_opts = ['--options', "./.rspec"]
|
15
16
|
# t.spec_files = FileList['spec/**/*_spec.rb']
|
16
17
|
# end
|
17
18
|
# end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'thor/group'
|
3
|
-
require 'thor/core_ext/
|
3
|
+
require 'thor/core_ext/io_binary_read'
|
4
4
|
|
5
5
|
require 'fileutils'
|
6
6
|
require 'open-uri'
|
@@ -16,33 +16,33 @@ class Thor::Runner < Thor #:nodoc:
|
|
16
16
|
def help(meth = nil)
|
17
17
|
if meth && !self.respond_to?(meth)
|
18
18
|
initialize_thorfiles(meth)
|
19
|
-
klass,
|
20
|
-
self.class.
|
21
|
-
klass.start(["-h",
|
19
|
+
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
20
|
+
self.class.handle_no_command_error(command, false) if klass.nil?
|
21
|
+
klass.start(["-h", command].compact, :shell => self.shell)
|
22
22
|
else
|
23
23
|
super
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
# If a
|
28
|
-
# Thor::Runner is then
|
27
|
+
# If a command is not found on Thor::Runner, method missing is invoked and
|
28
|
+
# Thor::Runner is then responsible for finding the command in all classes.
|
29
29
|
#
|
30
30
|
def method_missing(meth, *args)
|
31
31
|
meth = meth.to_s
|
32
32
|
initialize_thorfiles(meth)
|
33
|
-
klass,
|
34
|
-
self.class.
|
35
|
-
args.unshift(
|
33
|
+
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
34
|
+
self.class.handle_no_command_error(command, false) if klass.nil?
|
35
|
+
args.unshift(command) if command
|
36
36
|
klass.start(args, :shell => self.shell)
|
37
37
|
end
|
38
38
|
|
39
|
-
desc "install NAME", "Install an optionally named Thor file into your system
|
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
41
|
def install(name)
|
42
42
|
initialize_thorfiles
|
43
43
|
|
44
44
|
# If a directory name is provided as the argument, look for a 'main.thor'
|
45
|
-
#
|
45
|
+
# command in said directory.
|
46
46
|
begin
|
47
47
|
if File.directory?(File.expand_path(name))
|
48
48
|
base, package = File.join(name, "main.thor"), :directory
|
@@ -143,14 +143,14 @@ class Thor::Runner < Thor #:nodoc:
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
desc "installed", "List the installed Thor modules and
|
146
|
+
desc "installed", "List the installed Thor modules and commands"
|
147
147
|
method_options :internal => :boolean
|
148
148
|
def installed
|
149
149
|
initialize_thorfiles(nil, true)
|
150
150
|
display_klasses(true, options["internal"])
|
151
151
|
end
|
152
152
|
|
153
|
-
desc "list [SEARCH]", "List the available thor
|
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
155
|
def list(search="")
|
156
156
|
initialize_thorfiles
|
@@ -168,8 +168,8 @@ class Thor::Runner < Thor #:nodoc:
|
|
168
168
|
|
169
169
|
private
|
170
170
|
|
171
|
-
def self.banner(
|
172
|
-
"thor " +
|
171
|
+
def self.banner(command, all = false, subcommand = false)
|
172
|
+
"thor " + command.formatted_usage(self, all, subcommand)
|
173
173
|
end
|
174
174
|
|
175
175
|
def thor_root
|
@@ -276,25 +276,25 @@ class Thor::Runner < Thor #:nodoc:
|
|
276
276
|
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
|
277
277
|
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
278
278
|
|
279
|
-
raise Error, "No Thor
|
279
|
+
raise Error, "No Thor commands available" if klasses.empty?
|
280
280
|
show_modules if with_modules && !thor_yaml.empty?
|
281
281
|
|
282
282
|
list = Hash.new { |h,k| h[k] = [] }
|
283
283
|
groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
|
284
284
|
|
285
285
|
# Get classes which inherit from Thor
|
286
|
-
(klasses - groups).each { |k| list[k.namespace.split(":").first] += k.
|
286
|
+
(klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) }
|
287
287
|
|
288
288
|
# Get classes which inherit from Thor::Base
|
289
|
-
groups.map! { |k| k.
|
289
|
+
groups.map! { |k| k.printable_commands(false).first }
|
290
290
|
list["root"] = groups
|
291
291
|
|
292
292
|
# Order namespaces with default coming first
|
293
293
|
list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
|
294
|
-
list.each { |n,
|
294
|
+
list.each { |n, commands| display_commands(n, commands) unless commands.empty? }
|
295
295
|
end
|
296
296
|
|
297
|
-
def
|
297
|
+
def display_commands(namespace, list) #:nodoc:
|
298
298
|
list.sort!{ |a,b| a[0] <=> b[0] }
|
299
299
|
|
300
300
|
say shell.set_color(namespace, :blue, true)
|
@@ -303,6 +303,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
303
303
|
print_table(list, :truncate => true)
|
304
304
|
say
|
305
305
|
end
|
306
|
+
alias display_tasks display_commands
|
306
307
|
|
307
308
|
def show_modules #:nodoc:
|
308
309
|
info = []
|
@@ -58,10 +58,10 @@ class Thor
|
|
58
58
|
# ==== Example
|
59
59
|
# say("I know you knew that.")
|
60
60
|
#
|
61
|
-
def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)
|
61
|
+
def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)\Z/))
|
62
62
|
message = message.to_s
|
63
63
|
|
64
|
-
message = set_color(message, *color) if color
|
64
|
+
message = set_color(message, *color) if color && can_display_colors?
|
65
65
|
|
66
66
|
spaces = " " * padding
|
67
67
|
|
@@ -226,20 +226,20 @@ class Thor
|
|
226
226
|
answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
|
227
227
|
|
228
228
|
case answer
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
229
|
+
when is?(:yes), is?(:force), ""
|
230
|
+
return true
|
231
|
+
when is?(:no), is?(:skip)
|
232
|
+
return false
|
233
|
+
when is?(:always)
|
234
|
+
return @always_force = true
|
235
|
+
when is?(:quit)
|
236
|
+
say 'Aborting...'
|
237
|
+
raise SystemExit
|
238
|
+
when is?(:diff)
|
239
|
+
show_diff(destination, yield) if block_given?
|
240
|
+
say 'Retrying...'
|
241
|
+
else
|
242
|
+
say file_collision_help
|
243
243
|
end
|
244
244
|
end
|
245
245
|
end
|
@@ -275,6 +275,10 @@ class Thor
|
|
275
275
|
|
276
276
|
protected
|
277
277
|
|
278
|
+
def can_display_colors?
|
279
|
+
false
|
280
|
+
end
|
281
|
+
|
278
282
|
def lookup_color(color)
|
279
283
|
return color unless color.is_a?(Symbol)
|
280
284
|
self.class.const_get(color.to_s.upcase)
|
@@ -370,7 +374,7 @@ HELP
|
|
370
374
|
|
371
375
|
def ask_simply(statement, color=nil)
|
372
376
|
say("#{statement} ", color)
|
373
|
-
stdin.gets.strip
|
377
|
+
stdin.gets.tap{|text| text.strip! if text}
|
374
378
|
end
|
375
379
|
|
376
380
|
def ask_filtered(statement, answer_set, *args)
|
@@ -94,6 +94,10 @@ class Thor
|
|
94
94
|
|
95
95
|
protected
|
96
96
|
|
97
|
+
def can_display_colors?
|
98
|
+
stdout.tty?
|
99
|
+
end
|
100
|
+
|
97
101
|
# Overwrite show_diff to show diff with colors if Diff::LCS is
|
98
102
|
# available.
|
99
103
|
#
|
@@ -112,15 +116,15 @@ class Thor
|
|
112
116
|
|
113
117
|
def output_diff_line(diff) #:nodoc:
|
114
118
|
case diff.action
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
119
|
+
when '-'
|
120
|
+
say "- #{diff.old_element.chomp}", :red, true
|
121
|
+
when '+'
|
122
|
+
say "+ #{diff.new_element.chomp}", :green, true
|
123
|
+
when '!'
|
124
|
+
say "- #{diff.old_element.chomp}", :red, true
|
125
|
+
say "+ #{diff.new_element.chomp}", :green, true
|
126
|
+
else
|
127
|
+
say " #{diff.old_element.chomp}", nil, true
|
124
128
|
end
|
125
129
|
end
|
126
130
|
|
@@ -73,6 +73,10 @@ class Thor
|
|
73
73
|
|
74
74
|
protected
|
75
75
|
|
76
|
+
def can_display_colors?
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
76
80
|
# Overwrite show_diff to show diff with colors if Diff::LCS is
|
77
81
|
# available.
|
78
82
|
#
|
@@ -91,15 +95,15 @@ class Thor
|
|
91
95
|
|
92
96
|
def output_diff_line(diff) #:nodoc:
|
93
97
|
case diff.action
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
when '-'
|
99
|
+
say "- #{diff.old_element.chomp}", :red, true
|
100
|
+
when '+'
|
101
|
+
say "+ #{diff.new_element.chomp}", :green, true
|
102
|
+
when '!'
|
103
|
+
say "- #{diff.old_element.chomp}", :red, true
|
104
|
+
say "+ #{diff.new_element.chomp}", :green, true
|
105
|
+
else
|
106
|
+
say " #{diff.old_element.chomp}", nil, true
|
103
107
|
end
|
104
108
|
end
|
105
109
|
|
@@ -16,251 +16,255 @@ class Thor
|
|
16
16
|
#
|
17
17
|
module Util
|
18
18
|
|
19
|
-
|
20
|
-
#
|
21
|
-
# ==== Parameters
|
22
|
-
# namespace<String>:: The namespace to search for.
|
23
|
-
#
|
24
|
-
def self.find_by_namespace(namespace)
|
25
|
-
namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/
|
26
|
-
Thor::Base.subclasses.find { |klass| klass.namespace == namespace }
|
27
|
-
end
|
19
|
+
class << self
|
28
20
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# constant<Object>:: The constant to be converted to the thor path.
|
39
|
-
#
|
40
|
-
# ==== Returns
|
41
|
-
# String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
|
42
|
-
#
|
43
|
-
def self.namespace_from_thor_class(constant)
|
44
|
-
constant = constant.to_s.gsub(/^Thor::Sandbox::/, "")
|
45
|
-
constant = snake_case(constant).squeeze(":")
|
46
|
-
constant
|
47
|
-
end
|
21
|
+
# Receives a namespace and search for it in the Thor::Base subclasses.
|
22
|
+
#
|
23
|
+
# ==== Parameters
|
24
|
+
# namespace<String>:: The namespace to search for.
|
25
|
+
#
|
26
|
+
def find_by_namespace(namespace)
|
27
|
+
namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/
|
28
|
+
Thor::Base.subclasses.find { |klass| klass.namespace == namespace }
|
29
|
+
end
|
48
30
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
31
|
+
# Receives a constant and converts it to a Thor namespace. Since Thor
|
32
|
+
# commands can be added to a sandbox, this method is also responsable for
|
33
|
+
# removing the sandbox namespace.
|
34
|
+
#
|
35
|
+
# This method should not be used in general because it's used to deal with
|
36
|
+
# older versions of Thor. On current versions, if you need to get the
|
37
|
+
# namespace from a class, just call namespace on it.
|
38
|
+
#
|
39
|
+
# ==== Parameters
|
40
|
+
# constant<Object>:: The constant to be converted to the thor path.
|
41
|
+
#
|
42
|
+
# ==== Returns
|
43
|
+
# String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
|
44
|
+
#
|
45
|
+
def namespace_from_thor_class(constant)
|
46
|
+
constant = constant.to_s.gsub(/^Thor::Sandbox::/, "")
|
47
|
+
constant = snake_case(constant).squeeze(":")
|
48
|
+
constant
|
49
|
+
end
|
61
50
|
|
62
|
-
|
51
|
+
# Given the contents, evaluate it inside the sandbox and returns the
|
52
|
+
# namespaces defined in the sandbox.
|
53
|
+
#
|
54
|
+
# ==== Parameters
|
55
|
+
# contents<String>
|
56
|
+
#
|
57
|
+
# ==== Returns
|
58
|
+
# Array[Object]
|
59
|
+
#
|
60
|
+
def namespaces_in_content(contents, file=__FILE__)
|
61
|
+
old_constants = Thor::Base.subclasses.dup
|
62
|
+
Thor::Base.subclasses.clear
|
63
63
|
|
64
|
-
|
65
|
-
Thor::Base.subclasses.replace(old_constants)
|
64
|
+
load_thorfile(file, contents)
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
new_constants
|
70
|
-
end
|
66
|
+
new_constants = Thor::Base.subclasses.dup
|
67
|
+
Thor::Base.subclasses.replace(old_constants)
|
71
68
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
stringfied_constants = klass.constants.map { |c| c.to_s }
|
76
|
-
Thor::Base.subclasses.select do |subclass|
|
77
|
-
next unless subclass.name
|
78
|
-
stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ''))
|
69
|
+
new_constants.map!{ |c| c.namespace }
|
70
|
+
new_constants.compact!
|
71
|
+
new_constants
|
79
72
|
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Receives a string and convert it to snake case. SnakeCase returns snake_case.
|
83
|
-
#
|
84
|
-
# ==== Parameters
|
85
|
-
# String
|
86
|
-
#
|
87
|
-
# ==== Returns
|
88
|
-
# String
|
89
|
-
#
|
90
|
-
def self.snake_case(str)
|
91
|
-
return str.downcase if str =~ /^[A-Z_]+$/
|
92
|
-
str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/
|
93
|
-
return $+.downcase
|
94
|
-
end
|
95
|
-
|
96
|
-
# Receives a string and convert it to camel case. camel_case returns CamelCase.
|
97
|
-
#
|
98
|
-
# ==== Parameters
|
99
|
-
# String
|
100
|
-
#
|
101
|
-
# ==== Returns
|
102
|
-
# String
|
103
|
-
#
|
104
|
-
def self.camel_case(str)
|
105
|
-
return str if str !~ /_/ && str =~ /[A-Z]+.*/
|
106
|
-
str.split('_').map { |i| i.capitalize }.join
|
107
|
-
end
|
108
73
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
# def baz
|
118
|
-
# end
|
119
|
-
# end
|
120
|
-
#
|
121
|
-
# class Baz::Foo < Thor::Group
|
122
|
-
# end
|
123
|
-
#
|
124
|
-
# Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default task
|
125
|
-
# Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil
|
126
|
-
# Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz"
|
127
|
-
#
|
128
|
-
# ==== Parameters
|
129
|
-
# namespace<String>
|
130
|
-
#
|
131
|
-
def self.find_class_and_task_by_namespace(namespace, fallback = true)
|
132
|
-
if namespace.include?(?:) # look for a namespaced task
|
133
|
-
pieces = namespace.split(":")
|
134
|
-
task = pieces.pop
|
135
|
-
klass = Thor::Util.find_by_namespace(pieces.join(":"))
|
136
|
-
end
|
137
|
-
unless klass # look for a Thor::Group with the right name
|
138
|
-
klass, task = Thor::Util.find_by_namespace(namespace), nil
|
74
|
+
# Returns the thor classes declared inside the given class.
|
75
|
+
#
|
76
|
+
def thor_classes_in(klass)
|
77
|
+
stringfied_constants = klass.constants.map { |c| c.to_s }
|
78
|
+
Thor::Base.subclasses.select do |subclass|
|
79
|
+
next unless subclass.name
|
80
|
+
stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ''))
|
81
|
+
end
|
139
82
|
end
|
140
|
-
|
141
|
-
|
142
|
-
|
83
|
+
|
84
|
+
# Receives a string and convert it to snake case. SnakeCase returns snake_case.
|
85
|
+
#
|
86
|
+
# ==== Parameters
|
87
|
+
# String
|
88
|
+
#
|
89
|
+
# ==== Returns
|
90
|
+
# String
|
91
|
+
#
|
92
|
+
def snake_case(str)
|
93
|
+
return str.downcase if str =~ /^[A-Z_]+$/
|
94
|
+
str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/
|
95
|
+
return $+.downcase
|
143
96
|
end
|
144
|
-
return klass, task
|
145
|
-
end
|
146
97
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
98
|
+
# Receives a string and convert it to camel case. camel_case returns CamelCase.
|
99
|
+
#
|
100
|
+
# ==== Parameters
|
101
|
+
# String
|
102
|
+
#
|
103
|
+
# ==== Returns
|
104
|
+
# String
|
105
|
+
#
|
106
|
+
def camel_case(str)
|
107
|
+
return str if str !~ /_/ && str =~ /[A-Z]+.*/
|
108
|
+
str.split('_').map { |i| i.capitalize }.join
|
109
|
+
end
|
152
110
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
111
|
+
# Receives a namespace and tries to retrieve a Thor or Thor::Group class
|
112
|
+
# from it. It first searches for a class using the all the given namespace,
|
113
|
+
# if it's not found, removes the highest entry and searches for the class
|
114
|
+
# again. If found, returns the highest entry as the class name.
|
115
|
+
#
|
116
|
+
# ==== Examples
|
117
|
+
#
|
118
|
+
# class Foo::Bar < Thor
|
119
|
+
# def baz
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# class Baz::Foo < Thor::Group
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command
|
127
|
+
# Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil
|
128
|
+
# Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz"
|
129
|
+
#
|
130
|
+
# ==== Parameters
|
131
|
+
# namespace<String>
|
132
|
+
#
|
133
|
+
def find_class_and_command_by_namespace(namespace, fallback = true)
|
134
|
+
if namespace.include?(?:) # look for a namespaced command
|
135
|
+
pieces = namespace.split(":")
|
136
|
+
command = pieces.pop
|
137
|
+
klass = Thor::Util.find_by_namespace(pieces.join(":"))
|
138
|
+
end
|
139
|
+
unless klass # look for a Thor::Group with the right name
|
140
|
+
klass, command = Thor::Util.find_by_namespace(namespace), nil
|
141
|
+
end
|
142
|
+
if !klass && fallback # try a command in the default namespace
|
143
|
+
command = namespace
|
144
|
+
klass = Thor::Util.find_by_namespace('')
|
161
145
|
end
|
146
|
+
return klass, command
|
162
147
|
end
|
163
|
-
|
148
|
+
alias find_class_and_task_by_namespace find_class_and_command_by_namespace
|
149
|
+
|
150
|
+
# Receives a path and load the thor file in the path. The file is evaluated
|
151
|
+
# inside the sandbox to avoid namespacing conflicts.
|
152
|
+
#
|
153
|
+
def load_thorfile(path, content=nil, debug=false)
|
154
|
+
content ||= File.binread(path)
|
164
155
|
|
165
|
-
def self.user_home
|
166
|
-
@@user_home ||= if ENV["HOME"]
|
167
|
-
ENV["HOME"]
|
168
|
-
elsif ENV["USERPROFILE"]
|
169
|
-
ENV["USERPROFILE"]
|
170
|
-
elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"]
|
171
|
-
File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"])
|
172
|
-
elsif ENV["APPDATA"]
|
173
|
-
ENV["APPDATA"]
|
174
|
-
else
|
175
156
|
begin
|
176
|
-
|
177
|
-
rescue
|
178
|
-
|
179
|
-
|
157
|
+
Thor::Sandbox.class_eval(content, path)
|
158
|
+
rescue Exception => e
|
159
|
+
$stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}")
|
160
|
+
if debug
|
161
|
+
$stderr.puts(*e.backtrace)
|
180
162
|
else
|
181
|
-
|
163
|
+
$stderr.puts(e.backtrace.first)
|
182
164
|
end
|
183
165
|
end
|
184
166
|
end
|
185
|
-
end
|
186
167
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
168
|
+
def user_home
|
169
|
+
@@user_home ||= if ENV["HOME"]
|
170
|
+
ENV["HOME"]
|
171
|
+
elsif ENV["USERPROFILE"]
|
172
|
+
ENV["USERPROFILE"]
|
173
|
+
elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"]
|
174
|
+
File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"])
|
175
|
+
elsif ENV["APPDATA"]
|
176
|
+
ENV["APPDATA"]
|
177
|
+
else
|
178
|
+
begin
|
179
|
+
File.expand_path("~")
|
180
|
+
rescue
|
181
|
+
if File::ALT_SEPARATOR
|
182
|
+
"C:/"
|
183
|
+
else
|
184
|
+
"/"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns the root where thor files are located, depending on the OS.
|
191
|
+
#
|
192
|
+
def thor_root
|
193
|
+
File.join(user_home, ".thor").gsub(/\\/, '/')
|
194
|
+
end
|
192
195
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
196
|
+
# Returns the files in the thor root. On Windows thor_root will be something
|
197
|
+
# like this:
|
198
|
+
#
|
199
|
+
# C:\Documents and Settings\james\.thor
|
200
|
+
#
|
201
|
+
# If we don't #gsub the \ character, Dir.glob will fail.
|
202
|
+
#
|
203
|
+
def thor_root_glob
|
204
|
+
files = Dir["#{escape_globs(thor_root)}/*"]
|
202
205
|
|
203
|
-
|
204
|
-
|
206
|
+
files.map! do |file|
|
207
|
+
File.directory?(file) ? File.join(file, "main.thor") : file
|
208
|
+
end
|
205
209
|
end
|
206
|
-
end
|
207
210
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
211
|
+
# Where to look for Thor files.
|
212
|
+
#
|
213
|
+
def globs_for(path)
|
214
|
+
path = escape_globs(path)
|
215
|
+
["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"]
|
216
|
+
end
|
214
217
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
218
|
+
# Return the path to the ruby interpreter taking into account multiple
|
219
|
+
# installations and windows extensions.
|
220
|
+
#
|
221
|
+
def ruby_command
|
222
|
+
@ruby_command ||= begin
|
223
|
+
ruby_name = RbConfig::CONFIG['ruby_install_name']
|
224
|
+
ruby = File.join(RbConfig::CONFIG['bindir'], ruby_name)
|
225
|
+
ruby << RbConfig::CONFIG['EXEEXT']
|
223
226
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
227
|
+
# avoid using different name than ruby (on platforms supporting links)
|
228
|
+
if ruby_name != 'ruby' && File.respond_to?(:readlink)
|
229
|
+
begin
|
230
|
+
alternate_ruby = File.join(RbConfig::CONFIG['bindir'], 'ruby')
|
231
|
+
alternate_ruby << RbConfig::CONFIG['EXEEXT']
|
229
232
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
+
# ruby is a symlink
|
234
|
+
if File.symlink? alternate_ruby
|
235
|
+
linked_ruby = File.readlink alternate_ruby
|
233
236
|
|
234
|
-
|
235
|
-
|
237
|
+
# symlink points to 'ruby_install_name'
|
238
|
+
ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby
|
239
|
+
end
|
240
|
+
rescue NotImplementedError
|
241
|
+
# just ignore on windows
|
236
242
|
end
|
237
|
-
rescue NotImplementedError
|
238
|
-
# just ignore on windows
|
239
243
|
end
|
244
|
+
|
245
|
+
# escape string in case path to ruby executable contain spaces.
|
246
|
+
ruby.sub!(/.*\s.*/m, '"\&"')
|
247
|
+
ruby
|
240
248
|
end
|
249
|
+
end
|
241
250
|
|
242
|
-
|
243
|
-
|
244
|
-
|
251
|
+
# Returns a string that has had any glob characters escaped.
|
252
|
+
# The glob characters are `* ? { } [ ]`.
|
253
|
+
#
|
254
|
+
# ==== Examples
|
255
|
+
#
|
256
|
+
# Thor::Util.escape_globs('[apps]') # => '\[apps\]'
|
257
|
+
#
|
258
|
+
# ==== Parameters
|
259
|
+
# String
|
260
|
+
#
|
261
|
+
# ==== Returns
|
262
|
+
# String
|
263
|
+
#
|
264
|
+
def escape_globs(path)
|
265
|
+
path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
245
266
|
end
|
246
|
-
end
|
247
267
|
|
248
|
-
# Returns a string that has had any glob characters escaped.
|
249
|
-
# The glob characters are `* ? { } [ ]`.
|
250
|
-
#
|
251
|
-
# ==== Examples
|
252
|
-
#
|
253
|
-
# Thor::Util.escape_globs('[apps]') # => '\[apps\]'
|
254
|
-
#
|
255
|
-
# ==== Parameters
|
256
|
-
# String
|
257
|
-
#
|
258
|
-
# ==== Returns
|
259
|
-
# String
|
260
|
-
#
|
261
|
-
def self.escape_globs(path)
|
262
|
-
path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
263
268
|
end
|
264
|
-
|
265
269
|
end
|
266
270
|
end
|