thor 0.12.2 → 0.13.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.
- data/CHANGELOG.rdoc +5 -5
- data/README.rdoc +65 -2
- data/Thorfile +13 -8
- data/bin/thor +1 -0
- data/lib/thor/actions/create_file.rb +2 -2
- data/lib/thor/actions/file_manipulation.rb +4 -4
- data/lib/thor/actions/inject_into_file.rb +10 -7
- data/lib/thor/actions.rb +1 -0
- data/lib/thor/base.rb +15 -0
- data/lib/thor/core_ext/file_binary_read.rb +9 -0
- data/lib/thor/group.rb +41 -28
- data/lib/thor/runner.rb +45 -42
- data/lib/thor/shell/color.rb +1 -1
- data/lib/thor/task.rb +13 -14
- data/lib/thor/util.rb +4 -22
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +35 -39
- data/spec/actions/create_file_spec.rb +7 -7
- data/spec/actions/directory_spec.rb +1 -1
- data/spec/actions/file_manipulation_spec.rb +15 -15
- data/spec/actions/inject_into_file_spec.rb +29 -0
- data/spec/actions_spec.rb +14 -13
- data/spec/base_spec.rb +15 -0
- data/spec/fixtures/script.thor +4 -0
- data/spec/group_spec.rb +1 -7
- data/spec/runner_spec.rb +30 -34
- data/spec/shell/basic_spec.rb +43 -43
- data/spec/shell/color_spec.rb +6 -6
- data/spec/spec_helper.rb +5 -4
- data/spec/task_spec.rb +11 -9
- data/spec/thor_spec.rb +10 -13
- data/spec/util_spec.rb +7 -31
- metadata +3 -29
data/CHANGELOG.rdoc
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
==
|
|
2
|
-
|
|
3
|
-
* Improve spec coverage for Thor::Runner
|
|
4
|
-
|
|
5
|
-
== 0.12, released 2009-11-06
|
|
1
|
+
== 0.12, released 2010-01-02
|
|
6
2
|
|
|
3
|
+
* Methods generated by attr_* are automatically not marked as tasks
|
|
4
|
+
* inject_into_file does not add the same content twice, unless :force is set
|
|
5
|
+
* Removed rr in favor to rspec mock framework
|
|
6
|
+
* Improved output for thor -T
|
|
7
7
|
* [#7] Do not force white color on status
|
|
8
8
|
* [#8] Yield a block with the filename on directory
|
|
9
9
|
|
data/README.rdoc
CHANGED
|
@@ -145,7 +145,7 @@ When invoking the task one:
|
|
|
145
145
|
|
|
146
146
|
The output is "1 2 3", which means that the three task was invoked only once.
|
|
147
147
|
You can even invoke tasks from another class, so be sure to check the
|
|
148
|
-
documentation.
|
|
148
|
+
documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor.html].
|
|
149
149
|
|
|
150
150
|
== Thor::Group
|
|
151
151
|
|
|
@@ -227,7 +227,70 @@ To use them, you just need to include Thor::Actions in your Thor classes:
|
|
|
227
227
|
|
|
228
228
|
Some actions like copy file requires that a class method called source_root is
|
|
229
229
|
defined in your class. This is the directory where your templates should be
|
|
230
|
-
placed. Be sure to check the documentation.
|
|
230
|
+
placed. Be sure to check the documentation on actions[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor/Actions.html].
|
|
231
|
+
|
|
232
|
+
== Generators
|
|
233
|
+
|
|
234
|
+
A great use for Thor is creating custom generators. Combining Thor::Group,
|
|
235
|
+
Thor::Actions and ERB templates makes this very easy. Here is an example:
|
|
236
|
+
|
|
237
|
+
class Newgem < Thor::Group
|
|
238
|
+
include Thor::Actions
|
|
239
|
+
|
|
240
|
+
# Define arguments and options
|
|
241
|
+
argument :name
|
|
242
|
+
class_option :test_framework, :default => :test_unit
|
|
243
|
+
|
|
244
|
+
def self.source_root
|
|
245
|
+
File.dirname(__FILE__)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def create_lib_file
|
|
249
|
+
template('templates/newgem.tt', "#{name}/lib/#{name}.rb")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def create_test_file
|
|
253
|
+
test = options[:test_framework] == "rspec" ? :spec : :test
|
|
254
|
+
create_file "#{name}/#{test}/#{name}_#{test}.rb"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def copy_licence
|
|
258
|
+
if yes?("Use MIT license?")
|
|
259
|
+
# Make a copy of the MITLICENSE file at the source root
|
|
260
|
+
copy_file "MITLICENSE", "#{name}/MITLICENSE"
|
|
261
|
+
else
|
|
262
|
+
say "Shame on you…", :red
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
Doing a <tt>thor -T</tt> will show how to run our generator. It should read:
|
|
268
|
+
<tt>thor newgem NAME</tt>. This shows that we have to supply a NAME
|
|
269
|
+
argument for our generator to run.
|
|
270
|
+
|
|
271
|
+
The <tt>create_lib_file</tt> uses an ERB template. This is what it looks like:
|
|
272
|
+
|
|
273
|
+
class <%= name.camelize %>
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
The arguments that you set in your generator will automatically be passed in
|
|
277
|
+
when <tt>template</tt> gets called. Be sure to read the documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor/Actions.html] for
|
|
278
|
+
more options.
|
|
279
|
+
|
|
280
|
+
Running the generator with <tt>thor newgem devise</tt> will
|
|
281
|
+
create two files: "devise/lib/devise.rb",
|
|
282
|
+
"devise/test/devise_test.rb". The user will then be prompt (with the
|
|
283
|
+
use of the method <tt>yes?</tt>) if he wants to copy the MITLICENSE. If you
|
|
284
|
+
want to change the test framework, you can add the option:
|
|
285
|
+
<tt>thor newgem devise --test-framework=rspec</tt>
|
|
286
|
+
This will generate: "devise/lib/devise.rb" and
|
|
287
|
+
"devise/spec/devise_spec.rb".
|
|
288
|
+
|
|
289
|
+
== Further Reading
|
|
290
|
+
|
|
291
|
+
Thor has many scripting possibilities beyond these examples. Be sure to read
|
|
292
|
+
through the documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor.html] and specs[http://github.com/wycats/thor/tree/master/spec/] to get a better understanding of all the
|
|
293
|
+
options Thor offers.
|
|
231
294
|
|
|
232
295
|
== License
|
|
233
296
|
|
data/Thorfile
CHANGED
|
@@ -4,7 +4,10 @@ require File.join(File.dirname(__FILE__), "lib", "thor", "version")
|
|
|
4
4
|
require 'rubygems'
|
|
5
5
|
require 'thor/rake_compat'
|
|
6
6
|
require 'spec/rake/spectask'
|
|
7
|
-
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
end
|
|
8
11
|
|
|
9
12
|
GEM_NAME = 'thor'
|
|
10
13
|
EXTRA_RDOC_FILES = ["README.rdoc", "LICENSE", "CHANGELOG.rdoc", "VERSION", "Thorfile"]
|
|
@@ -26,13 +29,15 @@ class Default < Thor
|
|
|
26
29
|
t.rcov_dir = "rcov"
|
|
27
30
|
end
|
|
28
31
|
|
|
29
|
-
RDoc
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
if defined?(RDoc)
|
|
33
|
+
RDoc::Task.new do |rdoc|
|
|
34
|
+
rdoc.main = "README.rdoc"
|
|
35
|
+
rdoc.rdoc_dir = "rdoc"
|
|
36
|
+
rdoc.title = GEM_NAME
|
|
37
|
+
rdoc.rdoc_files.include(*EXTRA_RDOC_FILES)
|
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
39
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
begin
|
data/bin/thor
CHANGED
|
@@ -42,7 +42,7 @@ class Thor
|
|
|
42
42
|
# Boolean:: true if it is identical, false otherwise.
|
|
43
43
|
#
|
|
44
44
|
def identical?
|
|
45
|
-
exists? && File.
|
|
45
|
+
exists? && File.binread(destination) == render
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# Holds the content to be added to the file.
|
|
@@ -58,7 +58,7 @@ class Thor
|
|
|
58
58
|
def invoke!
|
|
59
59
|
invoke_with_conflict_check do
|
|
60
60
|
FileUtils.mkdir_p(File.dirname(destination))
|
|
61
|
-
File.open(destination, '
|
|
61
|
+
File.open(destination, 'wb') { |f| f.write render }
|
|
62
62
|
end
|
|
63
63
|
given_destination
|
|
64
64
|
end
|
|
@@ -23,7 +23,7 @@ class Thor
|
|
|
23
23
|
source = File.expand_path(find_in_source_paths(source.to_s))
|
|
24
24
|
|
|
25
25
|
create_file destination, nil, config do
|
|
26
|
-
content = File.
|
|
26
|
+
content = File.binread(source)
|
|
27
27
|
content = block.call(content) if block
|
|
28
28
|
content
|
|
29
29
|
end
|
|
@@ -48,7 +48,7 @@ class Thor
|
|
|
48
48
|
#
|
|
49
49
|
def get(source, destination=nil, config={}, &block)
|
|
50
50
|
source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\//
|
|
51
|
-
render =
|
|
51
|
+
render = File.binread(source)
|
|
52
52
|
|
|
53
53
|
destination ||= if block_given?
|
|
54
54
|
block.arity == 1 ? block.call(render) : block.call
|
|
@@ -80,7 +80,7 @@ class Thor
|
|
|
80
80
|
context = instance_eval('binding')
|
|
81
81
|
|
|
82
82
|
create_file destination, nil, config do
|
|
83
|
-
content = ERB.new(::File.
|
|
83
|
+
content = ERB.new(::File.binread(source), nil, '-').result(context)
|
|
84
84
|
content = block.call(content) if block
|
|
85
85
|
content
|
|
86
86
|
end
|
|
@@ -193,7 +193,7 @@ class Thor
|
|
|
193
193
|
say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
|
194
194
|
|
|
195
195
|
unless options[:pretend]
|
|
196
|
-
content = File.
|
|
196
|
+
content = File.binread(path)
|
|
197
197
|
content.gsub!(flag, *args, &block)
|
|
198
198
|
File.open(path, 'wb') { |file| file.write(content) }
|
|
199
199
|
end
|
|
@@ -10,7 +10,8 @@ class Thor
|
|
|
10
10
|
# destination<String>:: Relative path to the destination root
|
|
11
11
|
# data<String>:: Data to add to the file. Can be given as a block.
|
|
12
12
|
# config<Hash>:: give :verbose => false to not log the status and the flag
|
|
13
|
-
# for injection (:after or :before)
|
|
13
|
+
# for injection (:after or :before) or :force => true for
|
|
14
|
+
# insert two or more times the same content.
|
|
14
15
|
#
|
|
15
16
|
# ==== Examples
|
|
16
17
|
#
|
|
@@ -55,7 +56,7 @@ class Thor
|
|
|
55
56
|
replacement + '\0'
|
|
56
57
|
end
|
|
57
58
|
|
|
58
|
-
replace!(/#{flag}/, content)
|
|
59
|
+
replace!(/#{flag}/, content, config[:force])
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def revoke!
|
|
@@ -69,7 +70,7 @@ class Thor
|
|
|
69
70
|
/(#{Regexp.escape(replacement)})(.*)(#{flag})/m
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
replace!(regexp, content)
|
|
73
|
+
replace!(regexp, content, true)
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
protected
|
|
@@ -88,11 +89,13 @@ class Thor
|
|
|
88
89
|
|
|
89
90
|
# Adds the content to the file.
|
|
90
91
|
#
|
|
91
|
-
def replace!(regexp, string)
|
|
92
|
+
def replace!(regexp, string, force)
|
|
92
93
|
unless base.options[:pretend]
|
|
93
|
-
content = File.
|
|
94
|
-
content.
|
|
95
|
-
|
|
94
|
+
content = File.binread(destination)
|
|
95
|
+
if force || !content.include?(replacement)
|
|
96
|
+
content.gsub!(regexp, string)
|
|
97
|
+
File.open(destination, 'wb') { |file| file.write(content) }
|
|
98
|
+
end
|
|
96
99
|
end
|
|
97
100
|
end
|
|
98
101
|
|
data/lib/thor/actions.rb
CHANGED
data/lib/thor/base.rb
CHANGED
|
@@ -8,6 +8,9 @@ require 'thor/task'
|
|
|
8
8
|
require 'thor/util'
|
|
9
9
|
|
|
10
10
|
class Thor
|
|
11
|
+
autoload :Actions, 'thor/actions'
|
|
12
|
+
autoload :RakeCompat, 'thor/rake_compat'
|
|
13
|
+
|
|
11
14
|
# Shortcuts for help.
|
|
12
15
|
HELP_MAPPINGS = %w(-h -? --help -D)
|
|
13
16
|
|
|
@@ -94,6 +97,18 @@ class Thor
|
|
|
94
97
|
module ClassMethods
|
|
95
98
|
attr_accessor :debugging
|
|
96
99
|
|
|
100
|
+
def attr_reader(*) #:nodoc:
|
|
101
|
+
no_tasks { super }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def attr_writer(*) #:nodoc:
|
|
105
|
+
no_tasks { super }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def attr_accessor(*) #:nodoc:
|
|
109
|
+
no_tasks { super }
|
|
110
|
+
end
|
|
111
|
+
|
|
97
112
|
# Adds an argument to the class and creates an attr_accessor for it.
|
|
98
113
|
#
|
|
99
114
|
# Arguments are different from options in several aspects. The first one
|
data/lib/thor/group.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
require 'thor/base'
|
|
2
|
+
|
|
1
3
|
# Thor has a special class called Thor::Group. The main difference to Thor class
|
|
2
4
|
# is that it invokes all tasks at once. It also include some methods that allows
|
|
3
5
|
# invocations to be done at the class method, which are not available to Thor
|
|
4
6
|
# tasks.
|
|
5
|
-
#
|
|
6
7
|
class Thor::Group
|
|
7
8
|
class << self
|
|
8
9
|
# The descrition for this Thor::Group. If none is provided, but a source root
|
|
@@ -41,16 +42,12 @@ class Thor::Group
|
|
|
41
42
|
# ==== Options
|
|
42
43
|
# short:: When true, shows only usage.
|
|
43
44
|
#
|
|
44
|
-
def help(shell
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
shell.say ""
|
|
51
|
-
class_options_help(shell)
|
|
52
|
-
shell.say self.desc if self.desc
|
|
53
|
-
end
|
|
45
|
+
def help(shell)
|
|
46
|
+
shell.say "Usage:"
|
|
47
|
+
shell.say " #{banner}\n"
|
|
48
|
+
shell.say
|
|
49
|
+
class_options_help(shell)
|
|
50
|
+
shell.say self.desc if self.desc
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
# Stores invocations for this class merging with superclass values.
|
|
@@ -214,13 +211,27 @@ class Thor::Group
|
|
|
214
211
|
end
|
|
215
212
|
end
|
|
216
213
|
|
|
214
|
+
# Returns tasks ready to be printed.
|
|
215
|
+
def printable_tasks(*)
|
|
216
|
+
item = []
|
|
217
|
+
item << banner
|
|
218
|
+
item << (desc ? "# #{desc.gsub(/\s+/m,' ')}" : "")
|
|
219
|
+
[item]
|
|
220
|
+
end
|
|
221
|
+
|
|
217
222
|
protected
|
|
218
223
|
|
|
219
224
|
# The banner for this class. You can customize it if you are invoking the
|
|
220
225
|
# thor class by another ways which is not the Thor::Runner.
|
|
221
226
|
#
|
|
222
227
|
def banner
|
|
223
|
-
|
|
228
|
+
base = $thor_runner ? "thor" : File.basename($0.split(" ").first)
|
|
229
|
+
"#{base} #{self_task.formatted_usage(self, false)}"
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Represents the whole class as a task.
|
|
233
|
+
def self_task #:nodoc:
|
|
234
|
+
Thor::Task::Dynamic.new(self.namespace, class_options)
|
|
224
235
|
end
|
|
225
236
|
|
|
226
237
|
def baseclass #:nodoc:
|
|
@@ -237,23 +248,25 @@ class Thor::Group
|
|
|
237
248
|
|
|
238
249
|
protected
|
|
239
250
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
shell.padding += 1
|
|
251
|
+
# Shortcut to invoke with padding and block handling. Use internally by
|
|
252
|
+
# invoke and invoke_from_option class methods.
|
|
253
|
+
def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc:
|
|
254
|
+
shell.padding += 1
|
|
245
255
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
256
|
+
result = if block_given?
|
|
257
|
+
case block.arity
|
|
258
|
+
when 3
|
|
259
|
+
block.call(self, klass, task)
|
|
260
|
+
when 2
|
|
261
|
+
block.call(self, klass)
|
|
262
|
+
when 1
|
|
263
|
+
instance_exec(klass, &block)
|
|
254
264
|
end
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
result
|
|
265
|
+
else
|
|
266
|
+
invoke klass, task, *args
|
|
258
267
|
end
|
|
268
|
+
|
|
269
|
+
shell.padding -= 1
|
|
270
|
+
result
|
|
271
|
+
end
|
|
259
272
|
end
|
data/lib/thor/runner.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'fileutils'
|
|
2
|
+
require 'thor/core_ext/file_binary_read'
|
|
2
3
|
require 'open-uri'
|
|
3
4
|
require 'yaml'
|
|
4
5
|
require 'digest/md5'
|
|
@@ -32,7 +33,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
desc "install NAME", "Install an optionally named Thor file into your system tasks"
|
|
35
|
-
method_options :as => :string, :relative => :boolean
|
|
36
|
+
method_options :as => :string, :relative => :boolean, :force => :boolean
|
|
36
37
|
def install(name)
|
|
37
38
|
initialize_thorfiles
|
|
38
39
|
|
|
@@ -55,7 +56,9 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
55
56
|
say "Your Thorfile contains:"
|
|
56
57
|
say contents
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
unless options["force"]
|
|
60
|
+
return false if no?("Do you wish to continue [y/N]?")
|
|
61
|
+
end
|
|
59
62
|
|
|
60
63
|
as = options["as"] || begin
|
|
61
64
|
first_line = contents.split("\n")[0]
|
|
@@ -124,11 +127,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
124
127
|
method_options :internal => :boolean
|
|
125
128
|
def installed
|
|
126
129
|
initialize_thorfiles(nil, true)
|
|
127
|
-
|
|
128
|
-
klasses = Thor::Base.subclasses
|
|
129
|
-
klasses -= [Thor, Thor::Runner] unless options["internal"]
|
|
130
|
-
|
|
131
|
-
display_klasses(true, klasses)
|
|
130
|
+
display_klasses(true, options["internal"])
|
|
132
131
|
end
|
|
133
132
|
|
|
134
133
|
desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)"
|
|
@@ -144,7 +143,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
144
143
|
(options[:all] || k.group == group) && k.namespace =~ search
|
|
145
144
|
end
|
|
146
145
|
|
|
147
|
-
display_klasses(false, klasses)
|
|
146
|
+
display_klasses(false, false, klasses)
|
|
148
147
|
end
|
|
149
148
|
|
|
150
149
|
private
|
|
@@ -160,7 +159,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
160
159
|
def thor_yaml
|
|
161
160
|
@thor_yaml ||= begin
|
|
162
161
|
yaml_file = File.join(thor_root, "thor.yml")
|
|
163
|
-
yaml
|
|
162
|
+
yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
|
|
164
163
|
yaml || {}
|
|
165
164
|
end
|
|
166
165
|
end
|
|
@@ -219,9 +218,6 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
219
218
|
# 5. c:\ <-- no Thorfiles found!
|
|
220
219
|
#
|
|
221
220
|
def thorfiles(relevant_to=nil, skip_lookup=false)
|
|
222
|
-
# TODO Remove this dealing with deprecated thor when :namespaces: is available as constants
|
|
223
|
-
save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml)
|
|
224
|
-
|
|
225
221
|
thorfiles = []
|
|
226
222
|
|
|
227
223
|
unless skip_lookup
|
|
@@ -257,47 +253,54 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
257
253
|
# Display information about the given klasses. If with_module is given,
|
|
258
254
|
# it shows a table with information extracted from the yaml file.
|
|
259
255
|
#
|
|
260
|
-
def display_klasses(with_modules=false, klasses=Thor.subclasses)
|
|
261
|
-
klasses -= [Thor, Thor::Runner] unless
|
|
256
|
+
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
|
|
257
|
+
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
|
258
|
+
|
|
262
259
|
raise Error, "No Thor tasks available" if klasses.empty?
|
|
260
|
+
show_modules if with_modules && !thor_yaml.empty?
|
|
263
261
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
262
|
+
# Remove subclasses
|
|
263
|
+
klasses.dup.each do |klass|
|
|
264
|
+
klasses -= Thor::Util.thor_classes_in(klass)
|
|
265
|
+
end
|
|
267
266
|
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
list = Hash.new { |h,k| h[k] = [] }
|
|
268
|
+
groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
|
|
270
269
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
end
|
|
270
|
+
# Get classes which inherit from Thor
|
|
271
|
+
(klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) }
|
|
274
272
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
273
|
+
# Get classes which inherit from Thor::Base
|
|
274
|
+
groups.map! { |k| k.printable_tasks(false).first }
|
|
275
|
+
list["root"] = groups
|
|
278
276
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
# Order namespaces with default coming first
|
|
278
|
+
list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
|
|
279
|
+
list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
|
|
280
|
+
end
|
|
283
281
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
282
|
+
def display_tasks(namespace, list) #:nodoc:
|
|
283
|
+
list.sort!{ |a,b| a[0] <=> b[0] }
|
|
284
|
+
|
|
285
|
+
say shell.set_color(namespace, :blue, true)
|
|
286
|
+
say "-" * namespace.size
|
|
287
|
+
|
|
288
|
+
print_table(list, :truncate => true)
|
|
289
|
+
say
|
|
288
290
|
end
|
|
289
291
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
unless klass.tasks.empty?
|
|
294
|
-
base = klass.namespace
|
|
292
|
+
def show_modules #:nodoc:
|
|
293
|
+
info = []
|
|
294
|
+
labels = ["Modules", "Namespaces"]
|
|
295
295
|
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
info << labels
|
|
297
|
+
info << [ "-" * labels[0].size, "-" * labels[1].size ]
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
thor_yaml.each do |name, hash|
|
|
300
|
+
info << [ name, hash[:namespaces].join(", ") ]
|
|
301
301
|
end
|
|
302
|
+
|
|
303
|
+
print_table info
|
|
304
|
+
say ""
|
|
302
305
|
end
|
|
303
306
|
end
|
data/lib/thor/shell/color.rb
CHANGED
|
@@ -63,7 +63,7 @@ class Thor
|
|
|
63
63
|
#
|
|
64
64
|
def show_diff(destination, content) #:nodoc:
|
|
65
65
|
if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil?
|
|
66
|
-
actual = File.
|
|
66
|
+
actual = File.binread(destination).to_s.split("\n")
|
|
67
67
|
content = content.to_s.split("\n")
|
|
68
68
|
|
|
69
69
|
Diff::LCS.sdiff(actual, content).each do |diff|
|
data/lib/thor/task.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
class Thor
|
|
2
2
|
class Task < Struct.new(:name, :description, :usage, :options)
|
|
3
|
+
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
|
|
3
4
|
|
|
4
5
|
# A dynamic task that handles method missing scenarios.
|
|
5
|
-
#
|
|
6
6
|
class Dynamic < Task
|
|
7
|
-
def initialize(name)
|
|
8
|
-
super(name.to_s, "A dynamically-generated task", name.to_s)
|
|
7
|
+
def initialize(name, options=nil)
|
|
8
|
+
super(name.to_s, "A dynamically-generated task", name.to_s, options)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def run(instance, args=[])
|
|
@@ -27,7 +27,6 @@ class Thor
|
|
|
27
27
|
|
|
28
28
|
# By default, a task invokes a method in the thor class. You can change this
|
|
29
29
|
# implementation to create custom tasks.
|
|
30
|
-
#
|
|
31
30
|
def run(instance, args=[])
|
|
32
31
|
raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
|
|
33
32
|
instance.send(name, *args)
|
|
@@ -41,8 +40,8 @@ class Thor
|
|
|
41
40
|
|
|
42
41
|
# Returns the formatted usage by injecting given required arguments
|
|
43
42
|
# and required options into the given usage.
|
|
44
|
-
def formatted_usage(klass, namespace=
|
|
45
|
-
namespace = klass.namespace
|
|
43
|
+
def formatted_usage(klass, namespace=true)
|
|
44
|
+
namespace = klass.namespace unless namespace == false
|
|
46
45
|
|
|
47
46
|
# Add namespace
|
|
48
47
|
formatted = if namespace
|
|
@@ -54,7 +53,7 @@ class Thor
|
|
|
54
53
|
# Add usage with required arguments
|
|
55
54
|
formatted << if klass && !klass.arguments.empty?
|
|
56
55
|
usage.to_s.gsub(/^#{name}/) do |match|
|
|
57
|
-
match << " " <<
|
|
56
|
+
match << " " << klass.arguments.map{ |a| a.usage }.compact.join(' ')
|
|
58
57
|
end
|
|
59
58
|
else
|
|
60
59
|
usage.to_s
|
|
@@ -69,25 +68,25 @@ class Thor
|
|
|
69
68
|
|
|
70
69
|
protected
|
|
71
70
|
|
|
72
|
-
def required_arguments(klass)
|
|
73
|
-
klass.arguments.map{ |a| a.usage if a.required? }.compact.join(' ')
|
|
74
|
-
end
|
|
75
|
-
|
|
76
71
|
def required_options
|
|
77
72
|
@required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
|
|
78
73
|
end
|
|
79
74
|
|
|
80
75
|
# Given a target, checks if this class name is not a private/protected method.
|
|
81
|
-
#
|
|
82
76
|
def public_method?(instance) #:nodoc:
|
|
83
77
|
collection = instance.private_methods + instance.protected_methods
|
|
84
78
|
(collection & [name.to_s, name.to_sym]).empty?
|
|
85
79
|
end
|
|
86
80
|
|
|
81
|
+
def sans_backtrace(backtrace, caller) #:nodoc:
|
|
82
|
+
saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
|
|
83
|
+
saned -= caller
|
|
84
|
+
end
|
|
85
|
+
|
|
87
86
|
def parse_argument_error(instance, e, caller) #:nodoc:
|
|
88
|
-
|
|
87
|
+
backtrace = sans_backtrace(e.backtrace, caller)
|
|
89
88
|
|
|
90
|
-
if e.message =~ /wrong number of arguments/
|
|
89
|
+
if backtrace.empty? && e.message =~ /wrong number of arguments/
|
|
91
90
|
if instance.is_a?(Thor::Group)
|
|
92
91
|
raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
|
|
93
92
|
else
|
data/lib/thor/util.rb
CHANGED
|
@@ -76,8 +76,10 @@ class Thor
|
|
|
76
76
|
# Returns the thor classes declared inside the given class.
|
|
77
77
|
#
|
|
78
78
|
def self.thor_classes_in(klass)
|
|
79
|
+
stringfied_constants = klass.constants.map { |c| c.to_s }
|
|
79
80
|
Thor::Base.subclasses.select do |subclass|
|
|
80
|
-
|
|
81
|
+
next unless subclass.name
|
|
82
|
+
stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ''))
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
|
|
@@ -155,7 +157,7 @@ class Thor
|
|
|
155
157
|
# inside the sandbox to avoid namespacing conflicts.
|
|
156
158
|
#
|
|
157
159
|
def self.load_thorfile(path, content=nil)
|
|
158
|
-
content ||= File.
|
|
160
|
+
content ||= File.binread(path)
|
|
159
161
|
|
|
160
162
|
begin
|
|
161
163
|
Thor::Sandbox.class_eval(content, path)
|
|
@@ -164,26 +166,6 @@ class Thor
|
|
|
164
166
|
end
|
|
165
167
|
end
|
|
166
168
|
|
|
167
|
-
# Receives a yaml (hash) and updates all constants entries to namespace.
|
|
168
|
-
# This was added to deal with deprecated versions of Thor.
|
|
169
|
-
#
|
|
170
|
-
# TODO Deprecate this method in the future.
|
|
171
|
-
#
|
|
172
|
-
# ==== Returns
|
|
173
|
-
# TrueClass|FalseClass:: Returns true if any change to the yaml file was made.
|
|
174
|
-
#
|
|
175
|
-
def self.convert_constants_to_namespaces(yaml)
|
|
176
|
-
yaml_changed = false
|
|
177
|
-
|
|
178
|
-
yaml.each do |k, v|
|
|
179
|
-
next unless v[:constants] && v[:namespaces].nil?
|
|
180
|
-
yaml_changed = true
|
|
181
|
-
yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.namespace_from_thor_class(c)}
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
yaml_changed
|
|
185
|
-
end
|
|
186
|
-
|
|
187
169
|
def self.user_home
|
|
188
170
|
@@user_home ||= if ENV["HOME"]
|
|
189
171
|
ENV["HOME"]
|