classThorclassTask<Struct.new(:name,:description,:long_description,:usage,:options)FILE_REGEXP=/^#{Regexp.escape(File.dirname(__FILE__))}/definitialize(name,description,long_description,usage,options=nil)super(name.to_s,description,long_description,usage,options||{})enddefinitialize_copy(other)#:nodoc:super(other)self.options=other.options.dupifother.optionsenddefhidden?falseend# By default, a task invokes a method in the thor class. You can change this# implementation to create custom tasks.defrun(instance,args=[])public_method?(instance)?instance.send(name,*args):instance.class.handle_no_task_error(name)rescueArgumentError=>ehandle_argument_error?(instance,e,caller)?instance.class.handle_argument_error(self,e):(raisee)rescueNoMethodError=>ehandle_no_method_error?(instance,e,caller)?instance.class.handle_no_task_error(name):(raisee)end# Returns the formatted usage by injecting given required arguments# and required options into the given usage.defformatted_usage(klass,namespace=true,subcommand=false)ifnamespacenamespace=klass.namespaceformatted="#{namespace.gsub(/^(default)/,'')}:"formatted.sub!(/.$/,' ')ifsubcommandendformatted||=""# Add usage with required argumentsformatted<<ifklass&&!klass.arguments.empty?usage.to_s.gsub(/^#{name}/)do|match|match<<" "<<klass.arguments.map{|a|a.usage}.compact.join(' ')endelseusage.to_send# Add required optionsformatted<<" #{required_options}"# Strip and go!formatted.stripendprotecteddefnot_debugging?(instance)!(instance.class.respond_to?(:debugging)&&instance.class.debugging)enddefrequired_options@required_options||=options.map{|_,o|o.usageifo.required?}.compact.sort.join(" ")end# Given a target, checks if this class name is not a private/protected method.defpublic_method?(instance)#:nodoc:collection=instance.private_methods+instance.protected_methods(collection&[name.to_s,name.to_sym]).empty?enddefsans_backtrace(backtrace,caller)#:nodoc:saned=backtrace.reject{|frame|frame=~FILE_REGEXP}saned-=callerenddefhandle_argument_error?(instance,error,caller)not_debugging?(instance)&&error.message=~/wrong number of arguments/&&beginsaned=sans_backtrace(error.backtrace,caller)# Ruby 1.9 always include the called method in the backtracesaned.empty?||(saned.size==1&&RUBY_VERSION>="1.9")endenddefhandle_no_method_error?(instance,error,caller)not_debugging?(instance)&&error.message=~/^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/endend# A task that is hidden in help messages but still invocable.classHiddenTask<Taskdefhidden?trueendend# A dynamic task that handles method missing scenarios.classDynamicTask<Taskdefinitialize(name,options=nil)super(name.to_s,"A dynamically-generated task",name.to_s,name.to_s,options)enddefrun(instance,args=[])if(instance.methods&[name.to_s,name.to_sym]).empty?superelseinstance.class.handle_no_task_error(name)endendendend