class Pry::Method

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

Public Class Methods

all_from_class(klass, include_super = true) click to toggle source

Get all of the instance methods of a `Class` or `Module` @param [Class,Module] klass @param [Boolean] include_super Whether to include methods from ancestors. @return [Array]

# File lib/pry/method.rb, line 161
def all_from_class(klass, include_super = true)
  %w[public protected private].flat_map do |visibility|
    safe_send(
      klass, :"#{visibility}_instance_methods", include_super
    ).map do |method_name|
      new(
        safe_send(klass, :instance_method, method_name),
        visibility: visibility.to_sym
      )
    end
  end
end
all_from_obj(obj, include_super = true) click to toggle source

Get all of the methods on an `Object`

@param [Object] obj

@param [Boolean] include_super

indicates whether or not to include methods from ancestors.

@return [Array]

# File lib/pry/method.rb, line 184
def all_from_obj(obj, include_super = true)
  all_from_class(singleton_class_of(obj), include_super)
end
from_binding(binding) click to toggle source

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.

@param [Binding] binding @return [Pry::Method, nil]

# File lib/pry/method.rb, line 77
def from_binding(binding)
  meth_name = binding.eval('::Kernel.__method__')
  if [:__script__, nil].include?(meth_name)
    nil
  else
    method =
      begin
        if Object === binding.eval('self') # rubocop:disable Style/CaseEquality
          new(
            Kernel.instance_method(:method)
              .bind(binding.eval("self"))
              .call(meth_name)
          )
        else
          str = 'class << self; self; end' \
                '.instance_method(::Kernel.__method__).bind(self)'
          new(binding.eval(str))
        end
      rescue NameError, NoMethodError # rubocop:disable Lint/ShadowedException
        Disowned.new(binding.eval('self'), meth_name.to_s)
      end

    if WeirdMethodLocator.weird_method?(method, binding)
      WeirdMethodLocator.new(method, binding).find_method || method
    else
      method
    end
  end
end
from_class(klass, name, target = TOPLEVEL_BINDING) click to toggle source

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.

@param [Class, Module] klass @param [String] name @param [Binding] target The binding where the method is looked up. @return [Pry::Method, nil]

# File lib/pry/method.rb, line 136
def from_class(klass, name, target = TOPLEVEL_BINDING)
  new(lookup_method_via_binding(klass, name, :instance_method, target))
rescue StandardError
  nil
end
Also aliased as: from_module
from_module(klass, name, target = TOPLEVEL_BINDING)
Alias for: from_class
from_obj(obj, name, target = TOPLEVEL_BINDING) click to toggle source

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.

@param [Object] obj @param [String] name @param [Binding] target The binding where the method is looked up. @return [Pry::Method, nil]

# File lib/pry/method.rb, line 151
def from_obj(obj, name, target = TOPLEVEL_BINDING)
  new(lookup_method_via_binding(obj, name, :method, target))
rescue StandardError
  nil
end
from_str(name, target = TOPLEVEL_BINDING, options = {}) click to toggle source

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.

@param [String] name The name of the method to retrieve. @param [Binding] target The context in which to search for the method. @param [Hash] options @option options [Boolean] :instance Look for an instance method if

`name` doesn't contain any context.

@option options [Boolean] :methods Look for a bound/singleton method if

`name` doesn't contain any context.

@return [Pry::Method, nil] A `Pry::Method` instance containing the

requested method, or `nil` if name is `nil` or no method could be
located matching the parameters.
# File lib/pry/method.rb, line 43
def from_str(name, target = TOPLEVEL_BINDING, options = {})
  if name.nil?
    nil
  elsif name.to_s =~ /(.+)\#(\S+)\Z/
    context = Regexp.last_match(1)
    meth_name = Regexp.last_match(2)
    from_module(target.eval(context), meth_name, target)
  elsif name.to_s =~ /(.+)(\[\])\Z/
    context = Regexp.last_match(1)
    meth_name = Regexp.last_match(2)
    from_obj(target.eval(context), meth_name, target)
  elsif name.to_s =~ /(.+)(\.|::)(\S+)\Z/
    context = Regexp.last_match(1)
    meth_name = Regexp.last_match(3)
    from_obj(target.eval(context), meth_name, target)
  elsif options[:instance]
    from_module(target.eval("self"), name, target)
  elsif options[:methods]
    from_obj(target.eval("self"), name, target)
  else
    from_str(name, target, instance: true) ||
      from_str(name, target, methods: true)
  end
rescue Pry::RescuableException
  nil
end
instance_method_definition?(name, definition_line) click to toggle source
# File lib/pry/method.rb, line 227
def instance_method_definition?(name, definition_line)
  regexp =
    /^define_method\(?\s*[:\"\']#{Regexp.escape(name)}|
     ^def\s*#{Regexp.escape(name)}/x
  regexp =~ definition_line.strip
end
instance_resolution_order(klass) click to toggle source

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. @param [Class, Module] klass @return [Array[Class, Module]]

# File lib/pry/method.rb, line 210
def instance_resolution_order(klass)
  # include klass in case it is a singleton class,
  ([klass] + Pry::Method.safe_send(klass, :ancestors)).uniq
end
lookup_method_via_binding( obj, method_name, method_type, target = TOPLEVEL_BINDING ) click to toggle source

In order to support 2.0 Refinements we need to look up methods inside the relevant Binding. @param [Object] obj The owner/receiver of the method. @param [Symbol] method_name The name of the method. @param [Symbol] method_type The type of method: :method or :instance_method @param [Binding] target The binding where the method is looked up. @return [Method, UnboundMethod] The 'refined' method object.

# File lib/pry/method.rb, line 114
def lookup_method_via_binding(
  obj, method_name, method_type, target = TOPLEVEL_BINDING
)
  Pry.current[:obj] = obj
  Pry.current[:name] = method_name
  receiver = obj.is_a?(Module) ? "Module" : "Kernel"
  target.eval(
    "::#{receiver}.instance_method(:#{method_type})" \
    ".bind(Pry.current[:obj]).call(Pry.current[:name])"
  )
ensure
  Pry.current[:obj] = Pry.current[:name] = nil
end
method_definition?(name, definition_line) click to toggle source
# File lib/pry/method.rb, line 215
def method_definition?(name, definition_line)
  singleton_method_definition?(name, definition_line) ||
    instance_method_definition?(name, definition_line)
end
new(method, known_info = {}) click to toggle source

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

@param [::Method, UnboundMethod, Proc] method @param [Hash] known_info Can be used to pre-cache expensive to compute stuff. @return [Pry::Method]

# File lib/pry/method.rb, line 263
def initialize(method, known_info = {})
  @method = method
  @visibility = known_info[:visibility]
end
resolution_order(obj) click to toggle source

Get every `Class` and `Module`, in order, that will be checked when looking for an instance method to call on this object. @param [Object] obj @return [Array[Class, Module]]

# File lib/pry/method.rb, line 192
def resolution_order(obj)
  if Class === obj # rubocop:disable Style/CaseEquality
    singleton_class_resolution_order(obj) + instance_resolution_order(Class)
  else
    klass = begin
              singleton_class_of(obj)
            rescue StandardError
              obj.class
            end
    instance_resolution_order(klass)
  end
end
singleton_class_of(obj) click to toggle source
# File lib/pry/method.rb, line 247
def singleton_class_of(obj)
  class << obj; self; end
rescue TypeError # can't define singleton. Fixnum, Symbol, Float, ...
  obj.class
end
singleton_class_resolution_order(klass) click to toggle source

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.

# File lib/pry/method.rb, line 238
def singleton_class_resolution_order(klass)
  ancestors = Pry::Method.safe_send(klass, :ancestors)
  resolution_order = ancestors.grep(Class).flat_map do |anc|
    [singleton_class_of(anc), *singleton_class_of(anc).included_modules]
  end

  resolution_order.reverse.uniq.reverse - Class.included_modules
end
singleton_method_definition?(name, definition_line) click to toggle source
# File lib/pry/method.rb, line 220
def singleton_method_definition?(name, definition_line)
  regexp =
    /^define_singleton_method\(?\s*[:\"\']#{Regexp.escape(name)}|
     ^def\s*self\.#{Regexp.escape(name)}/x
  regexp =~ definition_line.strip
end

Public Instance Methods

==(other) click to toggle source

@return [Boolean]

# File lib/pry/method.rb, line 483
def ==(other)
  return other == @method if other.is_a?(Pry::Method)

  @method == other
end
alias?() click to toggle source

@return [Boolean] Is the method definitely an alias?

# File lib/pry/method.rb, line 478
def alias?
  name != original_name
end
aliases() click to toggle source

@return [Array<String>] All known aliases for the method.

# File lib/pry/method.rb, line 461
def aliases
  owner = @method.owner
  # Avoid using `to_sym` on {Method#name}, which returns a `String`, because
  # it won't be garbage collected.
  name = @method.name

  all_methods_to_compare = owner.instance_methods | owner.private_instance_methods
  alias_list = all_methods_to_compare.combination(2).select do |pair|
    pair.include?(name) &&
      owner.instance_method(pair.first) == owner.instance_method(pair.last)
  end.flatten
  alias_list.delete(name)

  alias_list.map(&:to_s)
end
bound_method?() click to toggle source

@return [Boolean] Whether the method is bound.

# File lib/pry/method.rb, line 446
def bound_method?
  is_a?(::Method)
end
comment() click to toggle source
# File lib/pry/method.rb, line 515
def comment
  Pry::Code.from_file(source_file).comment_describing(source_line)
end
doc() click to toggle source

@return [String, nil] The documentation for the method, or `nil` if it's

unavailable.
# File lib/pry/method.rb, line 329
def doc
  @doc ||=
    case source_type
    when :c
      info = pry_doc_info
      info.docstring if info
    when :ruby
      get_comment_content(comment)
    end
end
dynamically_defined?() click to toggle source

@return [Boolean] Was the method defined outside a source file?

# File lib/pry/method.rb, line 436
def dynamically_defined?
  !!(source_file && source_file =~ /(\(.*\))|<.*>/)
end
is_a?(klass) click to toggle source

@param [Class] klass @return [Boolean]

# File lib/pry/method.rb, line 491
def is_a?(klass)
  (klass == Pry::Method) || @method.is_a?(klass)
end
Also aliased as: kind_of?
kind_of?(klass)
Alias for: is_a?
method_missing(method_name, *args, &block) click to toggle source

Delegate any unknown calls to the wrapped method.

Calls superclass method
# File lib/pry/method.rb, line 503
def method_missing(method_name, *args, &block)
  if @method.respond_to?(method_name)
    @method.__send__(method_name, *args, &block)
  else
    super
  end
end
name() click to toggle source

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

@return [String]

# File lib/pry/method.rb, line 272
def name
  @method.name.to_s
end
name_with_owner() click to toggle source

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

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

@return [String]

# File lib/pry/method.rb, line 299
def name_with_owner
  "#{wrapped_owner.method_prefix}#{name}"
end
original_name() click to toggle source

@return [String, nil] The original name the method was defined under,

before any aliasing, or `nil` if it can't be determined.
# File lib/pry/method.rb, line 429
def original_name
  return nil if source_type != :ruby

  method_name_from_first_line(source.lines.first)
end
pry_method?() click to toggle source

@return [Boolean] Was the method defined within the Pry REPL?

# File lib/pry/method.rb, line 456
def pry_method?
  source_file == Pry.eval_path
end
redefine(source) click to toggle source

Update the live copy of the method's source.

# File lib/pry/method.rb, line 314
def redefine(source)
  Patcher.new(self).patch_in_ram source
  Pry::Method(owner.instance_method(name))
end
respond_to?(method_name, include_all = false) click to toggle source

@param [String, Symbol] method_name @return [Boolean]

Calls superclass method
# File lib/pry/method.rb, line 498
def respond_to?(method_name, include_all = false)
  super || @method.respond_to?(method_name, include_all)
end
respond_to_missing?(method_name, include_private = false) click to toggle source
Calls superclass method
# File lib/pry/method.rb, line 511
def respond_to_missing?(method_name, include_private = false)
  @method.respond_to?(method_name) || super
end
signature() click to toggle source

@return [String] A representation of the method's signature, including its

name and parameters. Optional and "rest" parameters are marked with `*`
and block parameters with `&`. Keyword arguments are shown with `:`
If the parameter names are unavailable, they're given numbered names instead.
Paraphrased from `awesome_print` gem.
# File lib/pry/method.rb, line 391
def signature
  if respond_to?(:parameters)
    args = parameters.inject([]) do |args_array, (arg_type, name)|
      name ||= (arg_type == :block ? 'block' : "arg#{args_array.size + 1}")
      args_array.push(
        case arg_type
        when :req    then name.to_s
        when :opt    then "#{name}=?"
        when :rest   then "*#{name}"
        when :block  then "&#{name}"
        when :key    then "#{name}:?"
        when :keyreq 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
singleton_method?() click to toggle source

@return [Boolean] Whether the method is a singleton method.

# File lib/pry/method.rb, line 451
def singleton_method?
  wrapped_owner.singleton_class?
end
source() click to toggle source

@return [String, nil] The source code of the method, or `nil` if it's unavailable.

# File lib/pry/method.rb, line 304
def source
  @source ||= case source_type
              when :c
                c_source
              when :ruby
                ruby_source
              end
end
source?() click to toggle source

Can we get the source code for this method? @return [Boolean]

# File lib/pry/method.rb, line 321
def source?
  !!source
rescue MethodSource::SourceNotFoundError
  false
end
source_file() click to toggle source

@return [String, nil] The name of the file the method is defined in, or

`nil` if the filename is unavailable.
# File lib/pry/method.rb, line 348
def source_file
  if source_location.nil?
    if source_type == :c
      info = pry_doc_info
      info.file if info
    end
  else
    source_location.first
  end
end
source_line() click to toggle source

@return [Fixnum, nil] The line of code in `source_file` which begins

the method's definition, or `nil` if that information is unavailable.
# File lib/pry/method.rb, line 361
def source_line
  source_location.nil? ? nil : source_location.last
end
source_range() click to toggle source

@return [Range, nil] The range of lines in `source_file` which contain

the method's definition, or `nil` if that information is unavailable.
# File lib/pry/method.rb, line 367
def source_range
  source_location.nil? ? nil : (source_line)..(source_line + source.lines.count - 1)
end
source_type() click to toggle source

@return [Symbol] The source type of the method. The options are

`:ruby` for Ruby methods or `:c` for methods written in C.
# File lib/pry/method.rb, line 342
def source_type
  source_location.nil? ? :c : :ruby
end
super(times = 1) click to toggle source

@return [Pry::Method, nil] The wrapped method that is called when you

use "super" in the body of this method.
# File lib/pry/method.rb, line 417
def super(times = 1)
  if @method.is_a?(UnboundMethod)
    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
unbound_method?() click to toggle source

@return [Boolean] Whether the method is unbound.

# File lib/pry/method.rb, line 441
def unbound_method?
  is_a?(::UnboundMethod)
end
undefined?() click to toggle source

Is the method undefined? (aka `Disowned`) @return [Boolean] false

# File lib/pry/method.rb, line 290
def undefined?
  false
end
visibility() click to toggle source

@return [Symbol] The visibility of the method. May be `:public`,

`:protected`, or `:private`.
# File lib/pry/method.rb, line 373
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() click to toggle source

Get underlying object wrapped by this Pry::Method instance @return [Method, UnboundMethod, Proc]

# File lib/pry/method.rb, line 284
def wrapped
  @method
end
wrapped_owner() click to toggle source

Get the owner of the method as a Pry::Module @return [Pry::Module]

# File lib/pry/method.rb, line 278
def wrapped_owner
  @wrapped_owner ||= Pry::WrappedModule.new(owner)
end

Private Instance Methods

c_source() click to toggle source
# File lib/pry/method.rb, line 577
def c_source
  info = pry_doc_info
  strip_comments_from_c_code(info.source) if info && info.source
end
method_name_from_first_line(first_ln) click to toggle source

@param [String] first_ln The first line of a method definition. @return [String, nil]

# File lib/pry/method.rb, line 563
def method_name_from_first_line(first_ln)
  return nil if first_ln.strip !~ /^def /

  tokens = SyntaxHighlighter.tokenize(first_ln)
  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
pry_doc_info() click to toggle source

@return [YARD::CodeObjects::MethodObject] @raise [CommandError] when the method can't be found or `pry-doc` isn't installed.

# File lib/pry/method.rb, line 523
def pry_doc_info
  if defined?(PryDoc)
    Pry::MethodInfo.info_for(@method) ||
      raise(
        CommandError,
        "Cannot locate this method: #{name}. (source_location returns nil)"
      )
  else
    fail_msg = "Cannot locate this method: #{name}."
    if Helpers::Platform.mri?
      fail_msg += " Invoke the 'gem-install pry-doc' Pry command to get " \
                  "access to Ruby Core documentation.\n"
    end
    raise CommandError, fail_msg
  end
end
ruby_source() click to toggle source
# File lib/pry/method.rb, line 582
def ruby_source
  # Clone of `MethodSource.source_helper` that knows to use our
  # hacked version of `source_location` for our input buffer for methods
  # defined in `(pry)`.
  file, line = *source_location
  unless file
    raise SourceNotFoundError, "Could not locate source for #{name_with_owner}!"
  end

  begin
    code = Pry::Code.from_file(file).expression_at(line)
  rescue SyntaxError => e
    raise MethodSource::SourceNotFoundError, e.message
  end
  strip_leading_whitespace(code)
end
super_using_ancestors(ancestors, times = 1) click to toggle source

@param [Class, Module] ancestors The ancestors to investigate @return [Method] The unwrapped super-method

# File lib/pry/method.rb, line 542
def super_using_ancestors(ancestors, times = 1)
  next_owner = 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]) || (return nil)
  end

  begin
    safe_send(next_owner, :instance_method, name)
  rescue StandardError
    nil
  end
end