Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

118 lines (100 sloc) 3.646 kb
class Thor
class Task < Struct.new(:name, :description, :usage, :options)
# A dynamic task that handles method missing scenarios.
#
class Dynamic < Task
def initialize(name)
super(name.to_s, "A dynamically-generated task", name.to_s)
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}'"
end
super
end
end
def initialize(name, description, usage, options=nil)
super(name.to_s, description, usage, options || {})
end
def initialize_copy(other) #:nodoc:
super(other)
self.options = other.options.dup if other.options
end
# 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)
rescue ArgumentError => e
parse_argument_error(instance, e, caller)
rescue NoMethodError => e
parse_no_method_error(instance, e)
end
# Returns the formatted usage. If a class is given, the class arguments are
# injected in the usage.
#
def formatted_usage(klass=nil, namespace=nil)
namespace = klass.namespace if namespace.nil?
formatted = if namespace
"#{namespace.gsub(/^(default|thor:runner:)/,'')}:"
else
""
end
formatted << formatted_arguments(klass)
formatted.strip!
formatted
end
# Injects the class arguments into the task usage.
#
def formatted_arguments(klass)
if klass && !klass.arguments.empty?
usage.to_s.gsub(/^#{name}/) do |match|
match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
end
else
usage.to_s
end
end
# Returns the options usage for this task.
#
def formatted_options
@formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ")
end
protected
# Given a target, checks if this class name is not a private/protected method.
#
def public_method?(instance) #:nodoc:
collection = instance.private_methods + instance.protected_methods
(collection & [name.to_s, name.to_sym]).empty?
end
# Clean everything that comes from the Thor gempath and remove the caller.
#
def sans_backtrace(backtrace, caller) #:nodoc:
dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/
saned = backtrace.reject { |frame| frame =~ dirname }
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 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
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
end
end
end
Jump to Line
Something went wrong with that request. Please try again.