thor 0.13.4 → 0.14.3

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.
Files changed (45) hide show
  1. data/CHANGELOG.rdoc +5 -3
  2. data/{README.rdoc → README.md} +65 -55
  3. data/Thorfile +3 -2
  4. data/lib/thor/actions/create_file.rb +3 -1
  5. data/lib/thor/actions/directory.rb +4 -2
  6. data/lib/thor/actions/file_manipulation.rb +30 -7
  7. data/lib/thor/actions.rb +31 -9
  8. data/lib/thor/base.rb +38 -15
  9. data/lib/thor/core_ext/hash_with_indifferent_access.rb +1 -1
  10. data/lib/thor/group.rb +22 -20
  11. data/lib/thor/invocation.rb +45 -57
  12. data/lib/thor/parser/argument.rb +15 -15
  13. data/lib/thor/parser/arguments.rb +14 -3
  14. data/lib/thor/parser/option.rb +38 -46
  15. data/lib/thor/parser/options.rb +26 -22
  16. data/lib/thor/runner.rb +11 -16
  17. data/lib/thor/shell/basic.rb +48 -12
  18. data/lib/thor/shell/html.rb +121 -0
  19. data/lib/thor/shell.rb +7 -2
  20. data/lib/thor/task.rb +63 -51
  21. data/lib/thor/util.rb +15 -16
  22. data/lib/thor/version.rb +1 -1
  23. data/lib/thor.rb +118 -45
  24. data/spec/actions/directory_spec.rb +2 -2
  25. data/spec/actions/file_manipulation_spec.rb +7 -0
  26. data/spec/actions_spec.rb +21 -3
  27. data/spec/base_spec.rb +9 -3
  28. data/spec/fixtures/doc/block_helper.rb +3 -0
  29. data/spec/fixtures/doc/components/.empty_directory +0 -0
  30. data/spec/fixtures/group.thor +16 -4
  31. data/spec/fixtures/path with spaces +0 -0
  32. data/spec/fixtures/script.thor +48 -4
  33. data/spec/group_spec.rb +3 -3
  34. data/spec/invocation_spec.rb +1 -8
  35. data/spec/parser/option_spec.rb +2 -2
  36. data/spec/parser/options_spec.rb +27 -0
  37. data/spec/runner_spec.rb +16 -8
  38. data/spec/shell/basic_spec.rb +13 -4
  39. data/spec/shell/html_spec.rb +27 -0
  40. data/spec/shell_spec.rb +13 -0
  41. data/spec/spec_helper.rb +4 -3
  42. data/spec/task_spec.rb +7 -7
  43. data/spec/thor_spec.rb +103 -6
  44. data/spec/util_spec.rb +3 -7
  45. metadata +63 -7
data/CHANGELOG.rdoc CHANGED
@@ -1,8 +1,10 @@
1
1
  == 0.13, released 2010-02-03
2
2
 
3
- * Several bug fixes
3
+ * Added :lazy_default which is only triggered if a switch is given
4
+ * Added Thor::Shell::HTML
5
+ * Added subcommands
4
6
  * Decoupled Thor::Group and Thor, so it's easier to vendor
5
- * Added check_unknown_options! in case you want error messages to be raised in valid switches.
7
+ * Added check_unknown_options! in case you want error messages to be raised in valid switches
6
8
  * run(command) should return the results of command
7
9
 
8
10
  == 0.12, released 2010-01-02
@@ -20,7 +22,7 @@
20
22
  Thor classes.
21
23
 
22
24
  * BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore
23
- since it wrong behavior to the invocation system.
25
+ since it may cause wrong behavior in the invocation system.
24
26
 
25
27
  * thor help now show information about any class/task. All those calls are
26
28
  possible:
@@ -1,7 +1,21 @@
1
- = thor
1
+ # Thor
2
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.
3
+ ## Description
4
+
5
+ Thor is a simple and efficient tool for building self-documenting command line utilities. It removes the pain of parsing command line options, writing "USAGE:" banners, and can also be used as an alternative to the [Rake](http://github.com/jimweirich/rake) build tool. The syntax is Rake-like, so it should be familiar to most Rake users.
6
+
7
+ ## Installation
8
+
9
+ $ gem install thor
10
+
11
+ or
12
+
13
+ $ gem install wycats-thor -s http://gems.github.com
14
+
15
+ ## Usage
16
+
17
+ Map options to a class. Simply create a class with the appropriate annotations
18
+ and have options automatically map to functions and parameters.
5
19
 
6
20
  Example:
7
21
 
@@ -24,7 +38,7 @@ Example:
24
38
  end
25
39
  end
26
40
 
27
- Thor automatically maps commands as such:
41
+ Thor automatically maps commands as such:
28
42
 
29
43
  thor app:install myname --force
30
44
 
@@ -33,12 +47,12 @@ That gets converted to:
33
47
  App.new.install("myname")
34
48
  # with {'force' => true} as options hash
35
49
 
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.
50
+ 1. Inherit from Thor to turn a class into an option mapper.
51
+ 2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list
52
+ 3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description.
53
+ 4. Provide any additional options that will be available the instance method options.
40
54
 
41
- == Types for <tt>method_options</tt>
55
+ ## Types for <tt>method_options</tt>
42
56
 
43
57
  * :boolean - is parsed as <tt>--option</tt> or <tt>--option=true</tt>
44
58
  * :string - is parsed as <tt>--option=VALUE</tt>
@@ -46,7 +60,7 @@ That gets converted to:
46
60
  * :array - is parsed as <tt>--option=one two three</tt>
47
61
  * :hash - is parsed as <tt>--option=name:string age:integer</tt>
48
62
 
49
- Besides, method_option allows a default value to be given, examples:
63
+ Besides, method_option allows a default value to be given. Examples:
50
64
 
51
65
  method_options :force => false
52
66
  #=> Creates a boolean option with default value false
@@ -57,19 +71,19 @@ Besides, method_option allows a default value to be given, examples:
57
71
  method_options :threshold => 3.0
58
72
  #=> Creates a numeric option with default value 3.0
59
73
 
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
74
+ You can also supply <tt>:option => :required</tt> to mark an option as required. The
75
+ type is assumed to be string. If you want a required hash with default values
62
76
  as option, you can use <tt>method_option</tt> which uses a more declarative style:
63
77
 
64
78
  method_option :attributes, :type => :hash, :default => {}, :required => true
65
79
 
66
80
  All arguments can be set to nil (except required arguments), by suppling a no or
67
- skip variant. For example:
81
+ skip variant. For example:
68
82
 
69
83
  thor app name --no-attributes
70
84
 
71
85
  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
86
+ they should be explicit. You can supply aliases in both short and declarative
73
87
  styles:
74
88
 
75
89
  method_options %w( force -f ) => :boolean
@@ -82,9 +96,9 @@ You can supply as many aliases as you want.
82
96
 
83
97
  NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead.
84
98
 
85
- == Namespaces
99
+ ## Namespaces
86
100
 
87
- By default, your Thor tasks are invoked using Ruby namespace. In the example
101
+ By default, your Thor tasks are invoked using Ruby namespace. In the example
88
102
  above, tasks are invoked as:
89
103
 
90
104
  thor app:install name --force
@@ -110,14 +124,13 @@ If desired, you can change the namespace:
110
124
  end
111
125
  end
112
126
 
113
- And then your tasks hould be invoked as:
127
+ And then your tasks should be invoked as:
114
128
 
115
129
  thor myapp:install name --force
116
130
 
117
- == Invocations
131
+ ## Invocations
118
132
 
119
- Thor comes with a invocation-dependency system as well which allows a task to be
120
- invoked only once. For example:
133
+ Thor comes with a invocation-dependency system as well, which allows a task to be invoked only once. For example:
121
134
 
122
135
  class Counter < Thor
123
136
  desc "one", "Prints 1, 2, 3"
@@ -143,14 +156,16 @@ When invoking the task one:
143
156
 
144
157
  thor counter:one
145
158
 
146
- The output is "1 2 3", which means that the three task was invoked only once.
159
+ The output is "1 2 3", which means that the three task was invoked only once.
147
160
  You can even invoke tasks from another class, so be sure to check the
148
- documentation[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor.html].
161
+ [documentation](http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor.html).
149
162
 
150
- == Thor::Group
163
+ Notice invocations do not share the same object. I.e, Thor will instantiate Counter once to invoke the task one, then, it instantiates another to invoke the task two and another for task three. This happens to allow options and arguments to parsed again. For example, if two and three have different options and both of them were given to the command line, calling invoke makes them be parsed each time and used accordingly by each task.
151
164
 
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
165
+ ## Thor::Group
166
+
167
+ Thor has a special class called Thor::Group. The main difference to Thor class
168
+ is that it invokes all tasks at once. The example above could be rewritten in
154
169
  Thor::Group as this:
155
170
 
156
171
  class Counter < Thor::Group
@@ -173,12 +188,12 @@ When invoked:
173
188
 
174
189
  thor counter
175
190
 
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
191
+ It prints "1 2 3" as well. Notice you should describe (using the method <tt>desc</tt>)
192
+ only the class and not each task anymore. Thor::Group is a great tool to create
178
193
  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).
194
+ are defined (Thor::Group is the tool use in generators in Rails 3.0).
180
195
 
181
- Besides, Thor::Group can parse arguments and options as Thor tasks:
196
+ Besides, Thor::Group can parse arguments and options as Thor tasks:
182
197
 
183
198
  class Counter < Thor::Group
184
199
  # number will be available as attr_accessor
@@ -210,13 +225,13 @@ You can also give options to Thor::Group, but instead of using <tt>method_option
210
225
  and <tt>method_options</tt>, you should use <tt>class_option</tt> and <tt>class_options</tt>.
211
226
  Both argument and class_options methods are available to Thor class as well.
212
227
 
213
- == Actions
228
+ ## Actions
214
229
 
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:
230
+ Thor comes with several actions which helps with script and generator tasks. You
231
+ might be familiar with them since some came from Rails Templates. They are:
217
232
  <tt>say</tt>, <tt>ask</tt>, <tt>yes?</tt>, <tt>no?</tt>, <tt>add_file</tt>,
218
233
  <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.
234
+ <tt>inside</tt>, <tt>run</tt>, <tt>inject_into_file</tt> and a couple more.
220
235
 
221
236
  To use them, you just need to include Thor::Actions in your Thor classes:
222
237
 
@@ -226,13 +241,13 @@ To use them, you just need to include Thor::Actions in your Thor classes:
226
241
  end
227
242
 
228
243
  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 on actions[http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor/Actions.html].
244
+ defined in your class. This is the directory where your templates should be
245
+ placed. Be sure to check the documentation on [actions](http://rdoc.info/rdoc/wycats/thor/blob/f939a3e8a854616784cac1dcff04ef4f3ee5f7ff/Thor/Actions.html).
231
246
 
232
- == Generators
247
+ ## Generators
233
248
 
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:
249
+ A great use for Thor is creating custom generators. Combining Thor::Group,
250
+ Thor::Actions and ERB templates makes this very easy. Here is an example:
236
251
 
237
252
  class Newgem < Thor::Group
238
253
  include Thor::Actions
@@ -264,34 +279,29 @@ Thor::Actions and ERB templates makes this very easy. Here is an example:
264
279
  end
265
280
  end
266
281
 
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
282
+ Doing a <tt>thor -T</tt> will show how to run our generator. It should read:
283
+ <tt>thor newgem NAME</tt>. This shows that we have to supply a NAME
269
284
  argument for our generator to run.
270
285
 
271
286
  The <tt>create_lib_file</tt> uses an ERB template. This is what it looks like:
272
287
 
273
- class <%= name.camelize %>
288
+ class <%= name.capitalize %>
274
289
  end
275
290
 
276
291
  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
292
+ 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
293
  more options.
279
294
 
280
295
  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".
296
+ create two files: "devise/lib/devise.rb", and "devise/test/devise_test.rb". The user will then be asked (via a prompt by the <tt>yes?</tt> method) whether or not they would like to copy the MIT License. If you want to change the test framework, you can add the option: <tt>thor newgem devise --test-framework=rspec</tt>
297
+
298
+ This will generate two files - "devise/lib/devise.rb" and "devise/spec/devise_spec.rb".
288
299
 
289
- == Further Reading
300
+ ## Further Reading
290
301
 
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.
302
+ Thor offers many scripting possibilities beyond these examples. Be sure to read
303
+ 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 the options available.
294
304
 
295
- == License
305
+ ## License
296
306
 
297
- See MIT LICENSE.
307
+ Released under the MIT License. See the LICENSE file for further details.
data/Thorfile CHANGED
@@ -10,7 +10,7 @@ rescue LoadError
10
10
  end
11
11
 
12
12
  GEM_NAME = 'thor'
13
- EXTRA_RDOC_FILES = ["README.rdoc", "LICENSE", "CHANGELOG.rdoc", "VERSION", "Thorfile"]
13
+ EXTRA_RDOC_FILES = ["README.md", "LICENSE", "CHANGELOG.rdoc", "Thorfile"]
14
14
 
15
15
  class Default < Thor
16
16
  include Thor::RakeCompat
@@ -44,7 +44,7 @@ class Default < Thor
44
44
  require 'jeweler'
45
45
  Jeweler::Tasks.new do |s|
46
46
  s.name = GEM_NAME
47
- s.version = Thor::VERSION
47
+ s.version = Thor::VERSION.dup
48
48
  s.rubyforge_project = "textmate"
49
49
  s.platform = Gem::Platform::RUBY
50
50
  s.summary = "A scripting framework that replaces rake, sake and rubigen"
@@ -59,6 +59,7 @@ class Default < Thor
59
59
  s.executables = %w( thor rake2thor )
60
60
  s.files = s.extra_rdoc_files + Dir.glob("{bin,lib}/**/*")
61
61
  s.test_files.include 'spec/**/*'
62
+ s.test_files.include 'spec/fixtures/doc/components/.empty_directory'
62
63
  s.test_files.exclude 'spec/sandbox/**/*'
63
64
  end
64
65
 
@@ -20,7 +20,9 @@ class Thor
20
20
  #
21
21
  # create_file "config/apach.conf", "your apache config"
22
22
  #
23
- def create_file(destination, data=nil, config={}, &block)
23
+ def create_file(destination, *args, &block)
24
+ config = args.last.is_a?(Hash) ? args.pop : {}
25
+ data = args.first
24
26
  action CreateFile.new(self, destination, block || data.to_s, config)
25
27
  end
26
28
  alias :add_file :create_file
@@ -21,7 +21,7 @@ class Thor
21
21
  # directory "doc"
22
22
  #
23
23
  # It will create a doc directory in the destination with the following
24
- # files (assuming that the app_name is "blog"):
24
+ # files (assuming that the `app_name` method returns the value "blog"):
25
25
  #
26
26
  # doc/
27
27
  # components/
@@ -40,7 +40,9 @@ class Thor
40
40
  # directory "doc"
41
41
  # directory "doc", "docs", :recursive => false
42
42
  #
43
- def directory(source, destination=nil, config={}, &block)
43
+ def directory(source, *args, &block)
44
+ config = args.last.is_a?(Hash) ? args.pop : {}
45
+ destination = args.first || source
44
46
  action Directory.new(self, source, destination || source, config, &block)
45
47
  end
46
48
 
@@ -18,8 +18,9 @@ class Thor
18
18
  #
19
19
  # copy_file "doc/README"
20
20
  #
21
- def copy_file(source, destination=nil, config={}, &block)
22
- destination ||= source
21
+ def copy_file(source, *args, &block)
22
+ config = args.last.is_a?(Hash) ? args.pop : {}
23
+ destination = args.first || source
23
24
  source = File.expand_path(find_in_source_paths(source.to_s))
24
25
 
25
26
  create_file destination, nil, config do
@@ -46,9 +47,12 @@ class Thor
46
47
  # content.split("\n").first
47
48
  # end
48
49
  #
49
- def get(source, destination=nil, config={}, &block)
50
+ def get(source, *args, &block)
51
+ config = args.last.is_a?(Hash) ? args.pop : {}
52
+ destination = args.first
53
+
50
54
  source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\//
51
- render = open(source).binmode.read
55
+ render = open(source) {|input| input.binmode.read }
52
56
 
53
57
  destination ||= if block_given?
54
58
  block.arity == 1 ? block.call(render) : block.call
@@ -74,13 +78,15 @@ class Thor
74
78
  #
75
79
  # template "doc/README"
76
80
  #
77
- def template(source, destination=nil, config={}, &block)
78
- destination ||= source
81
+ def template(source, *args, &block)
82
+ config = args.last.is_a?(Hash) ? args.pop : {}
83
+ destination = args.first || source
84
+
79
85
  source = File.expand_path(find_in_source_paths(source.to_s))
80
86
  context = instance_eval('binding')
81
87
 
82
88
  create_file destination, nil, config do
83
- content = ERB.new(::File.binread(source), nil, '-').result(context)
89
+ content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
84
90
  content = block.call(content) if block
85
91
  content
86
92
  end
@@ -219,5 +225,22 @@ class Thor
219
225
  end
220
226
  alias :remove_dir :remove_file
221
227
 
228
+ private
229
+ attr_accessor :output_buffer
230
+ def concat(string)
231
+ @output_buffer.concat(string)
232
+ end
233
+
234
+ def capture(*args, &block)
235
+ with_output_buffer { block.call(*args) }
236
+ end
237
+
238
+ def with_output_buffer(buf = '') #:nodoc:
239
+ self.output_buffer, old_buffer = buf, output_buffer
240
+ yield
241
+ output_buffer
242
+ ensure
243
+ self.output_buffer = old_buffer
244
+ end
222
245
  end
223
246
  end
data/lib/thor/actions.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'uri'
2
3
  require 'thor/core_ext/file_binary_read'
3
4
 
4
5
  Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action|
@@ -19,7 +20,13 @@ class Thor
19
20
  # inherited paths and the source root.
20
21
  #
21
22
  def source_paths
22
- @source_paths ||= []
23
+ @_source_paths ||= []
24
+ end
25
+
26
+ # Stores and return the source root for this class
27
+ def source_root(path=nil)
28
+ @_source_root = path if path
29
+ @_source_root
23
30
  end
24
31
 
25
32
  # Returns the source paths in the following order:
@@ -31,7 +38,7 @@ class Thor
31
38
  def source_paths_for_search
32
39
  paths = []
33
40
  paths += self.source_paths
34
- paths << self.source_root if self.respond_to?(:source_root)
41
+ paths << self.source_root if self.source_root
35
42
  paths += from_superclass(:source_paths, [])
36
43
  paths
37
44
  end
@@ -115,7 +122,7 @@ class Thor
115
122
  @source_paths ||= self.class.source_paths_for_search
116
123
  end
117
124
 
118
- # Receives a file or directory and search for it in the source paths.
125
+ # Receives a file or directory and search for it in the source paths.
119
126
  #
120
127
  def find_in_source_paths(file)
121
128
  relative_root = relative_to_original_destination_root(destination_root, false)
@@ -125,12 +132,19 @@ class Thor
125
132
  return source_file if File.exists?(source_file)
126
133
  end
127
134
 
135
+ message = "Could not find #{file.inspect} in any of your source paths. "
136
+
137
+ unless self.class.source_root
138
+ message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. "
139
+ end
140
+
128
141
  if source_paths.empty?
129
- raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " <<
130
- "you can define a source_root in your class."
142
+ message << "Currently you have no source paths."
131
143
  else
132
- raise Error, "Could not find #{file.inspect} in source paths."
144
+ message << "Your current source paths are: \n#{source_paths.join("\n")}"
133
145
  end
146
+
147
+ raise Error, message
134
148
  end
135
149
 
136
150
  # Do something in the root or on a provided subfolder. If a relative path
@@ -176,11 +190,19 @@ class Thor
176
190
  #
177
191
  def apply(path, config={})
178
192
  verbose = config.fetch(:verbose, true)
179
- path = find_in_source_paths(path) unless path =~ /^http\:\/\//
193
+ is_uri = path =~ /^https?\:\/\//
194
+ path = find_in_source_paths(path) unless is_uri
180
195
 
181
196
  say_status :apply, path, verbose
182
197
  shell.padding += 1 if verbose
183
- instance_eval(open(path).read)
198
+
199
+ if is_uri
200
+ contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read }
201
+ else
202
+ contents = open(path) {|io| io.read }
203
+ end
204
+
205
+ instance_eval(contents, path)
184
206
  shell.padding -= 1 if verbose
185
207
  end
186
208
 
@@ -223,7 +245,7 @@ class Thor
223
245
  run command, config.merge(:with => Thor::Util.ruby_command)
224
246
  end
225
247
 
226
- # Run a thor command. A hash of options can be given and it's converted to
248
+ # Run a thor command. A hash of options can be given and it's converted to
227
249
  # switches.
228
250
  #
229
251
  # ==== Parameters
data/lib/thor/base.rb CHANGED
@@ -53,7 +53,7 @@ class Thor
53
53
 
54
54
  opts = Thor::Options.new(parse_options, hash_options)
55
55
  self.options = opts.parse(array_options)
56
- opts.check_unknown! if self.class.check_unknown_options?
56
+ opts.check_unknown! if self.class.check_unknown_options?(config)
57
57
  end
58
58
 
59
59
  class << self
@@ -114,8 +114,12 @@ class Thor
114
114
  @check_unknown_options = true
115
115
  end
116
116
 
117
- def check_unknown_options? #:nodoc:
118
- @check_unknown_options || false
117
+ def check_unknown_options #:nodoc:
118
+ @check_unknown_options ||= from_superclass(:check_unknown_options, false)
119
+ end
120
+
121
+ def check_unknown_options?(config) #:nodoc:
122
+ !!check_unknown_options
119
123
  end
120
124
 
121
125
  # Adds an argument to the class and creates an attr_accessor for it.
@@ -336,6 +340,7 @@ class Thor
336
340
  def no_tasks
337
341
  @no_tasks = true
338
342
  yield
343
+ ensure
339
344
  @no_tasks = false
340
345
  end
341
346
 
@@ -363,32 +368,42 @@ class Thor
363
368
  #
364
369
  def namespace(name=nil)
365
370
  case name
366
- when nil
367
- @namespace ||= Thor::Util.namespace_from_thor_class(self)
368
- else
369
- @namespace = name.to_s
371
+ when nil
372
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
373
+ else
374
+ @namespace = name.to_s
370
375
  end
371
376
  end
372
377
 
373
- # Default way to start generators from the command line.
378
+ # Parses the task and options from the given args, instantiate the class
379
+ # and invoke the task. This method is used when the arguments must be parsed
380
+ # from an array. If you are inside Ruby and want to use a Thor class, you
381
+ # can simply initialize it:
382
+ #
383
+ # script = MyScript.new(args, options, config)
384
+ # script.invoke(:task, first_arg, second_arg, third_arg)
374
385
  #
375
386
  def start(given_args=ARGV, config={})
376
- self.debugging = given_args.include?("--debug")
387
+ self.debugging = given_args.delete("--debug")
377
388
  config[:shell] ||= Thor::Base.shell.new
378
- yield(given_args.dup)
389
+ dispatch(nil, given_args.dup, nil, config)
379
390
  rescue Thor::Error => e
380
391
  debugging ? (raise e) : config[:shell].error(e.message)
381
392
  exit(1) if exit_on_failure?
382
393
  end
383
394
 
384
395
  def handle_no_task_error(task) #:nodoc:
385
- if self.banner_base == "thor"
396
+ if $thor_runner
386
397
  raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
387
398
  else
388
399
  raise UndefinedTaskError, "Could not find task #{task.inspect}."
389
400
  end
390
401
  end
391
402
 
403
+ def handle_argument_error(task, error) #:nodoc:
404
+ raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{self.banner(task).inspect}."
405
+ end
406
+
392
407
  protected
393
408
 
394
409
  # Prints the class options per group. If an option does not belong to
@@ -445,7 +460,7 @@ class Thor
445
460
  def build_option(name, options, scope) #:nodoc:
446
461
  scope[name] = Thor::Option.new(name, options[:desc], options[:required],
447
462
  options[:type], options[:default], options[:banner],
448
- options[:group], options[:aliases])
463
+ options[:lazy_default], options[:group], options[:aliases])
449
464
  end
450
465
 
451
466
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -516,9 +531,11 @@ class Thor
516
531
  false
517
532
  end
518
533
 
519
- # Returns the base for banner.
520
- def banner_base
521
- @banner_base ||= $thor_runner ? "thor" : File.basename($0.split(" ").first)
534
+ #
535
+ # The basename of the program invoking the thor class.
536
+ #
537
+ def basename
538
+ File.basename($0).split(' ').first
522
539
  end
523
540
 
524
541
  # SIGNATURE: Sets the baseclass. This is where the superclass lookup
@@ -535,6 +552,12 @@ class Thor
535
552
  # class.
536
553
  def initialize_added #:nodoc:
537
554
  end
555
+
556
+ # SIGNATURE: The hook invoked by start.
557
+ def dispatch(task, given_args, given_opts, config) #:nodoc:
558
+ raise NotImplementedError
559
+ end
560
+
538
561
  end
539
562
  end
540
563
  end
@@ -65,7 +65,7 @@ class Thor
65
65
  else
66
66
  self[$1] == args.first
67
67
  end
68
- else
68
+ else
69
69
  self[method]
70
70
  end
71
71
  end