Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Unify error messages.

  • Loading branch information...
commit ac419faf0132bc91fdeba1eaa5cac7835dba4ef2 1 parent 0dddc4a
@josevalim josevalim authored
View
6 lib/thor.rb
@@ -147,7 +147,7 @@ def start(given_args=ARGV, config={})
def task_help(shell, task_name)
meth = normalize_task_name(task_name)
task = all_tasks[meth]
- raise UndefinedTaskError, "task '#{task_name}' could not be found in namespace '#{self.namespace}'" unless task
+ handle_no_task_error(meth) unless task
shell.say "Usage:"
shell.say " #{banner(task)}"
@@ -184,6 +184,10 @@ def printable_tasks(all=true)
end
end
+ def handle_argument_error(task, error) #:nodoc:
+ raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{task.formatted_usage(self, banner_base == "thor").inspect}."
+ end
+
protected
# The banner for this class. You can customize it if you are invoking the
View
14 lib/thor/base.rb
@@ -355,7 +355,7 @@ def no_tasks
def namespace(name=nil)
case name
when nil
- @namespace ||= Thor::Util.namespace_from_thor_class(self, false)
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
else
@namespace = name.to_s
end
@@ -368,12 +368,16 @@ def start(given_args=ARGV, config={})
config[:shell] ||= Thor::Base.shell.new
yield
rescue Thor::Error => e
- if debugging
- raise e
+ debugging ? (raise e) : config[:shell].error(e.message)
+ exit(1) if exit_on_failure?
+ end
+
+ def handle_no_task_error(task) #:nodoc:
+ if self.banner_base == "thor"
+ raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
else
- config[:shell].error e.message
+ raise UndefinedTaskError, "Could not find task #{task.inspect}."
end
- exit(1) if exit_on_failure?
end
protected
View
4 lib/thor/group.rb
@@ -219,6 +219,10 @@ def printable_tasks(*)
[item]
end
+ def handle_argument_error(task, error) #:nodoc:
+ raise error, "#{task.name.inspect} was called incorrectly. Are you sure it has arity equals to 0?"
+ end
+
protected
# The banner for this class. You can customize it if you are invoking the
View
2  lib/thor/invocation.rb
@@ -11,7 +11,7 @@ module ClassMethods
def prepare_for_invocation(key, name) #:nodoc:
case name
when Symbol, String
- Thor::Util.namespace_to_thor_class_and_task(name.to_s, false)
+ Thor::Util.find_class_and_task_by_namespace(name.to_s)
else
name
end
View
7 lib/thor/runner.rb
@@ -16,8 +16,7 @@ class Thor::Runner < Thor #:nodoc:
def help(meth=nil)
if meth && !self.respond_to?(meth)
initialize_thorfiles(meth)
- klass, task = Thor::Util.namespace_to_thor_class_and_task(meth)
- # Send mapping -h because it works with Thor::Group too
+ klass, task = Thor::Util.find_class_and_task_by_namespace!(meth)
klass.start(["-h", task].compact, :shell => self.shell)
else
super
@@ -30,9 +29,9 @@ def help(meth=nil)
def method_missing(meth, *args)
meth = meth.to_s
initialize_thorfiles(meth)
- klass, task = Thor::Util.namespace_to_thor_class_and_task(meth)
+ klass, task = Thor::Util.find_class_and_task_by_namespace!(meth)
args.unshift(task) if task
- klass.start(args, :shell => shell)
+ klass.start(args, :shell => self.shell)
end
desc "install NAME", "Install an optionally named Thor file into your system tasks"
View
49 lib/thor/task.rb
@@ -9,10 +9,11 @@ def initialize(name, options=nil)
end
def run(instance, args=[])
- unless (instance.methods & [name.to_s, name.to_sym]).empty?
- raise Error, "could not find Thor class or task '#{name}'"
+ if (instance.methods & [name.to_s, name.to_sym]).empty?
+ super
+ else
+ instance.class.handle_no_task_error(name)
end
- super
end
end
@@ -28,14 +29,14 @@ def initialize_copy(other) #:nodoc:
# By default, a task invokes a method in the thor class. You can change this
# implementation to create custom tasks.
def run(instance, args=[])
- raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
- instance.send(name, *args)
+ public_method?(instance) ?
+ instance.send(name, *args) : instance.class.handle_no_task_error(name)
rescue ArgumentError => e
- raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
- parse_argument_error(instance, e, caller)
+ handle_argument_error?(instance, e, caller) ?
+ instance.class.handle_argument_error(self, e) : (raise e)
rescue NoMethodError => e
- raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
- parse_no_method_error(instance, e)
+ handle_no_method_error?(instance, e, caller) ?
+ instance.class.handle_no_task_error(name) : (raise e)
end
# Returns the formatted usage by injecting given required arguments
@@ -68,6 +69,10 @@ def formatted_usage(klass, namespace=true)
protected
+ def not_debugging?(instance)
+ !(instance.class.respond_to?(:debugging) && instance.class.debugging)
+ end
+
def required_options
@required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
end
@@ -83,28 +88,14 @@ def sans_backtrace(backtrace, caller) #:nodoc:
saned -= caller
end
- def parse_argument_error(instance, e, caller) #:nodoc:
- backtrace = sans_backtrace(e.backtrace, caller)
-
- if backtrace.empty? && e.message =~ /wrong number of arguments/
- if defined?(Thor::Group) && instance.is_a?(Thor::Group)
- raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
- else
- raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
- "'#{formatted_usage(instance.class)}'"
- end
- else
- raise e
- end
+ def handle_argument_error?(instance, error, caller)
+ not_debugging?(instance) && error.message =~ /wrong number of arguments/ &&
+ sans_backtrace(error.backtrace, caller).empty?
end
- def parse_no_method_error(instance, e) #:nodoc:
- if e.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
- raise UndefinedTaskError, "The #{instance.class.namespace} namespace " <<
- "doesn't have a '#{name}' task"
- else
- raise e
- end
+ def handle_no_method_error?(instance, error, caller)
+ not_debugging?(instance) &&
+ error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
end
end
View
26 lib/thor/util.rb
@@ -23,10 +23,7 @@ module Util
#
def self.find_by_namespace(namespace)
namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/
-
- Thor::Base.subclasses.find do |klass|
- klass.namespace == namespace
- end
+ Thor::Base.subclasses.find { |klass| klass.namespace == namespace }
end
# Receives a constant and converts it to a Thor namespace. Since Thor tasks
@@ -43,10 +40,10 @@ def self.find_by_namespace(namespace)
# ==== Returns
# String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
#
- def self.namespace_from_thor_class(constant, remove_default=true)
+ def self.namespace_from_thor_class(constant)
constant = constant.to_s.gsub(/^Thor::Sandbox::/, "")
constant = snake_case(constant).squeeze(":")
- constant.gsub!(/^default/, '') if remove_default
+ constant.gsub!(/^default/, '')
constant
end
@@ -132,13 +129,7 @@ def self.camel_case(str)
# ==== Parameters
# namespace<String>
#
- # ==== Errors
- # Thor::Error:: raised if the namespace cannot be found.
- #
- # Thor::Error:: raised if the namespace evals to a class which does not
- # inherit from Thor or Thor::Group.
- #
- def self.namespace_to_thor_class_and_task(namespace, raise_if_nil=true)
+ def self.find_class_and_task_by_namespace(namespace)
if namespace.include?(?:)
pieces = namespace.split(":")
task = pieces.pop
@@ -149,7 +140,14 @@ def self.namespace_to_thor_class_and_task(namespace, raise_if_nil=true)
klass, task = Thor::Util.find_by_namespace(namespace), nil
end
- raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil?
+ return klass, task
+ end
+
+ # The same as namespace_to_thor_class_and_task!, but raises an error if a klass
+ # could not be found.
+ def self.find_class_and_task_by_namespace!(namespace)
+ klass, task = find_class_and_task_by_namespace(namespace)
+ raise Error, "Could not find namespace or task #{namespace.inspect}." unless klass
return klass, task
end
View
10 spec/base_spec.rb
@@ -230,22 +230,22 @@ def hello
it "raises an error instead of rescueing if --debug is given" do
lambda {
MyScript.start ["what", "--debug"]
- }.must raise_error(Thor::UndefinedTaskError, /the 'what' task of MyScript is private/)
+ }.must raise_error(Thor::UndefinedTaskError, 'Could not find task "what" in "my_script" namespace.')
end
end
describe "attr_*" do
it "should not add attr_reader as a task" do
- capture(:stderr){ MyScript.start(["another_attribute"]) }.must =~ /could not find/
+ capture(:stderr){ MyScript.start(["another_attribute"]) }.must =~ /Could not find/
end
it "should not add attr_writer as a task" do
- capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }.must =~ /could not find/
+ capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }.must =~ /Could not find/
end
it "should not add attr_accessor as a task" do
- capture(:stderr){ MyScript.start(["some_attribute"]) }.must =~ /could not find/
- capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }.must =~ /could not find/
+ capture(:stderr){ MyScript.start(["some_attribute"]) }.must =~ /Could not find/
+ capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }.must =~ /Could not find/
end
end
end
View
8 spec/runner_spec.rb
@@ -32,7 +32,7 @@
it "raises error if a class/task cannot be found" do
Thor::Runner.should_receive(:exit).with(1)
content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
- content.must =~ /could not find Thor class or task 'unknown'/
+ content.strip.must == 'Could not find namespace or task "unknown".'
end
end
@@ -70,7 +70,8 @@
it "raises an error if class/task can't be found" do
Thor::Runner.should_receive(:exit).with(1)
ARGV.replace ["unknown"]
- capture(:stderr){ Thor::Runner.start }.must =~ /could not find Thor class or task 'unknown'/
+ content = capture(:stderr){ Thor::Runner.start }
+ content.strip.must == 'Could not find namespace or task "unknown".'
end
it "does not swallow NoMethodErrors that occur inside the called method" do
@@ -85,7 +86,8 @@
it "does not swallow Thor InvocationError" do
ARGV.replace ["my_script:animal"]
- capture(:stderr) { Thor::Runner.start }.must =~ /'animal' was called incorrectly\. Call as 'my_script:animal TYPE'/
+ content = capture(:stderr) { Thor::Runner.start }
+ content.strip.must == '"animal" was called incorrectly. Call as "my_script:animal TYPE".'
end
end
View
11 spec/task_spec.rb
@@ -38,9 +38,9 @@ def task(options={})
end
it "does not invoke an existing method" do
- lambda {
- Thor::Task::Dynamic.new('to_s').run([])
- }.must raise_error(Thor::Error, "could not find Thor class or task 'to_s'")
+ mock = mock()
+ mock.class.should_receive(:handle_no_task_error).with("to_s")
+ Thor::Task::Dynamic.new('to_s').run(mock)
end
end
@@ -62,9 +62,8 @@ def task(options={})
it "raises an error if the method to be invoked is private" do
mock = mock()
mock.should_receive(:private_methods).and_return(['can_has'])
- lambda {
- task.run(mock)
- }.must raise_error(Thor::UndefinedTaskError, "the 'can_has' task of Spec::Mocks::Mock is private")
+ mock.class.should_receive(:handle_no_task_error).with("can_has")
+ task.run(mock)
end
end
end
View
8 spec/thor_spec.rb
@@ -112,11 +112,11 @@
end
it "raises an error if a required param is not provided" do
- capture(:stderr) { MyScript.start(["animal"]) }.must =~ /'animal' was called incorrectly\. Call as 'my_script:animal TYPE'/
+ capture(:stderr) { MyScript.start(["animal"]) }.strip.must == '"animal" was called incorrectly. Call as "my_script:animal TYPE".'
end
it "raises an error if the invoked task does not exist" do
- capture(:stderr) { Amazing.start(["animal"]) }.must =~ /The amazing namespace doesn't have a 'animal' task/
+ capture(:stderr) { Amazing.start(["animal"]) }.strip.must == 'Could not find task "animal" in "amazing" namespace.'
end
it "calls method_missing if an unknown method is passed in" do
@@ -124,7 +124,7 @@
end
it "does not call a private method no matter what" do
- capture(:stderr) { MyScript.start(["what"]) }.must =~ /the 'what' task of MyScript is private/
+ capture(:stderr) { MyScript.start(["what"]) }.strip.must == 'Could not find task "what" in "my_script" namespace.'
end
it "uses task default options" do
@@ -200,7 +200,7 @@ def shell
it "raises an error if the task can't be found" do
lambda {
MyScript.task_help(shell, "unknown")
- }.must raise_error(Thor::Error, "task 'unknown' could not be found in namespace 'my_script'")
+ }.must raise_error(Thor::UndefinedTaskError, 'Could not find task "unknown" in "my_script" namespace.')
end
it "normalizes names before claiming they don't exist" do
View
14 spec/util_spec.rb
@@ -92,30 +92,30 @@ def self.clear_user_home!
end
end
- describe "#namespace_to_thor_class_and_task" do
+ describe "#find_class_and_task_by_namespace" do
it "returns a Thor::Group class if full namespace matches" do
- Thor::Util.namespace_to_thor_class_and_task("my_counter").must == [MyCounter, nil]
+ Thor::Util.find_class_and_task_by_namespace("my_counter").must == [MyCounter, nil]
end
it "returns a Thor class if full namespace matches" do
- Thor::Util.namespace_to_thor_class_and_task("thor").must == [Thor, nil]
+ Thor::Util.find_class_and_task_by_namespace("thor").must == [Thor, nil]
end
it "returns a Thor class and the task name" do
- Thor::Util.namespace_to_thor_class_and_task("thor:help").must == [Thor, "help"]
+ Thor::Util.find_class_and_task_by_namespace("thor:help").must == [Thor, "help"]
end
it "fallbacks in the namespace:task look up even if a full namespace does not match" do
Thor.const_set(:Help, Module.new)
- Thor::Util.namespace_to_thor_class_and_task("thor:help").must == [Thor, "help"]
+ Thor::Util.find_class_and_task_by_namespace("thor:help").must == [Thor, "help"]
Thor.send :remove_const, :Help
end
describe 'errors' do
it "raises an error if the Thor class or task can't be found" do
lambda {
- Thor::Util.namespace_to_thor_class_and_task("foobar")
- }.must raise_error(Thor::Error, "could not find Thor class or task 'foobar'")
+ Thor::Util.find_class_and_task_by_namespace!("foobar")
+ }.must raise_error(Thor::Error, 'Could not find namespace or task "foobar".')
end
end
end

0 comments on commit ac419fa

Please sign in to comment.
Something went wrong with that request. Please try again.