thor 0.12.0 → 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.
Files changed (43) hide show
  1. data/CHANGELOG.rdoc +5 -5
  2. data/README.rdoc +65 -2
  3. data/Thorfile +15 -9
  4. data/bin/thor +1 -0
  5. data/lib/thor/actions/create_file.rb +2 -2
  6. data/lib/thor/actions/directory.rb +2 -4
  7. data/lib/thor/actions/file_manipulation.rb +10 -6
  8. data/lib/thor/actions/inject_into_file.rb +10 -7
  9. data/lib/thor/actions.rb +6 -5
  10. data/lib/thor/base.rb +45 -32
  11. data/lib/thor/core_ext/file_binary_read.rb +9 -0
  12. data/lib/thor/group.rb +46 -37
  13. data/lib/thor/runner.rb +49 -42
  14. data/lib/thor/shell/basic.rb +49 -29
  15. data/lib/thor/shell/color.rb +1 -1
  16. data/lib/thor/shell.rb +1 -1
  17. data/lib/thor/task.rb +27 -38
  18. data/lib/thor/util.rb +4 -22
  19. data/lib/thor/version.rb +1 -1
  20. data/lib/thor.rb +43 -45
  21. data/spec/actions/create_file_spec.rb +7 -7
  22. data/spec/actions/directory_spec.rb +5 -4
  23. data/spec/actions/file_manipulation_spec.rb +29 -16
  24. data/spec/actions/inject_into_file_spec.rb +29 -0
  25. data/spec/actions_spec.rb +14 -13
  26. data/spec/base_spec.rb +16 -1
  27. data/spec/fixtures/bundle/main.thor +1 -0
  28. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  29. data/spec/fixtures/doc/README +3 -0
  30. data/spec/fixtures/group.thor +83 -0
  31. data/spec/fixtures/invoke.thor +112 -0
  32. data/spec/fixtures/script.thor +134 -0
  33. data/spec/fixtures/task.thor +10 -0
  34. data/spec/group_spec.rb +1 -7
  35. data/spec/runner_spec.rb +35 -39
  36. data/spec/shell/basic_spec.rb +56 -62
  37. data/spec/shell/color_spec.rb +6 -6
  38. data/spec/spec.opts +1 -0
  39. data/spec/spec_helper.rb +5 -4
  40. data/spec/task_spec.rb +14 -32
  41. data/spec/thor_spec.rb +21 -22
  42. data/spec/util_spec.rb +7 -31
  43. metadata +28 -19
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
- return false if no?("Do you wish to continue [y/N]?")
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,11 +143,15 @@ 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
151
150
 
151
+ def self.banner(task)
152
+ "thor " + task.formatted_usage(self, false)
153
+ end
154
+
152
155
  def thor_root
153
156
  Thor::Util.thor_root
154
157
  end
@@ -156,7 +159,7 @@ class Thor::Runner < Thor #:nodoc:
156
159
  def thor_yaml
157
160
  @thor_yaml ||= begin
158
161
  yaml_file = File.join(thor_root, "thor.yml")
159
- yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
162
+ yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
160
163
  yaml || {}
161
164
  end
162
165
  end
@@ -215,9 +218,6 @@ class Thor::Runner < Thor #:nodoc:
215
218
  # 5. c:\ <-- no Thorfiles found!
216
219
  #
217
220
  def thorfiles(relevant_to=nil, skip_lookup=false)
218
- # TODO Remove this dealing with deprecated thor when :namespaces: is available as constants
219
- save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml)
220
-
221
221
  thorfiles = []
222
222
 
223
223
  unless skip_lookup
@@ -253,47 +253,54 @@ class Thor::Runner < Thor #:nodoc:
253
253
  # Display information about the given klasses. If with_module is given,
254
254
  # it shows a table with information extracted from the yaml file.
255
255
  #
256
- def display_klasses(with_modules=false, klasses=Thor.subclasses)
257
- klasses -= [Thor, Thor::Runner] unless with_modules
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
+
258
259
  raise Error, "No Thor tasks available" if klasses.empty?
260
+ show_modules if with_modules && !thor_yaml.empty?
259
261
 
260
- if with_modules && !thor_yaml.empty?
261
- info = []
262
- labels = ["Modules", "Namespaces"]
262
+ # Remove subclasses
263
+ klasses.dup.each do |klass|
264
+ klasses -= Thor::Util.thor_classes_in(klass)
265
+ end
263
266
 
264
- info << labels
265
- info << [ "-" * labels[0].size, "-" * labels[1].size ]
267
+ list = Hash.new { |h,k| h[k] = [] }
268
+ groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
266
269
 
267
- thor_yaml.each do |name, hash|
268
- info << [ name, hash[:namespaces].join(", ") ]
269
- end
270
+ # Get classes which inherit from Thor
271
+ (klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) }
270
272
 
271
- print_table info
272
- say ""
273
- end
273
+ # Get classes which inherit from Thor::Base
274
+ groups.map! { |k| k.printable_tasks(false).first }
275
+ list["root"] = groups
274
276
 
275
- unless klasses.empty?
276
- klasses.dup.each do |klass|
277
- klasses -= Thor::Util.thor_classes_in(klass)
278
- end
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
279
281
 
280
- klasses.each { |k| display_tasks(k) }
281
- else
282
- say "\033[1;34mNo Thor tasks available\033[0m"
283
- end
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
284
290
  end
285
291
 
286
- # Display tasks from the given Thor class.
287
- #
288
- def display_tasks(klass)
289
- unless klass.tasks.empty?
290
- base = klass.namespace
292
+ def show_modules #:nodoc:
293
+ info = []
294
+ labels = ["Modules", "Namespaces"]
291
295
 
292
- color = base == "default" ? :magenta : :blue
293
- say shell.set_color(base, color, true)
294
- say "-" * base.length
296
+ info << labels
297
+ info << [ "-" * labels[0].size, "-" * labels[1].size ]
295
298
 
296
- klass.help(shell, :short => true, :ident => 0, :namespace => true)
299
+ thor_yaml.each do |name, hash|
300
+ info << [ name, hash[:namespaces].join(", ") ]
297
301
  end
302
+
303
+ print_table info
304
+ say ""
298
305
  end
299
306
  end
@@ -75,30 +75,6 @@ class Thor
75
75
  !yes?(statement, color)
76
76
  end
77
77
 
78
- # Prints a list of items.
79
- #
80
- # ==== Parameters
81
- # list<Array[String, String, ...]>
82
- #
83
- # ==== Options
84
- # mode:: Can be :rows or :inline. Defaults to :rows.
85
- # ident:: Ident each item with the value given.
86
- #
87
- def print_list(list, options={})
88
- return if list.empty?
89
-
90
- ident = " " * (options[:ident] || 0)
91
- content = case options[:mode]
92
- when :inline
93
- last = list.pop
94
- "#{list.join(", ")}, and #{last}"
95
- else # rows
96
- ident + list.join("\n#{ident}")
97
- end
98
-
99
- $stdout.puts content
100
- end
101
-
102
78
  # Prints a table.
103
79
  #
104
80
  # ==== Parameters
@@ -110,20 +86,26 @@ class Thor
110
86
  def print_table(table, options={})
111
87
  return if table.empty?
112
88
 
113
- formats = []
89
+ formats, ident = [], options[:ident].to_i
90
+ options[:truncate] = terminal_width if options[:truncate] == true
91
+
114
92
  0.upto(table.first.length - 2) do |i|
115
93
  maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size
116
94
  formats << "%-#{maxima + 2}s"
117
95
  end
118
96
 
119
- formats[0] = formats[0].insert(0, " " * options[:ident]) if options[:ident]
97
+ formats[0] = formats[0].insert(0, " " * ident)
120
98
  formats << "%s"
121
99
 
122
100
  table.each do |row|
101
+ sentence = ""
102
+
123
103
  row.each_with_index do |column, i|
124
- $stdout.print formats[i] % column.to_s
104
+ sentence << formats[i] % column.to_s
125
105
  end
126
- $stdout.puts
106
+
107
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
108
+ $stdout.puts sentence
127
109
  end
128
110
  end
129
111
 
@@ -143,7 +125,7 @@ class Thor
143
125
  answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
144
126
 
145
127
  case answer
146
- when is?(:yes), is?(:force)
128
+ when is?(:yes), is?(:force), ""
147
129
  return true
148
130
  when is?(:no), is?(:skip)
149
131
  return false
@@ -214,6 +196,44 @@ HELP
214
196
  base && base.options[:quiet]
215
197
  end
216
198
 
199
+ # This code was copied from Rake, available under MIT-LICENSE
200
+ # Copyright (c) 2003, 2004 Jim Weirich
201
+ def terminal_width
202
+ if ENV['THOR_COLUMNS']
203
+ result = ENV['THOR_COLUMNS'].to_i
204
+ else
205
+ result = unix? ? dynamic_width : 80
206
+ end
207
+ (result < 10) ? 80 : result
208
+ rescue
209
+ 80
210
+ end
211
+
212
+ # Calculate the dynamic width of the terminal
213
+ def dynamic_width
214
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
215
+ end
216
+
217
+ def dynamic_width_stty
218
+ %x{stty size 2>/dev/null}.split[1].to_i
219
+ end
220
+
221
+ def dynamic_width_tput
222
+ %x{tput cols 2>/dev/null}.to_i
223
+ end
224
+
225
+ def unix?
226
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
227
+ end
228
+
229
+ def truncate(string, width)
230
+ if string.length <= width
231
+ string
232
+ else
233
+ ( string[0, width-3] || "" ) + "..."
234
+ end
235
+ end
236
+
217
237
  end
218
238
  end
219
239
  end
@@ -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.read(destination).to_s.split("\n")
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/shell.rb CHANGED
@@ -22,7 +22,7 @@ class Thor
22
22
  end
23
23
 
24
24
  module Shell
25
- SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table]
25
+ SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_table]
26
26
 
27
27
  # Add shell to initialize config values.
28
28
  #
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=[])
@@ -25,72 +25,61 @@ class Thor
25
25
  self.options = other.options.dup if other.options
26
26
  end
27
27
 
28
- def short_description
29
- description.split("\n").first if description
30
- end
31
-
32
28
  # By default, a task invokes a method in the thor class. You can change this
33
29
  # implementation to create custom tasks.
34
- #
35
30
  def run(instance, args=[])
36
31
  raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
37
32
  instance.send(name, *args)
38
33
  rescue ArgumentError => e
34
+ raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
39
35
  parse_argument_error(instance, e, caller)
40
36
  rescue NoMethodError => e
37
+ raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
41
38
  parse_no_method_error(instance, e)
42
39
  end
43
40
 
44
- # Returns the formatted usage. If a class is given, the class arguments are
45
- # injected in the usage.
46
- #
47
- def formatted_usage(klass=nil, namespace=false, show_options=true)
48
- formatted = if namespace.is_a?(String)
49
- "#{namespace}:"
50
- elsif klass && namespace
51
- "#{klass.namespace.gsub(/^default/,'')}:"
41
+ # Returns the formatted usage by injecting given required arguments
42
+ # and required options into the given usage.
43
+ def formatted_usage(klass, namespace=true)
44
+ namespace = klass.namespace unless namespace == false
45
+
46
+ # Add namespace
47
+ formatted = if namespace
48
+ "#{namespace.gsub(/^(default|thor:runner:)/,'')}:"
52
49
  else
53
50
  ""
54
51
  end
55
52
 
56
- formatted << formatted_arguments(klass)
57
- formatted << " #{formatted_options}" if show_options
58
- formatted.strip!
59
- formatted
60
- end
61
-
62
- # Injects the class arguments into the task usage.
63
- #
64
- def formatted_arguments(klass)
65
- if klass && !klass.arguments.empty?
53
+ # Add usage with required arguments
54
+ formatted << if klass && !klass.arguments.empty?
66
55
  usage.to_s.gsub(/^#{name}/) do |match|
67
- match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
56
+ match << " " << klass.arguments.map{ |a| a.usage }.compact.join(' ')
68
57
  end
69
58
  else
70
59
  usage.to_s
71
60
  end
72
- end
73
61
 
74
- # Returns the options usage for this task.
75
- #
76
- def formatted_options
77
- @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ")
62
+ # Add required options
63
+ formatted << " #{required_options}"
64
+
65
+ # Strip and go!
66
+ formatted.strip
78
67
  end
79
68
 
80
69
  protected
81
70
 
71
+ def required_options
72
+ @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
73
+ end
74
+
82
75
  # Given a target, checks if this class name is not a private/protected method.
83
- #
84
76
  def public_method?(instance) #:nodoc:
85
77
  collection = instance.private_methods + instance.protected_methods
86
78
  (collection & [name.to_s, name.to_sym]).empty?
87
79
  end
88
80
 
89
- # Clean everything that comes from the Thor gempath and remove the caller.
90
- #
91
81
  def sans_backtrace(backtrace, caller) #:nodoc:
92
- dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/
93
- saned = backtrace.reject { |frame| frame =~ dirname }
82
+ saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
94
83
  saned -= caller
95
84
  end
96
85
 
@@ -102,7 +91,7 @@ class Thor
102
91
  raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
103
92
  else
104
93
  raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
105
- "'#{formatted_usage(instance.class, true)}'"
94
+ "'#{formatted_usage(instance.class)}'"
106
95
  end
107
96
  else
108
97
  raise e
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
- klass.constants.include?(subclass.name.gsub("#{klass.name}::", ''))
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.read(path)
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"]
data/lib/thor/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.12.0".freeze
2
+ VERSION = "0.13.0".freeze
3
3
  end
data/lib/thor.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'thor/base'
2
- require 'thor/group'
3
- require 'thor/actions'
4
2
 
3
+ # TODO: Update thor to allow for git-style CLI (git bisect run)
5
4
  class Thor
6
5
  class << self
7
6
  # Sets the default task when thor is executed without an explicit task to be called.
@@ -78,14 +77,14 @@ class Thor
78
77
  @method_options
79
78
  end
80
79
 
81
- # Adds an option to the set of class options. If :for is given as option,
80
+ # Adds an option to the set of method options. If :for is given as option,
82
81
  # it allows you to change the options from a previous defined task.
83
82
  #
84
83
  # def previous_task
85
84
  # # magic
86
85
  # end
87
86
  #
88
- # method_options :foo => :bar, :for => :previous_task
87
+ # method_option :foo => :bar, :for => :previous_task
89
88
  #
90
89
  # def next_task
91
90
  # # magic
@@ -101,7 +100,6 @@ class Thor
101
100
  # :default - Default value for this argument. It cannot be required and have default values.
102
101
  # :aliases - Aliases for this option.
103
102
  # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
104
- # :group - The group for this options. Use by class options to output options in different levels.
105
103
  # :banner - String to show on usage notes.
106
104
  #
107
105
  def method_option(name, options={})
@@ -140,49 +138,48 @@ class Thor
140
138
  end
141
139
  end
142
140
 
143
- # Prints help information. If a task name is given, it shows information
144
- # only about the specific task.
141
+ # Prints help information for the given task.
145
142
  #
146
143
  # ==== Parameters
147
- # meth<String>:: An optional task name to print usage information about.
144
+ # shell<Thor::Shell>
145
+ # task_name<String>
146
+ #
147
+ def task_help(shell, task_name)
148
+ task = all_tasks[task_name]
149
+ raise UndefinedTaskError, "task '#{task_name}' could not be found in namespace '#{self.namespace}'" unless task
150
+
151
+ shell.say "Usage:"
152
+ shell.say " #{banner(task)}"
153
+ shell.say
154
+ class_options_help(shell, nil => task.options.map { |_, o| o })
155
+ shell.say task.description
156
+ end
157
+
158
+ # Prints help information for this class.
148
159
  #
149
- # ==== Options
150
- # namespace:: When true, shows the namespace in the output before the usage.
151
- # skip_inherited:: When true, does not show tasks from superclass.
160
+ # ==== Parameters
161
+ # shell<Thor::Shell>
152
162
  #
153
- def help(shell, meth=nil, options={})
154
- meth, options = nil, meth if meth.is_a?(Hash)
155
-
156
- if meth
157
- task = all_tasks[meth]
158
- raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task
159
-
160
- shell.say "Usage:"
161
- shell.say " #{banner(task, options[:namespace], false)}"
162
- shell.say
163
- class_options_help(shell, "Class", :Method => task.options.map { |_, o| o })
164
- shell.say task.description
165
- else
166
- list = (options[:short] ? tasks : all_tasks).map do |_, task|
167
- item = [ banner(task, options[:namespace]) ]
168
- item << "# #{task.short_description}" if task.short_description
169
- item << " "
170
- end
171
-
172
- options[:ident] ||= 2
173
- if options[:short]
174
- shell.print_list(list, :ident => options[:ident])
175
- else
176
- shell.say "Tasks:"
177
- shell.print_list(list, :ident => options[:ident])
178
- end
163
+ def help(shell)
164
+ list = printable_tasks
165
+ Thor::Util.thor_classes_in(self).each do |klass|
166
+ list += klass.printable_tasks(false)
167
+ end
168
+ list.sort!{ |a,b| a[0] <=> b[0] }
179
169
 
180
- Thor::Util.thor_classes_in(self).each do |subclass|
181
- namespace = options[:namespace] == true || subclass.namespace.gsub(/^#{self.namespace}:/, '')
182
- subclass.help(shell, options.merge(:short => true, :namespace => namespace))
183
- end
170
+ shell.say "Tasks:"
171
+ shell.print_table(list, :ident => 2, :truncate => true)
172
+ shell.say
173
+ class_options_help(shell)
174
+ end
184
175
 
185
- class_options_help(shell, "Class") unless options[:short]
176
+ # Returns tasks ready to be printed.
177
+ def printable_tasks(all=true)
178
+ (all ? all_tasks : tasks).map do |_, task|
179
+ item = []
180
+ item << banner(task)
181
+ item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
182
+ item
186
183
  end
187
184
  end
188
185
 
@@ -193,8 +190,9 @@ class Thor
193
190
  # the task that is going to be invoked and a boolean which indicates if
194
191
  # the namespace should be displayed as arguments.
195
192
  #
196
- def banner(task, namespace=true, show_options=true)
197
- task.formatted_usage(self, namespace, show_options)
193
+ def banner(task)
194
+ base = $thor_runner ? "thor" : File.basename($0.split(" ").first)
195
+ "#{base} #{task.formatted_usage(self, base == "thor")}"
198
196
  end
199
197
 
200
198
  def baseclass #:nodoc:
@@ -237,6 +235,6 @@ class Thor
237
235
 
238
236
  desc "help [TASK]", "Describe available tasks or one specific task"
239
237
  def help(task=nil)
240
- self.class.help(shell, task, :namespace => task && task.include?(?:))
238
+ task ? self.class.task_help(shell, task) : self.class.help(shell)
241
239
  end
242
240
  end
@@ -8,7 +8,7 @@ describe Thor::Actions::CreateFile do
8
8
 
9
9
  def create_file(destination=nil, config={}, options={})
10
10
  @base = MyCounter.new([1,2], options, { :destination_root => destination_root })
11
- stub(@base).file_name { 'rdoc' }
11
+ @base.stub!(:file_name).and_return('rdoc')
12
12
 
13
13
  @action = Thor::Actions::CreateFile.new(@base, destination, "CONFIGURATION",
14
14
  { :verbose => !@silence }.merge(config))
@@ -103,7 +103,7 @@ describe Thor::Actions::CreateFile do
103
103
 
104
104
  it "shows conflict status to ther user" do
105
105
  create_file("doc/config.rb").must_not be_identical
106
- mock($stdin).gets{ 's' }
106
+ $stdin.should_receive(:gets).and_return('s')
107
107
  file = File.join(destination_root, 'doc/config.rb')
108
108
 
109
109
  content = invoke!
@@ -114,21 +114,21 @@ describe Thor::Actions::CreateFile do
114
114
 
115
115
  it "creates the file if the file collision menu returns true" do
116
116
  create_file("doc/config.rb")
117
- mock($stdin).gets{ 'y' }
117
+ $stdin.should_receive(:gets).and_return('y')
118
118
  invoke!.must =~ /force doc\/config\.rb/
119
119
  end
120
120
 
121
121
  it "skips the file if the file collision menu returns false" do
122
122
  create_file("doc/config.rb")
123
- mock($stdin).gets{ 'n' }
123
+ $stdin.should_receive(:gets).and_return('n')
124
124
  invoke!.must =~ /skip doc\/config\.rb/
125
125
  end
126
126
 
127
127
  it "executes the block given to show file content" do
128
128
  create_file("doc/config.rb")
129
- mock($stdin).gets{ 'd' }
130
- mock($stdin).gets{ 'n' }
131
- mock(@base.shell).system(/diff -u/)
129
+ $stdin.should_receive(:gets).and_return('d')
130
+ $stdin.should_receive(:gets).and_return('n')
131
+ @base.shell.should_receive(:system).with(/diff -u/)
132
132
  invoke!
133
133
  end
134
134
  end
@@ -4,7 +4,7 @@ require 'thor/actions'
4
4
  describe Thor::Actions::Directory do
5
5
  before(:each) do
6
6
  ::FileUtils.rm_rf(destination_root)
7
- stub(invoker).file_name{ "rdoc" }
7
+ invoker.stub!(:file_name).and_return("rdoc")
8
8
  end
9
9
 
10
10
  def invoker
@@ -110,10 +110,11 @@ describe Thor::Actions::Directory do
110
110
  end
111
111
 
112
112
  it "yields a block" do
113
- invoke!("doc") do |f|
114
- %(doc/README doc/config.rb doc/rdoc.rb).must include(f)
115
- %(doc/components/).must_not include(f)
113
+ checked = false
114
+ invoke!("doc") do |content|
115
+ checked ||= !!(content =~ /FOO/)
116
116
  end
117
+ checked.must be_true
117
118
  end
118
119
  end
119
120