Class: Thor::Command

Inherits:
Struct
  • Object
show all
Includes:
NRSER::Log::Mixin
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: nil, long_description: nil, usage: nil, examples: [], arguments: [], options: nil) ⇒ Command

Returns a new instance of Command.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/thor/command.rb', line 18

def initialize  name:,
                description: nil,
                long_description: nil,
                usage: nil,
                examples: [],
                arguments: [],
                options: nil
  super \
    name.to_s,
    description,
    long_description,
    usage,
    examples,
    arguments,
    options || {}
end

Instance Attribute Details

#ancestor_nameObject

Returns the value of attribute ancestor_name

Returns:

  • (Object)

    the current value of ancestor_name



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

def ancestor_name
  @ancestor_name
end

#argumentsObject

Returns the value of attribute arguments

Returns:

  • (Object)

    the current value of arguments



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

def arguments
  @arguments
end

#descriptionObject

Returns the value of attribute description

Returns:

  • (Object)

    the current value of description



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

def description
  @description
end

#examplesObject

Returns the value of attribute examples

Returns:

  • (Object)

    the current value of examples



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

def examples
  @examples
end

#long_descriptionObject

Returns the value of attribute long_description

Returns:

  • (Object)

    the current value of long_description



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

def long_description
  @long_description
end

#nameObject

Returns the value of attribute name

Returns:

  • (Object)

    the current value of name



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

def name
  @name
end

#optionsObject

Returns the value of attribute options

Returns:

  • (Object)

    the current value of options



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

def options
  @options
end

#usageObject

Returns the value of attribute usage

Returns:

  • (Object)

    the current value of usage



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

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.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/thor/command.rb', line 163

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
  arguments = klass&.arguments( command: self ) || []
  formatted << unless arguments.empty?
                 usage.to_s.gsub(/^#{name}/) do |match|
                   match  << " " \
                          << 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)


333
334
335
336
337
338
339
340
341
342
# File 'lib/thor/command.rb', line 333

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)


344
345
346
347
348
# File 'lib/thor/command.rb', line 344

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)


40
41
42
# File 'lib/thor/command.rb', line 40

def hidden?
  false
end

#initialize_copy(other) ⇒ Object

:nodoc:



35
36
37
38
# File 'lib/thor/command.rb', line 35

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.



316
317
318
319
320
321
# File 'lib/thor/command.rb', line 316

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

#names_by_formatHash<Symbol, String>

Note:

In reality, since input words have ‘-` replaced with `_` when finding their instances during execution,

The name formats we recognize for the command, in command completion resolution order.

Returns:

  • (Hash<Symbol, String>)

    Keys and values in order (and order matters when completing commands, first result takes priority):

    1. ‘usage:` #usage_name

    2. ‘method:` #name, which is the command’s actual method name,

      and hence "underscored".
      
    3. ‘dashed:` A dash-separated version of #name.



231
232
233
234
235
236
237
# File 'lib/thor/command.rb', line 231

def names_by_format
  @names_by_format ||= {
    usage:  usage_name,
    method: name,
    dashed: name.dasherize,
  }.freeze
end

#not_debugging?(instance) ⇒ Boolean (protected)

Returns:

  • (Boolean)


242
243
244
# File 'lib/thor/command.rb', line 242

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`.



299
300
301
# File 'lib/thor/command.rb', line 299

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`.



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

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

#required_optionsObject (protected)



246
247
248
249
250
251
252
# File 'lib/thor/command.rb', line 246

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:



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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/thor/command.rb', line 64

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:



324
325
326
327
328
329
330
331
# File 'lib/thor/command.rb', line 324

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.



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

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

#usage_nameString

The command’s name as depicted in it’s #usage message.

We prefer this format when completing commands because it’s how we depict the command to the user.

Returns:

  • (String)

See Also:



210
211
212
# File 'lib/thor/command.rb', line 210

def usage_name
  @usage_name ||= usage.shellsplit[0]
end