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

Returns a new instance of 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

Returns:

  • (Object)

    the current value of 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

Returns:

  • (Object)

    the current value of description



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

def description
  @description
end

#long_descriptionObject

Returns the value of attribute long_description

Returns:

  • (Object)

    the current value of 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

Returns:

  • (Object)

    the current value of name



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

def name
  @name
end

#optionsObject

Returns the value of attribute options

Returns:

  • (Object)

    the current value of options



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

def options
  @options
end

#usageObject

Returns the value of attribute usage

Returns:

  • (Object)

    the current value of 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.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/thor/command.rb', line 146

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)

Returns:

  • (Boolean)


274
275
276
277
278
279
280
281
282
283
# File 'lib/thor/command.rb', line 274

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)

Returns:

  • (Boolean)


285
286
287
288
289
# File 'lib/thor/command.rb', line 285

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

Returns:

  • (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)

Is ‘name` the name of a method defined in `instance` itself (not any super class)?

Parameters:

  • instance (Thor::Base)

    The Thor instance this command is being run for.

  • name (Symbol | String)

    The method name.

Returns:

  • (Boolean)

    ‘true` if `name` is the name of a method defined in `instance` itself.



257
258
259
260
261
262
# File 'lib/thor/command.rb', line 257

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)

Returns:

  • (Boolean)


183
184
185
# File 'lib/thor/command.rb', line 183

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

#private_method?(instance) ⇒ Boolean (protected)

Is this command’s #name a private method of ‘instance`?

Parameters:

  • instance (Thor::Base)

    The Thor instance this command is being run for.

Returns:

  • (Boolean)

    ‘true` if #name is a private method of `instance`.



240
241
242
# File 'lib/thor/command.rb', line 240

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

#public_method?(instance) ⇒ Boolean (protected)

Is this command’s #name a public method of ‘instance`?

Parameters:

  • instance (Thor::Base)

    The Thor instance this command is being run for.

Returns:

  • (Boolean)

    ‘true` if #name is a public method of `instance`.



227
228
229
# File 'lib/thor/command.rb', line 227

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

#required_optionsObject (protected)



187
188
189
190
191
192
193
# File 'lib/thor/command.rb', line 187

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

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

Run a command by calling the actual method on the Base instance.

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

Parameters:

  • instance (Thor::Base)

    Thor class instance the command is being run for.

  • args (Array<?>) (defaults to: [])

    Arguments for the command.

Returns:

  • The return value of the command method on ‘instance`.

Raises:



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
77
78
79
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/thor/command.rb', line 47

def run instance, args = []
  logger.debug "Command#run",
    name: self.name,
    args: args
  
  # raise "BAD!!!" unless args.include? '--'
  
  # Declaration for arity of the method, which is set in (2) below and
  # used when handling raised {ArgumentError}
  arity = nil
  
  # Method invocation switch - figure out how to make the method call to
  # `instance`, or error out.
  # 
  # Cases:
  # 
  # 1.  Protect from calling private methods by error'ing out if {#name}
  #     is the name of a private method of `instance`.
  #     
  result = if private_method? instance
    instance.class.handle_no_command_error name
  
  # 2.  The success case - if {#name} is a public method of `instance`
  #     than call it with `args`.
  #     
  elsif public_method? instance
    # Save the arity to use when handling {ArgumentError} below
    # 
    # TODO  Why does it fetch the {Method} *then* use {#__send__} instead
    #       of just `#call` it?
    #       
    arity = instance.method( name ).arity
    
    # Unless the method is a subcommand, remove any '--' separators
    # since we know we're done option parsin'
    unless subcommand? instance, name
      args = args.reject { |arg| arg == '--' }
    end
    
    # Do that call
    instance.__send__ name, *args
  
  # 3.  If the {Thor} instance has a `#method_missing` defined in *itself*
  #     (not any super class) than call that.
  #     
  elsif local_method? instance, :method_missing
    instance.__send__ :method_missing, name.to_sym, *args
  
  # 4.  We got nothing... pass of to
  #     {Thor::Base::ClassMethods.handle_no_command_error}
  #     which will raise.
  #     
  else
    instance.class.handle_no_command_error name
  
  end # Method invocation switch
  
  instance.__send__ :on_run_success, result, self, args
  
rescue ArgumentError => error
  if handle_argument_error? instance, error, caller
    # NOTE  I *believe* `arity` could still be `nil`, assuming that
    #       (3) could raise {ArgumentError} and end up here.
    #       
    #       However...
    instance.class.handle_argument_error self, error, args, arity
  else
    raise error
  end

rescue NoMethodError => error
  if handle_no_method_error? instance, error, caller
    instance.class.handle_no_command_error name
  else
    raise error
  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:



265
266
267
268
269
270
271
272
# File 'lib/thor/command.rb', line 265

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

#subcommand?(instance, name) ⇒ return_type (protected)

Is ‘name` a subcommand of `instance`?

Parameters:

  • instance (Thor::Base)

    The Thor instance this command is being run for.

  • name (Symbol | String)

    The subcommand / method name.

Returns:

  • (return_type)

    @todo Document return value.



208
209
210
211
212
213
214
215
# File 'lib/thor/command.rb', line 208

def subcommand? instance, name
  # It doesn't look like {Thor::Group} has `.subcommands`, so test for
  # that first.
  return false unless instance.class.respond_to?( :subcommands )
  
  # See if the names is in the subcommands
  instance.class.subcommands.include? name.to_s
end