Class: Pry::Method

Inherits:
Object show all
Includes:
Helpers::DocumentationHelpers, RbxMethod
Defined in:
lib/pry/method.rb

Overview

This class wraps the normal Method and UnboundMethod classes to provide extra functionality useful to Pry.

Direct Known Subclasses

Disowned

Defined Under Namespace

Classes: Disowned

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::DocumentationHelpers

#process_comment_markup, #process_rdoc, #process_yardoc, #process_yardoc_tag, #strip_comments_from_c_code, #strip_leading_hash_and_whitespace_from_ruby_comments, #strip_leading_whitespace

Methods included from RbxMethod

#core_code, #core_doc

Constructor Details

#initialize(method, known_info = {}) ⇒ Pry::Method

A new instance of Pry::Method wrapping the given ::Method, UnboundMethod, or Proc.

Parameters:

  • method (::Method, UnboundMethod, Proc)
  • known_info (Hash) (defaults to: {})

    Can be used to pre-cache expensive to compute stuff.



215
216
217
218
# File 'lib/pry/method.rb', line 215

def initialize(method, known_info={})
  @method = method
  @visibility = known_info[:visibility]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object

Delegate any unknown calls to the wrapped method.



434
435
436
# File 'lib/pry/method.rb', line 434

def method_missing(method_name, *args, &block)
  @method.send(method_name, *args, &block)
end

Class Method Details

.all_from_class(klass, include_super = true) ⇒ Array[Pry::Method]

Get all of the instance methods of a Class or Module

Parameters:

  • klass (Class, Module)
  • include_super (Boolean) (defaults to: true)

    Whether to include methods from ancestors.

Returns:



136
137
138
# File 'lib/pry/method.rb', line 136

def all_from_class(klass, include_super=true)
  all_from_common(klass, :instance_method, include_super)
end

.all_from_common(obj, method_type, include_super = true) ⇒ Object (private)

See all_from_class and all_from_obj. If method_type is :instance_method, obj must be a Class or a Module If method_type is :method, obj can be any Object

N.B. we pre-cache the visibility here to avoid O(N²) behaviour in "ls".



178
179
180
181
182
183
184
# File 'lib/pry/method.rb', line 178

def all_from_common(obj, method_type, include_super=true)
  %w(public protected private).map do |visibility|
    safe_send(obj, :"#{visibility}_#{method_type}s", include_super).map do |method_name|
      new(safe_send(obj, method_type, method_name), :visibility => visibility.to_sym)
    end
  end.flatten(1)
end

.all_from_obj(obj, include_super = true) ⇒ Array[Pry::Method]

Get all of the methods on an Object

Parameters:

  • obj (Object)
  • include_super (Boolean) (defaults to: true)

    Whether to include methods from ancestors.

Returns:



144
145
146
# File 'lib/pry/method.rb', line 144

def all_from_obj(obj, include_super=true)
  all_from_common(obj, :method, include_super)
end

.from_binding(b) ⇒ Pry::Method?

Given a Binding, try to extract the ::Method it originated from and use it to instantiate a Pry::Method. Return nil if this isn't possible.

Parameters:

  • b (Binding)

Returns:



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
# File 'lib/pry/method.rb', line 64

def from_binding(b)
  meth_name = b.eval('__method__')
  if [:__script__, nil].include?(meth_name)
    nil
  else
    method = begin
               new(b.eval("Object.instance_method(:method).bind(self).call(#{meth_name.to_s.inspect})"))
             rescue NameError, NoMethodError
               Disowned.new(b.eval('self'), meth_name.to_s)
             end

    # it's possible in some cases that the method we find by this approach is a sub-method of
    # the one we're currently in, consider:
    #
    # class A; def b; binding.pry; end; end
    # class B < A; def b; super; end; end
    #
    # Given that we can normally find the source_range of methods, and that we know which
    # __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
    #
    # This obviously won't work if the source is unavaiable for some reason, or if both
    # methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
    # is broken.
    #
    guess = method

    while guess
      # needs rescue if this is a Disowned method or a C method or something...
      # TODO: Fix up the exception handling so we don't need a bare rescue
      if (guess.source_file && guess.source_range rescue false) &&
          File.expand_path(guess.source_file) == File.expand_path(b.eval('__FILE__')) &&
          guess.source_range.include?(b.eval('__LINE__'))
        return guess
      else
        guess = guess.super
      end
    end

    # Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
    # This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
    # or other unknown circumstances (TODO: we should warn the user when this happens)
    method
  end
end

.from_class(klass, name) ⇒ Pry::Method? Also known as: from_module

Given a Class or Module and the name of a method, try to instantiate a Pry::Method containing the instance method of that name. Return nil if no such method exists.

Parameters:

  • klass (Class, Module)
  • name (String)

Returns:



116
117
118
# File 'lib/pry/method.rb', line 116

def from_class(klass, name)
  new(safe_send(klass, :instance_method, name)) rescue nil
end

.from_obj(obj, name) ⇒ Pry::Method?

Given an object and the name of a method, try to instantiate a Pry::Method containing the method of that name bound to that object. Return nil if no such method exists.

Parameters:

Returns:



128
129
130
# File 'lib/pry/method.rb', line 128

def from_obj(obj, name)
  new(safe_send(obj, :method, name)) rescue nil
end

.from_str(name, target = TOPLEVEL_BINDING, options = {}) ⇒ Pry::Method?

Given a string representing a method name and optionally a binding to search in, find and return the requested method wrapped in a Pry::Method instance.

Parameters:

  • name (String, nil)

    The name of the method to retrieve, or nil to delegate to from_binding instead.

  • target (Binding) (defaults to: TOPLEVEL_BINDING)

    The context in which to search for the method.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :instance (Boolean)

    Look for an instance method if name doesn't contain any context.

  • :methods (Boolean)

    Look for a bound/singleton method if name doesn't contain any context.

Returns:

  • (Pry::Method, nil)

    A Pry::Method instance containing the requested method, or nil if no method could be located matching the parameters.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/pry/method.rb', line 38

def from_str(name, target=TOPLEVEL_BINDING, options={})
  if name.nil?
    from_binding(target)
  elsif name.to_s =~ /(.+)\#(\S+)\Z/
    context, meth_name = $1, $2
    from_module(target.eval(context), meth_name)
  elsif name.to_s =~ /(.+)\.(\S+)\Z/
    context, meth_name = $1, $2
    from_obj(target.eval(context), meth_name)
  elsif options[:instance]
    from_module(target.eval("self"), name)
  elsif options[:methods]
    from_obj(target.eval("self"), name)
  else
    from_str(name, target, :instance => true) or
      from_str(name, target, :methods => true)
  end
end

.instance_resolution_order(klass) ⇒ Array[Class, Module]

Get every Class and Module, in order, that will be checked when looking for methods on instances of the given Class or Module. This does not treat singleton classes of classes specially.

Parameters:

  • klass (Class, Module)

Returns:



166
167
168
169
# File 'lib/pry/method.rb', line 166

def instance_resolution_order(klass)
  # include klass in case it is a singleton class,
  ([klass] + klass.ancestors).uniq
end

.resolution_order(obj) ⇒ Array[Class, Module]

Get every Class and Module, in order, that will be checked when looking for an instance method to call on this object.

Parameters:

Returns:



152
153
154
155
156
157
158
159
# File 'lib/pry/method.rb', line 152

def resolution_order(obj)
  if Class === obj
    singleton_class_resolution_order(obj) + instance_resolution_order(Class)
  else
    klass = singleton_class(obj) rescue obj.class
    instance_resolution_order(klass)
  end
end

.safe_send(obj, method, *args, &block) ⇒ Object (private)

Acts like send but ignores any methods defined below Object or Class in the inheritance hierarchy. This is required to introspect methods on objects like Net::HTTP::Get that have overridden the method method.



190
191
192
# File 'lib/pry/method.rb', line 190

def safe_send(obj, method, *args, &block)
  (Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block)
end

.singleton_class(obj) ⇒ Object (private)



207
# File 'lib/pry/method.rb', line 207

def singleton_class(obj); class << obj; self; end end

.singleton_class_resolution_order(klass) ⇒ Object (private)

Get the singleton classes of superclasses that could define methods on the given class object, and any modules they include. If a module is included at multiple points in the ancestry, only the lowest copy will be returned.



199
200
201
202
203
204
205
# File 'lib/pry/method.rb', line 199

def singleton_class_resolution_order(klass)
  resolution_order = klass.ancestors.map do |anc|
    [singleton_class(anc)] + singleton_class(anc).included_modules if anc.is_a?(Class)
  end.compact.flatten(1)

  resolution_order.reverse.uniq.reverse - Class.included_modules
end

Instance Method Details

#==(obj) ⇒ Boolean

Returns:

  • (Boolean)


412
413
414
415
416
417
418
# File 'lib/pry/method.rb', line 412

def ==(obj)
  if obj.is_a? Pry::Method
    obj == @method
  else
    @method == obj
  end
end

#alias?Boolean

Returns Is the method definitely an alias?.

Returns:

  • (Boolean)

    Is the method definitely an alias?



407
408
409
# File 'lib/pry/method.rb', line 407

def alias?
  name != original_name
end

#docString?

Returns The documentation for the method, or nil if it's unavailable.

Returns:

  • (String, nil)

    The documentation for the method, or nil if it's unavailable.

Raises:

  • (CommandError)

    Raises when the method was defined in the REPL.



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/pry/method.rb', line 282

def doc
  @doc ||= case source_type
    when :c
      info = pry_doc_info
      info.docstring if info
    when :ruby
      if Helpers::BaseHelpers.rbx? && !pry_method?
        strip_leading_hash_and_whitespace_from_ruby_comments(core_doc)
      elsif pry_method?
        strip_leading_hash_and_whitespace_from_ruby_comments(doc_for_pry_method)
      else
        strip_leading_hash_and_whitespace_from_ruby_comments(@method.comment)
      end
    end
end

#doc_for_pry_methodObject (private)

FIXME: a very similar method to this exists on WrappedModule: extract_doc_for_candidate



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/pry/method.rb', line 450

def doc_for_pry_method
  _, line_num = source_location

  buffer = ""
  Pry.line_buffer[0..(line_num - 1)].each do |line|
    # Add any line that is a valid ruby comment,
    # but clear as soon as we hit a non comment line.
    if (line =~ /^\s*#/) || (line =~ /^\s*$/)
      buffer << line.lstrip
    else
      buffer.replace("")
    end
  end

  buffer
end

#dynamically_defined?Boolean

Returns Was the method defined outside a source file?.

Returns:

  • (Boolean)

    Was the method defined outside a source file?



397
398
399
# File 'lib/pry/method.rb', line 397

def dynamically_defined?
  !!(source_file and source_file =~ /(\(.*\))|<.*>/)
end

#is_a?(klass) ⇒ Boolean Also known as: kind_of?

Parameters:

  • klass (Class)

Returns:

  • (Boolean)


422
423
424
# File 'lib/pry/method.rb', line 422

def is_a?(klass)
  klass == Pry::Method or @method.is_a?(klass)
end

#method_name_from_first_line(first_ln) ⇒ String? (private)

Parameters:

  • first_ln (String)

    The first line of a method definition.

Returns:

  • (String, nil)


483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/pry/method.rb', line 483

def method_name_from_first_line(first_ln)
  return nil if first_ln.strip !~ /^def /

  tokens = CodeRay.scan(first_ln, :ruby)
  tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens)
  tokens.each_cons(2) do |t1, t2|
    if t2.last == :method || t2.last == :ident && t1 == [".", :operator]
      return t2.first
    end
  end

  nil
end

#nameString

Get the name of the method as a String, regardless of the underlying Method#name type.

Returns:

  • (String)


222
223
224
# File 'lib/pry/method.rb', line 222

def name
  @method.name.to_s
end

#name_with_ownerString

Get the name of the method including the class on which it was defined.

Examples:

method(:puts).method_name
=> "Kernel.puts"

Returns:

  • (String)


243
244
245
# File 'lib/pry/method.rb', line 243

def name_with_owner
  "#{wrapped_owner.method_prefix}#{name}"
end

#original_nameString?

Returns The original name the method was defined under, before any aliasing, or nil if it can't be determined.

Returns:

  • (String, nil)

    The original name the method was defined under, before any aliasing, or nil if it can't be determined.



391
392
393
394
# File 'lib/pry/method.rb', line 391

def original_name
  return nil if source_type != :ruby
  method_name_from_first_line(source.lines.first)
end

#pry_doc_infoYARD::CodeObjects::MethodObject (private)

Returns:

  • (YARD::CodeObjects::MethodObject)

Raises:

  • (CommandError)

    Raises when the method can't be found or pry-doc isn't installed.



441
442
443
444
445
446
447
# File 'lib/pry/method.rb', line 441

def pry_doc_info
  if Pry.config.has_pry_doc
    Pry::MethodInfo.info_for(@method) or raise CommandError, "Cannot locate this method: #{name}. (source_location returns nil)"
  else
    raise CommandError, "Cannot locate this method: #{name}. Try `gem install pry-doc` to get access to Ruby Core documentation."
  end
end

#pry_method?Boolean

Returns Was the method defined within the Pry REPL?.

Returns:

  • (Boolean)

    Was the method defined within the Pry REPL?



402
403
404
# File 'lib/pry/method.rb', line 402

def pry_method?
  source_file == Pry.eval_path
end

#respond_to?(method_name) ⇒ Boolean

Parameters:

  • method_name (String, Symbol)

Returns:

  • (Boolean)


429
430
431
# File 'lib/pry/method.rb', line 429

def respond_to?(method_name)
  super or @method.respond_to?(method_name)
end

#signatureString

Returns A representation of the method's signature, including its name and parameters. Optional and "rest" parameters are marked with * and block parameters with &. If the parameter names are unavailable, they're given numbered names instead. Paraphrased from awesome_print gem.

Returns:

  • (String)

    A representation of the method's signature, including its name and parameters. Optional and "rest" parameters are marked with * and block parameters with &. If the parameter names are unavailable, they're given numbered names instead. Paraphrased from awesome_print gem.



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/pry/method.rb', line 357

def signature
  if respond_to?(:parameters)
    args = parameters.inject([]) do |arr, (type, name)|
      name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
      arr << case type
             when :req   then name.to_s
             when :opt   then "#{name}=?"
             when :rest  then "*#{name}"
             when :block then "&#{name}"
             else '?'
             end
    end
  else
    args = (1..arity.abs).map { |i| "arg#{i}" }
    args[-1] = "*#{args[-1]}" if arity < 0
  end

  "#{name}(#{args.join(', ')})"
end

#sourceString?

Returns The source code of the method, or nil if it's unavailable.

Returns:

  • (String, nil)

    The source code of the method, or nil if it's unavailable.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/pry/method.rb', line 248

def source
  @source ||= case source_type
              when :c
                info = pry_doc_info
                if info and info.source
                  code = strip_comments_from_c_code(info.source)
                end
              when :ruby
                # clone of MethodSource.source_helper that knows to use our
                # hacked version of source_location for rbx core methods, and
                # our input buffer for methods defined in (pry)
                file, line = *source_location
                raise SourceNotFoundError, "Could not locate source for #{name_with_owner}!" unless file

                begin
                  code = Pry::Code.from_file(file).expression_at(line)
                rescue SyntaxError => e
                  raise MethodSource::SourceNotFoundError.new(e.message)
                end
                strip_leading_whitespace(code)
              end
end

#source?Boolean

Can we get the source code for this method?

Returns:

  • (Boolean)


273
274
275
276
277
# File 'lib/pry/method.rb', line 273

def source?
  !!source
rescue MethodSource::SourceNotFoundError
  false
end

#source_fileString?

Returns The name of the file the method is defined in, or nil if the filename is unavailable.

Returns:

  • (String, nil)

    The name of the file the method is defined in, or nil if the filename is unavailable.



315
316
317
318
319
320
321
322
323
324
# File 'lib/pry/method.rb', line 315

def source_file
  if source_location.nil?
    if !Helpers::BaseHelpers.rbx? and source_type == :c
      info = pry_doc_info
      info.file if info
    end
  else
    source_location.first
  end
end

#source_lineFixnum?

Returns The line of code in source_file which begins the method's definition, or nil if that information is unavailable.

Returns:

  • (Fixnum, nil)

    The line of code in source_file which begins the method's definition, or nil if that information is unavailable.



328
329
330
# File 'lib/pry/method.rb', line 328

def source_line
  source_location.nil? ? nil : source_location.last
end

#source_locationObject



304
305
306
307
308
309
310
311
# File 'lib/pry/method.rb', line 304

def source_location
  if @method.source_location && Helpers::BaseHelpers.rbx?
    file, line = @method.source_location
    [RbxPath.convert_path_to_full(file), line]
  else
    @method.source_location
  end
end

#source_rangeRange?

Returns The range of lines in source_file which contain the method's definition, or nil if that information is unavailable.

Returns:

  • (Range, nil)

    The range of lines in source_file which contain the method's definition, or nil if that information is unavailable.



334
335
336
# File 'lib/pry/method.rb', line 334

def source_range
  source_location.nil? ? nil : (source_line)..(source_line + source.lines.count - 1)
end

#source_typeSymbol

Returns The source type of the method. The options are :ruby for Ruby methods or :c for methods written in C.

Returns:

  • (Symbol)

    The source type of the method. The options are :ruby for Ruby methods or :c for methods written in C.



300
301
302
# File 'lib/pry/method.rb', line 300

def source_type
  source_location.nil? ? :c : :ruby
end

#super(times = 1) ⇒ Pry::Method?

Returns The wrapped method that is called when you use "super" in the body of this method.

Returns:

  • (Pry::Method, nil)

    The wrapped method that is called when you use "super" in the body of this method.



379
380
381
382
383
384
385
386
387
# File 'lib/pry/method.rb', line 379

def super(times=1)
  if UnboundMethod === @method
    sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times)
  else
    sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times)
    sup &&= sup.bind(receiver)
  end
  Pry::Method.new(sup) if sup
end

#super_using_ancestors(ancestors, times = 1) ⇒ Method (private)

Returns The unwrapped super-method.

Parameters:

  • ancestors (Class, Module)

    The ancestors to investigate

Returns:

  • (Method)

    The unwrapped super-method



469
470
471
472
473
474
475
476
477
478
479
# File 'lib/pry/method.rb', line 469

def super_using_ancestors(ancestors, times=1)
  next_owner = self.owner
  times.times do
    i = ancestors.index(next_owner) + 1
    while ancestors[i] && !(ancestors[i].method_defined?(name) || ancestors[i].private_method_defined?(name))
      i += 1
    end
    next_owner = ancestors[i] or return nil
  end
  next_owner.instance_method(name) rescue nil
end

#undefined?Boolean

Is the method undefined? (aka Disowned)

Returns:

  • (Boolean)

    false



234
235
236
# File 'lib/pry/method.rb', line 234

def undefined?
  false
end

#visibilitySymbol

Returns The visibility of the method. May be :public, :protected, or :private.

Returns:

  • (Symbol)

    The visibility of the method. May be :public, :protected, or :private.



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/pry/method.rb', line 340

def visibility
 @visibility ||= if owner.public_instance_methods.any? { |m| m.to_s == name }
                   :public
                 elsif owner.protected_instance_methods.any? { |m| m.to_s == name }
                   :protected
                 elsif owner.private_instance_methods.any? { |m| m.to_s == name }
                   :private
                 else
                   :none
                 end
end

#wrapped_ownerPry::Module

Get the owner of the method as a Pry::Module

Returns:

  • (Pry::Module)


228
229
230
# File 'lib/pry/method.rb', line 228

def wrapped_owner
  @wrapped_owner ||= Pry::WrappedModule.new(owner)
end