Class: Thor::Command

Inherits:
Struct
  • Object
show all
Includes:
SemanticLogger::Loggable
Defined in:
lib/thor/command.rb

Direct Known Subclasses

DynamicCommand, HiddenCommand

Constant Summary collapse

FILE_REGEXP =
/^#{Regexp.escape(File.dirname(__FILE__))}/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, description, long_description, usage, options = nil) ⇒ Command



14
15
16
# File 'lib/thor/command.rb', line 14

def initialize(name, description, long_description, usage, options = nil)
  super(name.to_s, description, long_description, usage, options || {})
end

Instance Attribute Details

#ancestor_nameObject

Returns the value of attribute ancestor_name



4
5
6
# File 'lib/thor/command.rb', line 4

def ancestor_name
  @ancestor_name
end

#descriptionObject

Returns the value of attribute description



4
5
6
# File 'lib/thor/command.rb', line 4

def description
  @description
end

#long_descriptionObject

Returns the value of attribute long_description



4
5
6
# File 'lib/thor/command.rb', line 4

def long_description
  @long_description
end

#nameObject

Returns the value of attribute name



4
5
6
# File 'lib/thor/command.rb', line 4

def name
  @name
end

#optionsObject

Returns the value of attribute options



4
5
6
# File 'lib/thor/command.rb', line 4

def options
  @options
end

#usageObject

Returns the value of attribute usage



4
5
6
# File 'lib/thor/command.rb', line 4

def usage
  @usage
end

Instance Method Details

#formatted_usage(klass, namespace = true, subcommand = false) ⇒ Object

Returns the formatted usage by injecting given required arguments and required options into the given usage.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/thor/command.rb', line 80

def formatted_usage(klass, namespace = true, subcommand = false)
  logger.trace "Formatting usage",
    self: self,
    klass: klass,
    namespace: namespace,
    subcommand: subcommand,
    ancestor_name: ancestor_name
  
  if ancestor_name
    formatted = "#{ancestor_name} ".dup # add space
  elsif namespace
    namespace = klass.namespace
    formatted = "#{namespace.gsub(/^(default)/, '')}:".dup
  end
  formatted ||= "#{klass.namespace.split(':').last} ".dup if subcommand

  formatted ||= "".dup

  # Add usage with required arguments
  formatted << if klass && !klass.arguments.empty?
                 usage.to_s.gsub(/^#{name}/) do |match|
                   match  << " " \
                          << klass.arguments.map(&:usage).compact.join(" ")
                 end
               else
                 usage.to_s
               end

  # Add required options
  formatted << " #{required_options}"

  # Strip and go!
  formatted.strip
end

#handle_argument_error?(instance, error, caller) ⇒ Boolean (protected)



154
155
156
157
158
159
160
161
162
163
# File 'lib/thor/command.rb', line 154

def handle_argument_error?(instance, error, caller)
  not_debugging?(instance) \
  && (  error.message =~ /wrong number of arguments/ \
        || error.message =~ /given \d*, expected \d*/ ) \
  && begin
    saned = sans_backtrace(error.backtrace, caller)
    # Ruby 1.9 always include the called method in the backtrace
    saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
  end
end

#handle_no_method_error?(instance, error, caller) ⇒ Boolean (protected)



165
166
167
168
169
# File 'lib/thor/command.rb', line 165

def handle_no_method_error?(instance, error, caller)
  not_debugging?(instance) &&
    error.message =~ \
      /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
end

#hidden?Boolean



23
24
25
# File 'lib/thor/command.rb', line 23

def hidden?
  false
end

#initialize_copy(other) ⇒ Object

:nodoc:



18
19
20
21
# File 'lib/thor/command.rb', line 18

def initialize_copy(other) #:nodoc:
  super(other)
  self.options = other.options.dup if other.options
end

#local_method?(instance, name) ⇒ Boolean (protected)



138
139
140
141
142
143
# File 'lib/thor/command.rb', line 138

def local_method?(instance, name)
  methods = instance.public_methods(false) +
            instance.private_methods(false) +
            instance.protected_methods(false)
  !(methods & [name.to_s, name.to_sym]).empty?
end

#not_debugging?(instance) ⇒ Boolean (protected)



117
118
119
# File 'lib/thor/command.rb', line 117

def not_debugging?(instance)
  !(instance.class.respond_to?(:debugging) && instance.class.debugging)
end

#private_method?(instance) ⇒ Boolean (protected)



134
135
136
# File 'lib/thor/command.rb', line 134

def private_method?(instance)
  !(instance.private_methods & [name.to_s, name.to_sym]).empty?
end

#public_method?(instance) ⇒ Boolean (protected)

Given a target, checks if this class name is a public method.



130
131
132
# File 'lib/thor/command.rb', line 130

def public_method?(instance) #:nodoc:
  !(instance.public_methods & [name.to_s, name.to_sym]).empty?
end

#required_optionsObject (protected)



121
122
123
124
125
126
127
# File 'lib/thor/command.rb', line 121

def required_options
  @required_options ||= options.
    map { |_, o| o.usage if o.required? }.
    compact.
    sort.
    join(" ")
end

#run(instance, args = []) ⇒ Object

By default, a command invokes a method in the thor class. You can change this implementation to create custom commands.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/thor/command.rb', line 29

def run(instance, args = [])
  logger.trace "Command#run",
    self: self,
    instance: instance,
    args: args
  
  arity = nil

  if private_method?(instance)
    instance.class.handle_no_command_error(name)
  elsif public_method?(instance)
    arity = instance.method(name).arity
    instance.__send__(name, *args)
  elsif local_method?(instance, :method_missing)
    instance.__send__(:method_missing, name.to_sym, *args)
  else
    instance.class.handle_no_command_error(name)
  end
rescue ArgumentError => e
  if handle_argument_error?(instance, e, caller)
    instance.class.handle_argument_error(self, e, args, arity)
  else
    raise e
  end
rescue NoMethodError => e
  if handle_no_method_error?(instance, e, caller)
    instance.class.handle_no_command_error(name)
  else
    raise e
  end
rescue Exception => error
  # NOTE  Need to use `#__send__` because the instance may define a
  #       command (method) `#send` - and one of the test fixtures **does**:
  #       
  #       //spec/fixtures/script.thor:100
  #       
  #       That's why the Thor code above uses `#__send__`, and we need to
  #       too.
  #       
  instance.__send__ :on_run_error, error, self, args
  
  # We should not get here!!!
  # {Thor::Base#on_run_error} should exit or re-raise :(
  logger.error "#on_run_error failed to exit or re-raise", error: error
  
  # If you want something done right...
  raise error
end

#sans_backtrace(backtrace, caller) ⇒ Object (protected)

:nodoc:



145
146
147
148
149
150
151
152
# File 'lib/thor/command.rb', line 145

def sans_backtrace(backtrace, caller) #:nodoc:
  saned = backtrace.reject { |frame|
    (frame =~ FILE_REGEXP) ||
    (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) ||
    (frame =~ %r{^kernel/} && RUBY_ENGINE =~ /rbx/)
  }
  saned - caller
end