class Pry::WrappedModule

Attributes

wrapped[R]

Public Class Methods

from_str(mod_name, target = TOPLEVEL_BINDING) click to toggle source

Convert a string to a module.

@param [String] mod_name @param [Binding] target The binding where the lookup takes place. @return [Module, nil] The module or `nil` (if conversion failed). @example

Pry::WrappedModule.from_str("Pry::Code")
# File lib/pry/wrapped_module.rb, line 29
def self.from_str(mod_name, target = TOPLEVEL_BINDING)
  Pry::WrappedModule.new(target.eval(mod_name)) if safe_to_evaluate?(mod_name, target)
rescue RescuableException
  nil
end
new(mod) click to toggle source

@raise [ArgumentError] if the argument is not a `Module` @param [Module] mod

# File lib/pry/wrapped_module.rb, line 56
def initialize(mod)
  unless mod.is_a?(Module)
    raise ArgumentError, "Tried to initialize a WrappedModule with a " \
                         "non-module #{mod.inspect}"
  end

  @wrapped = mod
  @memoized_candidates = []
  @host_file_lines = nil
  @source = nil
  @source_location = nil
  @doc = nil
  @all_source_locations_by_popularity = nil
end

Private Class Methods

safe_to_evaluate?(str, target) click to toggle source

We use this method to decide whether code is safe to eval. Method's are generally not, but everything else is. TODO: is just checking != “method” enough?? TODO: see duplication of this method in Pry::CodeObject @param [String] str The string to lookup. @param [Binding] target Where the lookup takes place. @return [Boolean]

# File lib/pry/wrapped_module.rb, line 45
def safe_to_evaluate?(str, target)
  return true if str.strip == "self"
  return false if str =~ /%/

  kind = target.eval("defined?(#{str})")
  kind =~ /variable|constant/
end

Public Instance Methods

candidate(rank) click to toggle source

Return a candidate for this module of specified rank. A `rank` of 0 is equivalent to the 'primary candidate', which is the module definition with the highest number of methods. A `rank` of 1 is the module definition with the second highest number of methods, and so on. Module candidates are necessary as modules can be reopened multiple times and in multiple places in Ruby, the candidate API gives you access to the module definition representing each of those reopenings. @raise [Pry::CommandError] If the `rank` is out of range. That

is greater than `number_of_candidates - 1`.

@param [Fixnum] rank @return [Pry::WrappedModule::Candidate]

# File lib/pry/wrapped_module.rb, line 239
def candidate(rank)
  @memoized_candidates[rank] ||= WrappedModule::Candidate.new(self, rank)
end
candidates() click to toggle source

@note On JRuby 1.9 and higher, in certain conditions, this method chucks

away its ability to be quick (when there are lots of monkey patches,
like in Rails). However, it should be efficient enough on other rubies.

@see github.com/jruby/jruby/issues/525 @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on

other rubies returns Enumerator
# File lib/pry/wrapped_module.rb, line 255
def candidates
  enum = Enumerator.new do |y|
    (0...number_of_candidates).each do |num|
      y.yield candidate(num)
    end
  end
  Helpers::Platform.jruby_19? ? enum.to_a : enum
end
class?() click to toggle source

Is this strictly a class? @return [Boolean]

# File lib/pry/wrapped_module.rb, line 126
def class?
  wrapped.instance_of?(Class)
end
constants(inherit = true) click to toggle source

Returns an array of the names of the constants accessible in the wrapped module. This avoids the problem of accidentally calling the singleton method `Module.constants`. @param [Boolean] inherit Include the names of constants from included

modules?
# File lib/pry/wrapped_module.rb, line 76
def constants(inherit = true)
  Module.instance_method(:constants).bind(@wrapped).call(inherit)
end
doc() click to toggle source

Returns documentation for the module. This documentation is for the primary candidate, if you would like documentation for other candidates use `WrappedModule#candidate` to select the candidate you're interested in. @raise [Pry::CommandError] If documentation cannot be found. @return [String] The documentation for the module.

# File lib/pry/wrapped_module.rb, line 195
def doc
  @doc ||= primary_candidate.doc
end
file() click to toggle source

@return [String, nil] The associated file for the module (i.e

the primary candidate: highest ranked monkeypatch).
# File lib/pry/wrapped_module.rb, line 176
def file
  Array(source_location).first
end
Also aliased as: source_file
line() click to toggle source

@return [Fixnum, nil] The associated line for the module (i.e

the primary candidate: highest ranked monkeypatch).
# File lib/pry/wrapped_module.rb, line 183
def line
  Array(source_location).last
end
Also aliased as: source_line
method_missing(method_name, *args, &block) click to toggle source

Forward method invocations to the wrapped module

Calls superclass method
# File lib/pry/wrapped_module.rb, line 150
def method_missing(method_name, *args, &block)
  if wrapped.respond_to?(method_name)
    wrapped.send(method_name, *args, &block)
  else
    super
  end
end
method_prefix() click to toggle source

The prefix that would appear before methods defined on this class.

i.e. the “String.” or “String#” in String.new and String#initialize.

@return String

# File lib/pry/wrapped_module.rb, line 85
def method_prefix
  if singleton_class?
    if Module === singleton_instance # rubocop:disable Style/CaseEquality
      "#{WrappedModule.new(singleton_instance).nonblank_name}."
    else
      "self."
    end
  else
    "#{nonblank_name}#"
  end
end
module?() click to toggle source

Is this strictly a module? (does not match classes) @return [Boolean]

# File lib/pry/wrapped_module.rb, line 120
def module?
  wrapped.instance_of?(Module)
end
nonblank_name() click to toggle source

The name of the Module if it has one, otherwise #<Class:0xf00>.

@return [String]

# File lib/pry/wrapped_module.rb, line 100
def nonblank_name
  if name.to_s == ""
    wrapped.inspect
  else
    name
  end
end
number_of_candidates() click to toggle source

@return [Fixnum] The number of candidate definitions for the

current module.
# File lib/pry/wrapped_module.rb, line 245
def number_of_candidates
  method_candidates.count
end
respond_to_missing?(method_name, include_private = false) click to toggle source
Calls superclass method
# File lib/pry/wrapped_module.rb, line 158
def respond_to_missing?(method_name, include_private = false)
  wrapped.respond_to?(method_name, include_private) || super
end
singleton_class?() click to toggle source

Is this a singleton class? @return [Boolean]

# File lib/pry/wrapped_module.rb, line 110
def singleton_class?
  if Pry::Method.safe_send(wrapped, :respond_to?, :singleton_class?)
    Pry::Method.safe_send(wrapped, :singleton_class?)
  else
    wrapped != Pry::Method.safe_send(wrapped, :ancestors).first
  end
end
singleton_instance() click to toggle source

Get the instance associated with this singleton class.

@raise ArgumentError: tried to get instance of non singleton class

@return [Object]

# File lib/pry/wrapped_module.rb, line 135
def singleton_instance
  unless singleton_class?
    raise ArgumentError, "tried to get instance of non singleton class"
  end

  if Helpers::Platform.jruby?
    wrapped.to_java.attached
  else
    @singleton_instance ||= ObjectSpace.each_object(wrapped).detect do |x|
      (class << x; self; end) == wrapped
    end
  end
end
source() click to toggle source

Returns the source for the module. This source is for the primary candidate, if you would like source for other candidates use `WrappedModule#candidate` to select the candidate you're interested in. @raise [Pry::CommandError] If source cannot be found. @return [String] The source for the module.

# File lib/pry/wrapped_module.rb, line 206
def source
  @source ||= primary_candidate.source
end
source_file()
Alias for: file
source_line()
Alias for: line
source_location() click to toggle source

Retrieve the source location of a module. Return value is in same format as Method#source_location. If the source location cannot be found this method returns `nil`.

@return [Array<String, Fixnum>, nil] The source location of the

module (or class), or `nil` if no source location found.
# File lib/pry/wrapped_module.rb, line 168
def source_location
  @source_location ||= primary_candidate.source_location
rescue Pry::RescuableException
  nil
end
super(times = 1) click to toggle source

@param [Fixnum] times How far to travel up the ancestor chain. @return [Pry::WrappedModule, nil] The wrapped module that is the

superclass.
When `self` is a `Module` then return the
nth ancestor, otherwise (in the case of classes) return the
nth ancestor that is a class.
# File lib/pry/wrapped_module.rb, line 275
def super(times = 1)
  return self if times.zero?

  sup =
    if wrapped.is_a?(Class)
      ancestors.select { |v| v.is_a?(Class) }[times]
    else
      ancestors[times]
    end

  Pry::WrappedModule(sup) if sup
end
yard_doc() click to toggle source

@return [String] Return the YARD docs for this module.

# File lib/pry/wrapped_module.rb, line 223
def yard_doc
  YARD::Registry.at(name).docstring.to_s if yard_docs?
end
yard_docs?() click to toggle source

@return [Boolean] Whether YARD docs are available for this module.

# File lib/pry/wrapped_module.rb, line 265
def yard_docs?
  !!(defined?(YARD) && YARD::Registry.at(name))
end
yard_file() click to toggle source

@return [String] Return the associated file for the

module from YARD, if one exists.
# File lib/pry/wrapped_module.rb, line 212
def yard_file
  YARD::Registry.at(name).file if yard_docs?
end
yard_line() click to toggle source

@return [Fixnum] Return the associated line for the

module from YARD, if one exists.
# File lib/pry/wrapped_module.rb, line 218
def yard_line
  YARD::Registry.at(name).line if yard_docs?
end

Private Instance Methods

all_methods_for(mod) click to toggle source

Return all methods (instance methods and class methods) for a given module. @return [Array<Pry::Method>]

# File lib/pry/wrapped_module.rb, line 352
def all_methods_for(mod)
  Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false)
end
all_relevant_methods_for(mod) click to toggle source

We only want methods that have a non-nil `source_location`. We also skip some spooky internal methods.

@return [Array<Pry::Method>]

# File lib/pry/wrapped_module.rb, line 334
def all_relevant_methods_for(mod)
  methods = all_methods_for(mod).select(&:source_location)
    .reject { |x| method_defined_by_forwardable_module?(x) }

  return methods unless methods.empty?

  safe_send(mod, :constants).flat_map do |const_name|
    if (const = nested_module?(mod, const_name))
      all_relevant_methods_for(const)
    else
      []
    end
  end
end
all_source_locations_by_popularity() click to toggle source

A helper method.

# File lib/pry/wrapped_module.rb, line 315
def all_source_locations_by_popularity
  return @all_source_locations_by_popularity if @all_source_locations_by_popularity

  ims = all_relevant_methods_for(wrapped).group_by do |v|
    Array(v.source_location).first
  end

  @all_source_locations_by_popularity = ims.sort_by do |path, methods|
    expanded = File.expand_path(path)
    load_order = $LOADED_FEATURES.index { |file| expanded.end_with?(file) }

    [-methods.size, load_order || (1.0 / 0.0)]
  end
end
lines_for_file(file) click to toggle source

memoized lines for file

# File lib/pry/wrapped_module.rb, line 376
def lines_for_file(file)
  @lines_for_file ||= {}

  @lines_for_file[file] ||=
    if file == Pry.eval_path
      Pry.line_buffer.drop(1)
    else
      File.readlines(file)
    end
end
method_candidates() click to toggle source

@return [Array<Array<Pry::Method>>] The array of `Pry::Method` objects,

there are two associated with each candidate. The first is the 'base
method' for a candidate and it serves as the start point for
the search in  uncovering the module definition. The second is
the last method defined for that candidate and it is used to
speed up source code extraction.
# File lib/pry/wrapped_module.rb, line 307
def method_candidates
  @method_candidates ||= all_source_locations_by_popularity.map do |group|
    methods_sorted_by_source_line = group.last.sort_by(&:source_line)
    [methods_sorted_by_source_line.first, methods_sorted_by_source_line.last]
  end
end
method_defined_by_forwardable_module?(method) click to toggle source

Detect methods that are defined with `def_delegator` from the Forwardable module. We want to reject these methods as they screw up module extraction since the `source_location` for such methods points at forwardable.rb TODO: make this more robust as valid user-defined files called forwardable.rb are also skipped.

# File lib/pry/wrapped_module.rb, line 371
def method_defined_by_forwardable_module?(method)
  method.source_location.first =~ /forwardable\.rb/
end
nested_module?(parent, name) click to toggle source
# File lib/pry/wrapped_module.rb, line 356
def nested_module?(parent, name)
  return if safe_send(parent, :autoload?, name)

  child = safe_send(parent, :const_get, name)
  return unless child.is_a?(Module)
  return unless safe_send(child, :name) == "#{safe_send(parent, :name)}::#{name}"

  child
end
primary_candidate() click to toggle source

@return [Pry::WrappedModule::Candidate] The candidate with the

highest rank, that is the 'monkey patch' of this module with the
highest number of methods, which contains a source code line that
defines the module. It is considered the 'canonical' definition
for the module. In the absense of a suitable candidate, the
candidate of rank 0 will be returned, or a CommandError raised if
there are no candidates at all.
# File lib/pry/wrapped_module.rb, line 297
def primary_candidate
  @primary_candidate ||= candidates.find(&:file) || candidate(0)
end