# File lib/pry/wrapped_module.rb, line 292 def primary_candidate @primary_candidate ||= candidates.find(&:file) || candidate(0) end
class Pry::WrappedModule
Attributes
Public Class Methods
Source
# 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
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")
Source
# 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
@raise [ArgumentError] if the argument is not a ‘Module` @param [Module] mod
Private Class Methods
Source
# 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
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]
Public Instance Methods
Source
# File lib/pry/wrapped_module.rb, line 239 def candidate(rank) @memoized_candidates[rank] ||= WrappedModule::Candidate.new(self, rank) end
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]
Source
# File lib/pry/wrapped_module.rb, line 250 def candidates enum = Enumerator.new do |y| (0...number_of_candidates).each do |num| y.yield candidate(num) end end enum end
@return [Array]
Source
# File lib/pry/wrapped_module.rb, line 126 def class? wrapped.instance_of?(Class) end
Is this strictly a class? @return [Boolean]
Source
# File lib/pry/wrapped_module.rb, line 76 def constants(inherit = true) Module.instance_method(:constants).bind(@wrapped).call(inherit) end
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?
Source
# File lib/pry/wrapped_module.rb, line 195 def doc @doc ||= primary_candidate.doc end
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.
Source
# File lib/pry/wrapped_module.rb, line 176 def file Array(source_location).first end
@return [String, nil] The associated file for the module (i.e
the primary candidate: highest ranked monkeypatch).
Source
# File lib/pry/wrapped_module.rb, line 183 def line Array(source_location).last end
@return [Fixnum, nil] The associated line for the module (i.e
the primary candidate: highest ranked monkeypatch).
Source
# 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
Forward method invocations to the wrapped module
Source
# 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
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
Source
# File lib/pry/wrapped_module.rb, line 120 def module? wrapped.instance_of?(Module) end
Is this strictly a module? (does not match classes) @return [Boolean]
Source
# File lib/pry/wrapped_module.rb, line 100 def nonblank_name if name.to_s == "" wrapped.inspect else name end end
The name of the Module if it has one, otherwise @return [String]
Source
# File lib/pry/wrapped_module.rb, line 245 def number_of_candidates method_candidates.count end
@return [Fixnum] The number of candidate definitions for the
current module.
Source
# 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
Source
# 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
Is this a singleton class? @return [Boolean]
Source
# 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
Get the instance associated with this singleton class.
@raise ArgumentError: tried to get instance of non singleton class
@return [Object]
Source
# File lib/pry/wrapped_module.rb, line 206 def source @source ||= primary_candidate.source end
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.
Source
# File lib/pry/wrapped_module.rb, line 168 def source_location @source_location ||= primary_candidate.source_location rescue Pry::RescuableException nil end
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.
Source
# File lib/pry/wrapped_module.rb, line 270 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
@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.
Source
# File lib/pry/wrapped_module.rb, line 223 def yard_doc YARD::Registry.at(name).docstring.to_s if yard_docs? end
@return [String] Return the YARD docs for this module.
Source
# File lib/pry/wrapped_module.rb, line 260 def yard_docs? !!(defined?(YARD) && YARD::Registry.at(name)) end
@return [Boolean] Whether YARD docs are available for this module.
Source
# File lib/pry/wrapped_module.rb, line 212 def yard_file YARD::Registry.at(name).file if yard_docs? end
@return [String] Return the associated file for the
module from YARD, if one exists.
Source
# File lib/pry/wrapped_module.rb, line 218 def yard_line YARD::Registry.at(name).line if yard_docs? end
@return [Fixnum] Return the associated line for the
module from YARD, if one exists.
Private Instance Methods
Source
# File lib/pry/wrapped_module.rb, line 347 def all_methods_for(mod) Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false) end
Return all methods (instance methods and class methods) for a given module. @return [Array<Pry::Method>]
Source
# File lib/pry/wrapped_module.rb, line 329 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
We only want methods that have a non-nil ‘source_location`. We also skip some spooky internal methods.
@return [Array<Pry::Method>]
Source
# File lib/pry/wrapped_module.rb, line 310 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
A helper method.
Source
# File lib/pry/wrapped_module.rb, line 371 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
memoized lines for file
Source
# File lib/pry/wrapped_module.rb, line 302 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
@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.
Source
# File lib/pry/wrapped_module.rb, line 366 def method_defined_by_forwardable_module?(method) method.source_location.first =~ /forwardable\.rb/ end
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.
Source
# File lib/pry/wrapped_module.rb, line 351 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
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 absence of a suitable candidate, the candidate of rank 0 will be returned, or a CommandError raised if there are no candidates at all.