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 CHANGED
@@ -1,9 +1,9 @@
1
- == TODO
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
- require 'rdoc/task'
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::Task.new do |rdoc|
30
- rdoc.main = "README.rdoc"
31
- rdoc.rdoc_dir = "rdoc"
32
- rdoc.title = GEM_NAME
33
- rdoc.rdoc_files.include(*EXTRA_RDOC_FILES)
34
- rdoc.rdoc_files.include('lib/**/*.rb')
35
- rdoc.options << '--line-numbers' << '--inline-source'
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
@@ -4,4 +4,5 @@
4
4
  require 'thor'
5
5
  require 'thor/runner'
6
6
 
7
+ $thor_runner = true
7
8
  Thor::Runner.start
@@ -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.read(destination) == render
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, 'w'){ |f| f.write render }
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.read(source)
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 = open(source).read
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.read(source), nil, '-').result(context)
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.read(path)
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.read(destination)
94
- content.gsub!(regexp, string)
95
- File.open(destination, 'wb') { |file| file.write(content) }
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
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'thor/core_ext/file_binary_read'
2
3
 
3
4
  Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action|
4
5
  require action
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
@@ -0,0 +1,9 @@
1
+ class File #:nodoc:
2
+
3
+ unless File.respond_to?(:binread)
4
+ def self.binread(file)
5
+ File.open(file, 'rb') { |f| f.read }
6
+ end
7
+ end
8
+
9
+ end
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, options={})
45
- if options[:short]
46
- shell.say banner
47
- else
48
- shell.say "Usage:"
49
- shell.say " #{banner}\n"
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
- "thor #{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}"
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
- # Shortcut to invoke with padding and block handling. Use internally by
241
- # invoke and invoke_from_option class methods.
242
- #
243
- def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc:
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
- result = if block_given?
247
- if block.arity == 2
248
- block.call(self, klass)
249
- else
250
- block.call(self, klass, task)
251
- end
252
- else
253
- invoke klass, task, *args
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
- shell.padding -= 1
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
- 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,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 = YAML.load_file(yaml_file) if File.exists?(yaml_file)
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 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
+
262
259
  raise Error, "No Thor tasks available" if klasses.empty?
260
+ show_modules if with_modules && !thor_yaml.empty?
263
261
 
264
- if with_modules && !thor_yaml.empty?
265
- info = []
266
- labels = ["Modules", "Namespaces"]
262
+ # Remove subclasses
263
+ klasses.dup.each do |klass|
264
+ klasses -= Thor::Util.thor_classes_in(klass)
265
+ end
267
266
 
268
- info << labels
269
- 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) }
270
269
 
271
- thor_yaml.each do |name, hash|
272
- info << [ name, hash[:namespaces].join(", ") ]
273
- end
270
+ # Get classes which inherit from Thor
271
+ (klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) }
274
272
 
275
- print_table info
276
- say ""
277
- end
273
+ # Get classes which inherit from Thor::Base
274
+ groups.map! { |k| k.printable_tasks(false).first }
275
+ list["root"] = groups
278
276
 
279
- unless klasses.empty?
280
- klasses.dup.each do |klass|
281
- klasses -= Thor::Util.thor_classes_in(klass)
282
- 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
283
281
 
284
- klasses.each { |k| display_tasks(k) }
285
- else
286
- say "\033[1;34mNo Thor tasks available\033[0m"
287
- 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
288
290
  end
289
291
 
290
- # Display tasks from the given Thor class.
291
- #
292
- def display_tasks(klass)
293
- unless klass.tasks.empty?
294
- base = klass.namespace
292
+ def show_modules #:nodoc:
293
+ info = []
294
+ labels = ["Modules", "Namespaces"]
295
295
 
296
- say shell.set_color(base, :blue, true)
297
- say "-" * base.length
296
+ info << labels
297
+ info << [ "-" * labels[0].size, "-" * labels[1].size ]
298
298
 
299
- klass.help(shell, :short => true)
300
- say
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
@@ -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/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=nil)
45
- namespace = klass.namespace if namespace.nil?
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 << " " << required_arguments(klass)
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
- method_name = /`#{Regexp.escape(name.split(':').last)}'/
87
+ backtrace = sans_backtrace(e.backtrace, caller)
89
88
 
90
- if e.message =~ /wrong number of arguments/ && e.backtrace.first.to_s =~ method_name
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
- 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"]