Class: Pry::WrappedModule
- Includes:
- CodeObject::Helpers, Helpers::BaseHelpers
- Defined in:
- lib/pry/wrapped_module.rb,
lib/pry/wrapped_module/candidate.rb
Defined Under Namespace
Classes: Candidate
Instance Attribute Summary collapse
-
#wrapped ⇒ Object
readonly
Returns the value of attribute wrapped.
Class Method Summary collapse
-
.from_str(mod_name, target = TOPLEVEL_BINDING) ⇒ Module?
Convert a string to a module.
-
.safe_to_evaluate?(str, target) ⇒ Boolean
private
We use this method to decide whether code is safe to eval.
Instance Method Summary collapse
-
#all_methods_for(mod) ⇒ Array<Pry::Method>
private
Return all methods (instance methods and class methods) for a given module.
-
#all_relevant_methods_for(mod) ⇒ Array<Pry::Method>
private
We only want methods that have a non-nil
source_location
. -
#all_source_locations_by_popularity ⇒ Object
private
A helper method.
-
#candidate(rank) ⇒ Pry::WrappedModule::Candidate
Return a candidate for this module of specified rank.
- #candidates ⇒ Array
-
#class? ⇒ Boolean
Is this strictly a class?.
-
#constants(inherit = true) ⇒ Object
Returns an array of the names of the constants accessible in the wrapped module.
-
#doc ⇒ String
Returns documentation for the module.
-
#file ⇒ String?
(also: #source_file)
The associated file for the module (i.e the primary candidate: highest ranked monkeypatch).
-
#initialize(mod) ⇒ WrappedModule
constructor
A new instance of WrappedModule.
-
#line ⇒ Fixnum?
(also: #source_line)
The associated line for the module (i.e the primary candidate: highest ranked monkeypatch).
-
#lines_for_file(file) ⇒ Object
private
memoized lines for file.
-
#method_candidates ⇒ Array<Array<Pry::Method>>
private
The array of
Pry::Method
objects, there are two associated with each candidate. -
#method_defined_by_forwardable_module?(method) ⇒ Boolean
private
Detect methods that are defined with
def_delegator
from the Forwardable module. -
#method_missing(method_name, *args, &block) ⇒ Object
Forward method invocations to the wrapped module.
-
#method_prefix ⇒ Object
The prefix that would appear before methods defined on this class.
-
#module? ⇒ Boolean
Is this strictly a module? (does not match classes).
- #nested_module?(parent, name) ⇒ Boolean private
-
#nonblank_name ⇒ String
The name of the Module if it has one, otherwise #Class:0xf00.
-
#number_of_candidates ⇒ Fixnum
The number of candidate definitions for the current module.
-
#primary_candidate ⇒ Pry::WrappedModule::Candidate
private
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.
- #respond_to_missing?(method_name, include_private = false) ⇒ Boolean
-
#singleton_class? ⇒ Boolean
Is this a singleton class?.
-
#singleton_instance ⇒ Object
Get the instance associated with this singleton class.
-
#source ⇒ String
Returns the source for the module.
-
#source_location ⇒ Array<String, Fixnum>?
Retrieve the source location of a module.
-
#super(times = 1) ⇒ Pry::WrappedModule?
The wrapped module that is the superclass.
-
#yard_doc ⇒ String
Return the YARD docs for this module.
-
#yard_docs? ⇒ Boolean
Whether YARD docs are available for this module.
-
#yard_file ⇒ String
Return the associated file for the module from YARD, if one exists.
-
#yard_line ⇒ Fixnum
Return the associated line for the module from YARD, if one exists.
Methods included from CodeObject::Helpers
#c_method?, #c_module?, #command?, #module_with_yard_docs?, #real_method_object?
Methods included from Helpers::BaseHelpers
#colorize_code, #find_command, #heading, #highlight, #not_a_real_file?, #safe_send, #silence_warnings, #stagger_output, #use_ansi_codes?
Constructor Details
#initialize(mod) ⇒ WrappedModule
Returns a new instance of WrappedModule.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# 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 |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
Forward method invocations to the wrapped module
150 151 152 153 154 155 156 |
# 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 |
Instance Attribute Details
#wrapped ⇒ Object (readonly)
Returns the value of attribute wrapped.
20 21 22 |
# File 'lib/pry/wrapped_module.rb', line 20 def wrapped @wrapped end |
Class Method Details
.from_str(mod_name, target = TOPLEVEL_BINDING) ⇒ Module?
Convert a string to a module.
29 30 31 32 33 |
# 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 |
.safe_to_evaluate?(str, target) ⇒ Boolean (private)
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
45 46 47 48 49 50 51 |
# 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 |
Instance Method Details
#all_methods_for(mod) ⇒ Array<Pry::Method> (private)
Return all methods (instance methods and class methods) for a given module.
347 348 349 |
# 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 |
#all_relevant_methods_for(mod) ⇒ Array<Pry::Method> (private)
We only want methods that have a non-nil source_location
. We also
skip some spooky internal methods.
329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# 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 |
#all_source_locations_by_popularity ⇒ Object (private)
A helper method.
310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# 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| = File.(path) load_order = $LOADED_FEATURES.index { |file| .end_with?(file) } [-methods.size, load_order || (1.0 / 0.0)] end end |
#candidate(rank) ⇒ Pry::WrappedModule::Candidate
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.
239 240 241 |
# File 'lib/pry/wrapped_module.rb', line 239 def candidate(rank) @memoized_candidates[rank] ||= WrappedModule::Candidate.new(self, rank) end |
#candidates ⇒ Array
250 251 252 253 254 255 256 257 |
# 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 |
#class? ⇒ Boolean
Is this strictly a class?
126 127 128 |
# File 'lib/pry/wrapped_module.rb', line 126 def class? wrapped.instance_of?(Class) end |
#constants(inherit = true) ⇒ Object
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
.
76 77 78 |
# File 'lib/pry/wrapped_module.rb', line 76 def constants(inherit = true) Module.instance_method(:constants).bind(@wrapped).call(inherit) end |
#doc ⇒ String
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.
195 196 197 |
# File 'lib/pry/wrapped_module.rb', line 195 def doc @doc ||= primary_candidate.doc end |
#file ⇒ String? Also known as: source_file
Returns The associated file for the module (i.e the primary candidate: highest ranked monkeypatch).
176 177 178 |
# File 'lib/pry/wrapped_module.rb', line 176 def file Array(source_location).first end |
#line ⇒ Fixnum? Also known as: source_line
Returns The associated line for the module (i.e the primary candidate: highest ranked monkeypatch).
183 184 185 |
# File 'lib/pry/wrapped_module.rb', line 183 def line Array(source_location).last end |
#lines_for_file(file) ⇒ Object (private)
memoized lines for file
371 372 373 374 375 376 377 378 379 380 |
# 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 |
#method_candidates ⇒ Array<Array<Pry::Method>> (private)
Returns 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.
302 303 304 305 306 307 |
# 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 |
#method_defined_by_forwardable_module?(method) ⇒ Boolean (private)
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.
366 367 368 |
# File 'lib/pry/wrapped_module.rb', line 366 def method_defined_by_forwardable_module?(method) method.source_location.first =~ /forwardable\.rb/ end |
#method_prefix ⇒ Object
The prefix that would appear before methods defined on this class.
i.e. the "String." or "String#" in String.new and String#initialize.
85 86 87 88 89 90 91 92 93 94 95 |
# 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? ⇒ Boolean
Is this strictly a module? (does not match classes)
120 121 122 |
# File 'lib/pry/wrapped_module.rb', line 120 def module? wrapped.instance_of?(Module) end |
#nested_module?(parent, name) ⇒ Boolean (private)
351 352 353 354 355 356 357 358 359 |
# 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 |
#nonblank_name ⇒ String
The name of the Module if it has one, otherwise #Class:0xf00.
100 101 102 103 104 105 106 |
# File 'lib/pry/wrapped_module.rb', line 100 def nonblank_name if name.to_s == "" wrapped.inspect else name end end |
#number_of_candidates ⇒ Fixnum
Returns The number of candidate definitions for the current module.
245 246 247 |
# File 'lib/pry/wrapped_module.rb', line 245 def number_of_candidates method_candidates.count end |
#primary_candidate ⇒ Pry::WrappedModule::Candidate (private)
Returns 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.
292 293 294 |
# File 'lib/pry/wrapped_module.rb', line 292 def primary_candidate @primary_candidate ||= candidates.find(&:file) || candidate(0) end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
158 159 160 |
# 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? ⇒ Boolean
Is this a singleton class?
110 111 112 113 114 115 116 |
# 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 ⇒ Object
Get the instance associated with this singleton class.
135 136 137 138 139 140 141 142 143 144 145 146 147 |
# 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 ⇒ String
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.
206 207 208 |
# File 'lib/pry/wrapped_module.rb', line 206 def source @source ||= primary_candidate.source end |
#source_location ⇒ Array<String, Fixnum>?
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
.
168 169 170 171 172 |
# 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) ⇒ Pry::WrappedModule?
Returns 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.
270 271 272 273 274 275 276 277 278 279 280 281 |
# 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 |
#yard_doc ⇒ String
Return the YARD docs for this module.
223 224 225 |
# File 'lib/pry/wrapped_module.rb', line 223 def yard_doc YARD::Registry.at(name).docstring.to_s if yard_docs? end |
#yard_docs? ⇒ Boolean
Returns Whether YARD docs are available for this module.
260 261 262 |
# File 'lib/pry/wrapped_module.rb', line 260 def yard_docs? !!(defined?(YARD) && YARD::Registry.at(name)) end |
#yard_file ⇒ String
Return the associated file for the module from YARD, if one exists.
212 213 214 |
# File 'lib/pry/wrapped_module.rb', line 212 def yard_file YARD::Registry.at(name).file if yard_docs? end |
#yard_line ⇒ Fixnum
Return the associated line for the module from YARD, if one exists.
218 219 220 |
# File 'lib/pry/wrapped_module.rb', line 218 def yard_line YARD::Registry.at(name).line if yard_docs? end |