thor 0.9.9 → 0.11.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +29 -4
- data/README.rdoc +234 -0
- data/Thorfile +57 -0
- data/VERSION +1 -0
- data/bin/rake2thor +4 -0
- data/bin/thor +1 -1
- data/lib/thor.rb +216 -119
- data/lib/thor/actions.rb +272 -0
- data/lib/thor/actions/create_file.rb +102 -0
- data/lib/thor/actions/directory.rb +87 -0
- data/lib/thor/actions/empty_directory.rb +133 -0
- data/lib/thor/actions/file_manipulation.rb +195 -0
- data/lib/thor/actions/inject_into_file.rb +78 -0
- data/lib/thor/base.rb +510 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/thor/error.rb +25 -1
- data/lib/thor/group.rb +263 -0
- data/lib/thor/invocation.rb +178 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/parser/argument.rb +67 -0
- data/lib/thor/parser/arguments.rb +145 -0
- data/lib/thor/parser/option.rb +132 -0
- data/lib/thor/parser/options.rb +142 -0
- data/lib/thor/rake_compat.rb +67 -0
- data/lib/thor/runner.rb +232 -242
- data/lib/thor/shell.rb +72 -0
- data/lib/thor/shell/basic.rb +220 -0
- data/lib/thor/shell/color.rb +108 -0
- data/lib/thor/task.rb +97 -60
- data/lib/thor/util.rb +230 -55
- data/spec/actions/create_file_spec.rb +170 -0
- data/spec/actions/directory_spec.rb +118 -0
- data/spec/actions/empty_directory_spec.rb +91 -0
- data/spec/actions/file_manipulation_spec.rb +242 -0
- data/spec/actions/inject_into_file_spec.rb +80 -0
- data/spec/actions_spec.rb +291 -0
- data/spec/base_spec.rb +236 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
- data/spec/core_ext/ordered_hash_spec.rb +115 -0
- data/spec/fixtures/bundle/execute.rb +6 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/group_spec.rb +177 -0
- data/spec/invocation_spec.rb +107 -0
- data/spec/parser/argument_spec.rb +47 -0
- data/spec/parser/arguments_spec.rb +64 -0
- data/spec/parser/option_spec.rb +212 -0
- data/spec/parser/options_spec.rb +255 -0
- data/spec/rake_compat_spec.rb +64 -0
- data/spec/runner_spec.rb +204 -0
- data/spec/shell/basic_spec.rb +206 -0
- data/spec/shell/color_spec.rb +41 -0
- data/spec/shell_spec.rb +25 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/task_spec.rb +82 -0
- data/spec/thor_spec.rb +234 -0
- data/spec/util_spec.rb +196 -0
- metadata +69 -25
- data/README.markdown +0 -76
- data/Rakefile +0 -6
- data/lib/thor/options.rb +0 -242
- data/lib/thor/ordered_hash.rb +0 -64
- data/lib/thor/task_hash.rb +0 -22
- data/lib/thor/tasks.rb +0 -77
- data/lib/thor/tasks/package.rb +0 -18
data/CHANGELOG.rdoc
CHANGED
@@ -1,9 +1,34 @@
|
|
1
1
|
== TODO
|
2
2
|
|
3
|
-
* Change Thor.start to parse ARGV in a single pass
|
4
3
|
* Improve spec coverage for Thor::Runner
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
== 0.11.x, released 2009-07-01
|
6
|
+
|
7
|
+
* Added a rake compatibility layer. It allows you to use spec and rdoc tasks on
|
8
|
+
Thor classes.
|
9
|
+
|
10
|
+
* BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
|
11
|
+
since it wrong behavior to the invocation system.
|
12
|
+
|
13
|
+
* thor help now show information about any class/task. All those calls are
|
14
|
+
possible:
|
15
|
+
|
16
|
+
thor help describe
|
17
|
+
thor help describe:amazing
|
18
|
+
|
19
|
+
Or even with default namespaces:
|
20
|
+
|
21
|
+
thor help :spec
|
22
|
+
|
23
|
+
* Thor::Runner now invokes the default task if none is supplied:
|
24
|
+
|
25
|
+
thor describe # invokes the default task, usually help
|
26
|
+
|
27
|
+
* Thor::Runner now works with mappings:
|
28
|
+
|
29
|
+
thor describe -h
|
30
|
+
|
31
|
+
* Added some documentation and code refactoring.
|
7
32
|
|
8
33
|
== 0.9.8, released 2008-10-20
|
9
34
|
|
@@ -49,4 +74,4 @@
|
|
49
74
|
* Thor::Options now doesn't parse through things that look like options but aren't.
|
50
75
|
* Add URI detection to install task, and make sure we don't append ".thor" to URIs
|
51
76
|
* Add rake2thor to the gem binfiles.
|
52
|
-
* Make sure local Thorfiles override system-wide ones.
|
77
|
+
* Make sure local Thorfiles override system-wide ones.
|
data/README.rdoc
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
= thor
|
2
|
+
|
3
|
+
Map options to a class. Simply create a class with the appropriate annotations
|
4
|
+
and have options automatically map to functions and parameters.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
|
8
|
+
class App < Thor # [1]
|
9
|
+
map "-L" => :list # [2]
|
10
|
+
|
11
|
+
desc "install APP_NAME", "install one of the available apps" # [3]
|
12
|
+
method_options :force => :boolean, :alias => :string # [4]
|
13
|
+
def install(name)
|
14
|
+
user_alias = options[:alias]
|
15
|
+
if options.force?
|
16
|
+
# do something
|
17
|
+
end
|
18
|
+
# other code
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
|
22
|
+
def list(search="")
|
23
|
+
# list everything
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Thor automatically maps commands as such:
|
28
|
+
|
29
|
+
thor app:install myname --force
|
30
|
+
|
31
|
+
That gets converted to:
|
32
|
+
|
33
|
+
App.new.install("myname")
|
34
|
+
# with {'force' => true} as options hash
|
35
|
+
|
36
|
+
1. Inherit from Thor to turn a class into an option mapper
|
37
|
+
2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list
|
38
|
+
3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description
|
39
|
+
4. Provide any additional options that will be available the instance method options.
|
40
|
+
|
41
|
+
== Types for <tt>method_options</tt>
|
42
|
+
|
43
|
+
* :boolean - is parsed as <tt>--option</tt> or <tt>--option=true</tt>
|
44
|
+
* :string - is parsed as <tt>--option=VALUE</tt>
|
45
|
+
* :numeric - is parsed as <tt>--option=N</tt>
|
46
|
+
* :array - is parsed as <tt>--option=one two three</tt>
|
47
|
+
* :hash - is parsed as <tt>--option=name:string age:integer</tt>
|
48
|
+
|
49
|
+
Besides, method_option allows a default value to be given, examples:
|
50
|
+
|
51
|
+
method_options :force => false
|
52
|
+
#=> Creates a boolean option with default value false
|
53
|
+
|
54
|
+
method_options :alias => "bar"
|
55
|
+
#=> Creates a string option with default value "bar"
|
56
|
+
|
57
|
+
method_options :threshold => 3.0
|
58
|
+
#=> Creates a numeric option with default value 3.0
|
59
|
+
|
60
|
+
You can also supply <tt>:option => :required</tt> to mark an option as required. The
|
61
|
+
type is assumed to be string. If you want a required hash with default values
|
62
|
+
as option, you can use <tt>method_option</tt> which uses a more declarative style:
|
63
|
+
|
64
|
+
method_option :attributes, :type => :hash, :default => {}, :required => true
|
65
|
+
|
66
|
+
All arguments can be set to nil (except required arguments), by suppling a no or
|
67
|
+
skip variant. For example:
|
68
|
+
|
69
|
+
thor app name --no-attributes
|
70
|
+
|
71
|
+
In previous versions, aliases for options were created automatically, but now
|
72
|
+
they should be explicit. You can supply aliases in both short and declarative
|
73
|
+
styles:
|
74
|
+
|
75
|
+
method_options %w( force -f ) => :boolean
|
76
|
+
|
77
|
+
Or:
|
78
|
+
|
79
|
+
method_option :force, :type => :boolean, :aliases => "-f"
|
80
|
+
|
81
|
+
You can supply as many aliases as you want.
|
82
|
+
|
83
|
+
NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead.
|
84
|
+
|
85
|
+
== Namespaces
|
86
|
+
|
87
|
+
By default, your Thor tasks are invoked using Ruby namespace. In the example
|
88
|
+
above, tasks are invoked as:
|
89
|
+
|
90
|
+
thor app:install name --force
|
91
|
+
|
92
|
+
However, you could namespace your class as:
|
93
|
+
|
94
|
+
module Sinatra
|
95
|
+
class App < Thor
|
96
|
+
# tasks
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
And then you should invoke your tasks as:
|
101
|
+
|
102
|
+
thor sinatra:app:install name --force
|
103
|
+
|
104
|
+
If desired, you can change the namespace:
|
105
|
+
|
106
|
+
module Sinatra
|
107
|
+
class App < Thor
|
108
|
+
namespace :myapp
|
109
|
+
# tasks
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
And then your tasks hould be invoked as:
|
114
|
+
|
115
|
+
thor myapp:install name --force
|
116
|
+
|
117
|
+
== Invocations
|
118
|
+
|
119
|
+
Thor comes with a invocation-dependency system as well which allows a task to be
|
120
|
+
invoked only once. For example:
|
121
|
+
|
122
|
+
class Counter < Thor
|
123
|
+
desc "one", "Prints 1, 2, 3"
|
124
|
+
def one
|
125
|
+
puts 1
|
126
|
+
invoke :two
|
127
|
+
invoke :three
|
128
|
+
end
|
129
|
+
|
130
|
+
desc "two", "Prints 2, 3"
|
131
|
+
def two
|
132
|
+
puts 2
|
133
|
+
invoke :three
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "three", "Prints 3"
|
137
|
+
def three
|
138
|
+
puts 3
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
When invoking the task one:
|
143
|
+
|
144
|
+
thor counter:one
|
145
|
+
|
146
|
+
The output is "1 2 3", which means that the three task was invoked only once.
|
147
|
+
You can even invoke tasks from another class, so be sure to check the
|
148
|
+
documentation.
|
149
|
+
|
150
|
+
== Thor::Group
|
151
|
+
|
152
|
+
Thor has a special class called Thor::Group. The main difference to Thor class
|
153
|
+
is that it invokes all tasks at once. The example above could be rewritten in
|
154
|
+
Thor::Group as this:
|
155
|
+
|
156
|
+
class Counter < Thor::Group
|
157
|
+
desc "Prints 1, 2, 3"
|
158
|
+
|
159
|
+
def one
|
160
|
+
puts 1
|
161
|
+
end
|
162
|
+
|
163
|
+
def two
|
164
|
+
puts 2
|
165
|
+
end
|
166
|
+
|
167
|
+
def three
|
168
|
+
puts 3
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
When invoked:
|
173
|
+
|
174
|
+
thor counter
|
175
|
+
|
176
|
+
It prints "1 2 3" as well. Notice you should describe (using the method <tt>desc</tt>)
|
177
|
+
only the class and not each task anymore. Thor::Group is a great tool to create
|
178
|
+
generators, since you can define several steps which are invoked in the order they
|
179
|
+
are defined (Thor::Group is the tool use in generators in Rails 3.0).
|
180
|
+
|
181
|
+
Besides, Thor::Group can parse arguments and options as Thor tasks:
|
182
|
+
|
183
|
+
class Counter < Thor::Group
|
184
|
+
# number will be available as attr_accessor
|
185
|
+
argument :number, :type => :numeric, :desc => "The number to start counting"
|
186
|
+
desc "Prints the 'number' given upto 'number+2'"
|
187
|
+
|
188
|
+
def one
|
189
|
+
puts number + 0
|
190
|
+
end
|
191
|
+
|
192
|
+
def two
|
193
|
+
puts number + 1
|
194
|
+
end
|
195
|
+
|
196
|
+
def three
|
197
|
+
puts number + 2
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
The counter above expects one parameter and has the folling outputs:
|
202
|
+
|
203
|
+
thor counter 5
|
204
|
+
# Prints "5 6 7"
|
205
|
+
|
206
|
+
thor counter 11
|
207
|
+
# Prints "11 12 13"
|
208
|
+
|
209
|
+
You can also give options to Thor::Group, but instead of using <tt>method_option</tt>
|
210
|
+
and <tt>method_options</tt>, you should use <tt>class_option</tt> and <tt>class_options</tt>.
|
211
|
+
Both argument and class_options methods are available to Thor class as well.
|
212
|
+
|
213
|
+
== Actions
|
214
|
+
|
215
|
+
Thor comes with several actions which helps with script and generator tasks. You
|
216
|
+
might be familiar with them since some came from Rails Templates. They are:
|
217
|
+
<tt>say</tt>, <tt>ask</tt>, <tt>yes?</tt>, <tt>no?</tt>, <tt>add_file</tt>,
|
218
|
+
<tt>remove_file</tt>, <tt>copy_file</tt>, <tt>template</tt>, <tt>directory</tt>,
|
219
|
+
<tt>inside</tt>, <tt>run</tt>, <tt>inject_into_file</tt> and a couple more.
|
220
|
+
|
221
|
+
To use them, you just need to include Thor::Actions in your Thor classes:
|
222
|
+
|
223
|
+
class App < Thor
|
224
|
+
include Thor::Actions
|
225
|
+
# tasks
|
226
|
+
end
|
227
|
+
|
228
|
+
Some actions like copy file requires that a class method called source_root is
|
229
|
+
defined in your class. This is the directory where your templates should be
|
230
|
+
placed. Be sure to check the documentation.
|
231
|
+
|
232
|
+
== License
|
233
|
+
|
234
|
+
See MIT LICENSE.
|
data/Thorfile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'thor/rake_compat'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'rdoc/task'
|
5
|
+
|
6
|
+
GEM_NAME = 'thor'
|
7
|
+
EXTRA_RDOC_FILES = ["README.rdoc", "LICENSE", "CHANGELOG.rdoc", "VERSION", "Thorfile"]
|
8
|
+
|
9
|
+
class Default < Thor
|
10
|
+
include Thor::RakeCompat
|
11
|
+
|
12
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
13
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
14
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
15
|
+
end
|
16
|
+
|
17
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
18
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
19
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
20
|
+
t.rcov = true
|
21
|
+
t.rcov_dir = "rcov"
|
22
|
+
end
|
23
|
+
|
24
|
+
RDoc::Task.new do |rdoc|
|
25
|
+
rdoc.main = "README.rdoc"
|
26
|
+
rdoc.rdoc_dir = "rdoc"
|
27
|
+
rdoc.title = GEM_NAME
|
28
|
+
rdoc.rdoc_files.include(*EXTRA_RDOC_FILES)
|
29
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
30
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'jeweler'
|
35
|
+
Jeweler::Tasks.new do |s|
|
36
|
+
s.name = GEM_NAME
|
37
|
+
s.version = "0.11.4"
|
38
|
+
s.rubyforge_project = "textmate"
|
39
|
+
s.platform = Gem::Platform::RUBY
|
40
|
+
s.summary = "A scripting framework that replaces rake, sake and rubigen"
|
41
|
+
s.email = "ruby-thor@googlegroups.com"
|
42
|
+
s.homepage = "http://yehudakatz.com"
|
43
|
+
s.description = "A scripting framework that replaces rake, sake and rubigen"
|
44
|
+
s.authors = ['Yehuda Katz', 'José Valim']
|
45
|
+
s.has_rdoc = true
|
46
|
+
s.extra_rdoc_files = EXTRA_RDOC_FILES
|
47
|
+
s.require_path = 'lib'
|
48
|
+
s.bindir = "bin"
|
49
|
+
s.executables = %w( thor rake2thor )
|
50
|
+
s.files = s.extra_rdoc_files + Dir.glob("{bin,lib}/**/*")
|
51
|
+
s.files.exclude 'spec/sandbox/**/*'
|
52
|
+
s.test_files.exclude 'spec/sandbox/**/*'
|
53
|
+
end
|
54
|
+
rescue LoadError
|
55
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
56
|
+
end
|
57
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.11.5
|
data/bin/rake2thor
CHANGED
data/bin/thor
CHANGED
data/lib/thor.rb
CHANGED
@@ -1,146 +1,243 @@
|
|
1
1
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require "thor/task_hash"
|
2
|
+
require 'thor/base'
|
3
|
+
require 'thor/group'
|
4
|
+
require 'thor/actions'
|
6
5
|
|
7
6
|
class Thor
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
class << self
|
8
|
+
# Sets the default task when thor is executed without an explicit task to be called.
|
9
|
+
#
|
10
|
+
# ==== Parameters
|
11
|
+
# meth<Symbol>:: name of the defaut task
|
12
|
+
#
|
13
|
+
def default_task(meth=nil)
|
14
|
+
case meth
|
15
|
+
when :none
|
16
|
+
@default_task = 'help'
|
17
|
+
when nil
|
18
|
+
@default_task ||= from_superclass(:default_task, 'help')
|
19
|
+
else
|
20
|
+
@default_task = meth.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Defines the usage and the description of the next task.
|
25
|
+
#
|
26
|
+
# ==== Parameters
|
27
|
+
# usage<String>
|
28
|
+
# description<String>
|
29
|
+
#
|
30
|
+
def desc(usage, description, options={})
|
31
|
+
if options[:for]
|
32
|
+
task = find_and_refresh_task(options[:for])
|
33
|
+
task.usage = usage if usage
|
34
|
+
task.description = description if description
|
15
35
|
else
|
16
|
-
@
|
36
|
+
@usage, @desc = usage, description
|
17
37
|
end
|
18
38
|
end
|
19
|
-
end
|
20
39
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
# Maps an input to a task. If you define:
|
41
|
+
#
|
42
|
+
# map "-T" => "list"
|
43
|
+
#
|
44
|
+
# Running:
|
45
|
+
#
|
46
|
+
# thor -T
|
47
|
+
#
|
48
|
+
# Will invoke the list task.
|
49
|
+
#
|
50
|
+
# ==== Parameters
|
51
|
+
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task.
|
52
|
+
#
|
53
|
+
def map(mappings=nil)
|
54
|
+
@map ||= from_superclass(:map, {})
|
55
|
+
|
56
|
+
if mappings
|
57
|
+
mappings.each do |key, value|
|
58
|
+
if key.respond_to?(:each)
|
59
|
+
key.each {|subkey| @map[subkey] = value}
|
60
|
+
else
|
61
|
+
@map[key] = value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
36
65
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.subclasses
|
42
|
-
@subclasses ||= []
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.tasks
|
46
|
-
@tasks ||= TaskHash.new(self)
|
47
|
-
end
|
66
|
+
@map
|
67
|
+
end
|
48
68
|
|
49
|
-
|
50
|
-
|
51
|
-
|
69
|
+
# Declares the options for the next task to be declared.
|
70
|
+
#
|
71
|
+
# ==== Parameters
|
72
|
+
# Hash[Symbol => Object]:: The hash key is the name of the option and the value
|
73
|
+
# is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
|
74
|
+
# or :required (string). If you give a value, the type of the value is used.
|
75
|
+
#
|
76
|
+
def method_options(options=nil)
|
77
|
+
@method_options ||= {}
|
78
|
+
build_options(options, @method_options) if options
|
79
|
+
@method_options
|
80
|
+
end
|
52
81
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
82
|
+
# Adds an option to the set of class options. If :for is given as option,
|
83
|
+
# it allows you to change the options from a previous defined task.
|
84
|
+
#
|
85
|
+
# def previous_task
|
86
|
+
# # magic
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# method_options :foo => :bar, :for => :previous_task
|
90
|
+
#
|
91
|
+
# def next_task
|
92
|
+
# # magic
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# ==== Parameters
|
96
|
+
# name<Symbol>:: The name of the argument.
|
97
|
+
# options<Hash>:: Described below.
|
98
|
+
#
|
99
|
+
# ==== Options
|
100
|
+
# :desc - Description for the argument.
|
101
|
+
# :required - If the argument is required or not.
|
102
|
+
# :default - Default value for this argument. It cannot be required and have default values.
|
103
|
+
# :aliases - Aliases for this option.
|
104
|
+
# :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
|
105
|
+
# :group - The group for this options. Use by class options to output options in different levels.
|
106
|
+
# :banner - String to show on usage notes.
|
107
|
+
#
|
108
|
+
def method_option(name, options={})
|
109
|
+
scope = if options[:for]
|
110
|
+
find_and_refresh_task(options[:for]).options
|
111
|
+
else
|
112
|
+
method_options
|
113
|
+
end
|
59
114
|
|
60
|
-
|
61
|
-
@maxima ||= begin
|
62
|
-
max_usage = tasks.map {|_, t| t.usage}.max {|x,y| x.to_s.size <=> y.to_s.size}.size
|
63
|
-
max_desc = tasks.map {|_, t| t.description}.max {|x,y| x.to_s.size <=> y.to_s.size}.size
|
64
|
-
max_opts = tasks.map {|_, t| t.opts ? t.opts.formatted_usage : ""}.max {|x,y| x.to_s.size <=> y.to_s.size}.size
|
65
|
-
Struct.new(:description, :usage, :opt).new(max_desc, max_usage, max_opts)
|
115
|
+
build_option(name, options, scope)
|
66
116
|
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.start(args = ARGV)
|
70
|
-
options = Thor::Options.new(self.opts)
|
71
|
-
opts = options.parse(args, false)
|
72
|
-
args = options.trailing_non_opts
|
73
|
-
|
74
|
-
meth = args.first
|
75
|
-
meth = @map[meth].to_s if @map && @map[meth]
|
76
|
-
meth ||= "help"
|
77
|
-
|
78
|
-
tasks[meth].parse new(opts, *args), args[1..-1]
|
79
|
-
rescue Thor::Error => e
|
80
|
-
$stderr.puts e.message
|
81
|
-
end
|
82
117
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
118
|
+
# Parses the task and options from the given args, instantiate the class
|
119
|
+
# and invoke the task. This method is used when the arguments must be parsed
|
120
|
+
# from an array. If you are inside Ruby and want to use a Thor class, you
|
121
|
+
# can simply initialize it:
|
122
|
+
#
|
123
|
+
# script = MyScript.new(args, options, config)
|
124
|
+
# script.invoke(:task, first_arg, second_arg, third_arg)
|
125
|
+
#
|
126
|
+
def start(given_args=ARGV, config={})
|
127
|
+
super do
|
128
|
+
meth = normalize_task_name(given_args.shift)
|
129
|
+
task = all_tasks[meth]
|
130
|
+
|
131
|
+
if task
|
132
|
+
args, opts = Thor::Options.split(given_args)
|
133
|
+
config.merge!(:task_options => task.options)
|
134
|
+
else
|
135
|
+
args, opts = given_args, {}
|
136
|
+
end
|
137
|
+
|
138
|
+
task ||= Task.dynamic(meth)
|
139
|
+
trailing = args[Range.new(arguments.size, -1)]
|
140
|
+
new(args, opts, config).invoke(task, trailing || [])
|
141
|
+
end
|
87
142
|
end
|
88
143
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
144
|
+
# Prints help information. If a task name is given, it shows information
|
145
|
+
# only about the specific task.
|
146
|
+
#
|
147
|
+
# ==== Parameters
|
148
|
+
# meth<String>:: An optional task name to print usage information about.
|
149
|
+
#
|
150
|
+
# ==== Options
|
151
|
+
# namespace:: When true, shows the namespace in the output before the usage.
|
152
|
+
# skip_inherited:: When true, does not show tasks from superclass.
|
153
|
+
#
|
154
|
+
def help(shell, meth=nil, options={})
|
155
|
+
meth, options = nil, meth if meth.is_a?(Hash)
|
156
|
+
|
157
|
+
if meth
|
158
|
+
task = all_tasks[meth]
|
159
|
+
raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task
|
160
|
+
|
161
|
+
shell.say "Usage:"
|
162
|
+
shell.say " #{banner(task, options[:namespace], false)}"
|
163
|
+
shell.say
|
164
|
+
class_options_help(shell, "Class", :Method => task.options.map { |_, o| o })
|
165
|
+
shell.say task.description
|
166
|
+
else
|
167
|
+
list = (options[:short] ? tasks : all_tasks).map do |_, task|
|
168
|
+
item = [ banner(task, options[:namespace]) ]
|
169
|
+
item << "# #{task.short_description}" if task.short_description
|
170
|
+
item << " "
|
171
|
+
end
|
172
|
+
|
173
|
+
options[:ident] ||= 2
|
174
|
+
if options[:short]
|
175
|
+
shell.print_list(list, :ident => options[:ident])
|
176
|
+
else
|
177
|
+
shell.say "Tasks:"
|
178
|
+
shell.print_list(list, :ident => options[:ident])
|
179
|
+
end
|
180
|
+
|
181
|
+
Thor::Util.thor_classes_in(self).each do |subclass|
|
182
|
+
namespace = options[:namespace] == true || subclass.namespace.gsub(/^#{self.namespace}:/, '')
|
183
|
+
subclass.help(shell, options.merge(:short => true, :namespace => namespace))
|
184
|
+
end
|
185
|
+
|
186
|
+
class_options_help(shell, "Class") unless options[:short]
|
96
187
|
end
|
188
|
+
end
|
97
189
|
|
98
|
-
|
99
|
-
register_klass_file self
|
100
|
-
|
101
|
-
tasks[meth] = Task.new(meth, @desc, @usage, @method_options)
|
190
|
+
protected
|
102
191
|
|
103
|
-
|
104
|
-
|
192
|
+
# The banner for this class. You can customize it if you are invoking the
|
193
|
+
# thor class by another ways which is not the Thor::Runner. It receives
|
194
|
+
# the task that is going to be invoked and a boolean which indicates if
|
195
|
+
# the namespace should be displayed as arguments.
|
196
|
+
#
|
197
|
+
def banner(task, namespace=true, show_options=true)
|
198
|
+
task.formatted_usage(self, namespace, show_options)
|
199
|
+
end
|
105
200
|
|
106
|
-
|
107
|
-
|
108
|
-
superclass.register_klass_file(klass, file)
|
109
|
-
return
|
201
|
+
def baseclass #:nodoc:
|
202
|
+
Thor
|
110
203
|
end
|
111
204
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
205
|
+
def create_task(meth) #:nodoc:
|
206
|
+
if @usage && @desc
|
207
|
+
tasks[meth.to_s] = Thor::Task.new(meth, @desc, @usage, method_options)
|
208
|
+
@usage, @desc, @method_options = nil
|
209
|
+
true
|
210
|
+
elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing
|
211
|
+
true
|
212
|
+
else
|
213
|
+
puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
|
214
|
+
"Call desc if you want this method to be available as task or declare it inside a " <<
|
215
|
+
"no_tasks{} block. Invoked from #{caller[1].inspect}."
|
216
|
+
false
|
217
|
+
end
|
218
|
+
end
|
117
219
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
map ["-h", "-?", "--help", "-D"] => :help
|
122
|
-
|
123
|
-
desc "help [TASK]", "describe available tasks or one specific task"
|
124
|
-
def help(task = nil)
|
125
|
-
if task
|
126
|
-
if task.include? ?:
|
127
|
-
task = self.class[task]
|
128
|
-
namespace = true
|
129
|
-
else
|
130
|
-
task = self.class.tasks[task]
|
220
|
+
def initialize_added #:nodoc:
|
221
|
+
class_options.merge!(method_options)
|
222
|
+
@method_options = nil
|
131
223
|
end
|
132
224
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
print format % ("#{task.formatted_usage}")
|
141
|
-
puts task.description.split("\n").first
|
225
|
+
# Receives a task name (can be nil), and try to get a map from it.
|
226
|
+
# If a map can't be found use the sent name or the default task.
|
227
|
+
#
|
228
|
+
def normalize_task_name(meth) #:nodoc:
|
229
|
+
mapping = map[meth.to_s]
|
230
|
+
meth = mapping || meth || default_task
|
231
|
+
meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
|
142
232
|
end
|
143
|
-
end
|
144
233
|
end
|
145
|
-
|
234
|
+
|
235
|
+
include Thor::Base
|
236
|
+
|
237
|
+
map HELP_MAPPINGS => :help
|
238
|
+
|
239
|
+
desc "help [TASK]", "Describe available tasks or one specific task"
|
240
|
+
def help(task=nil)
|
241
|
+
self.class.help(shell, task, :namespace => task && task.include?(?:))
|
242
|
+
end
|
146
243
|
end
|