Class: Pry::WrappedModule

Inherits:
Object show all
Includes:
CodeObject::Helpers, Helpers::BaseHelpers
Defined in:
lib/pry/wrapped_module.rb,
lib/pry/module_candidate.rb

Defined Under Namespace

Classes: Candidate

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CodeObject::Helpers

#c_method?, #command?, #module_with_yard_docs?, #real_method_object?

Methods included from Helpers::BaseHelpers

#colorize_code, colorize_code, command_dependencies_met?, #command_dependencies_met?, #find_command, find_command, #heading, heading, highlight, #highlight, jruby?, #jruby?, #jruby_19?, jruby_19?, mri?, #mri?, #mri_19?, mri_19?, #mri_20?, mri_20?, #mri_21?, mri_21?, mri_2?, #mri_2?, #not_a_real_file?, not_a_real_file?, #rbx?, rbx?, #safe_send, safe_send, silence_warnings, #silence_warnings, #stagger_output, stagger_output, use_ansi_codes?, #use_ansi_codes?, windows?, #windows?, windows_ansi?, #windows_ansi?

Constructor Details

#initialize(mod) ⇒ WrappedModule

Returns a new instance of WrappedModule

Parameters:

  • mod (Module)

Raises:

  • (ArgumentError)

    if the argument is not a Module



58
59
60
61
62
63
64
65
66
67
# File 'lib/pry/wrapped_module.rb', line 58

def initialize(mod)
  raise ArgumentError, "Tried to initialize a WrappedModule with a non-module #{mod.inspect}" unless ::Module === mod
  @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



148
149
150
# File 'lib/pry/wrapped_module.rb', line 148

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

Instance Attribute Details

#wrappedObject (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.

Examples:

Pry::WrappedModule.from_str("Pry::Code")

Parameters:

  • mod_name (String)
  • target (Binding) (defaults to: TOPLEVEL_BINDING)

    The binding where the lookup takes place.

Returns:

  • (Module, nil)

    The module or nil (if conversion failed).



29
30
31
32
33
34
35
36
37
# File 'lib/pry/wrapped_module.rb', line 29

def self.from_str(mod_name, target=TOPLEVEL_BINDING)
  if safe_to_evaluate?(mod_name, target)
    Pry::WrappedModule.new(target.eval(mod_name))
  else
    nil
  end
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

Parameters:

  • str (String)

    The string to lookup.

  • target (Binding)

    Where the lookup takes place.

Returns:

  • (Boolean)


49
50
51
52
53
# File 'lib/pry/wrapped_module.rb', line 49

def safe_to_evaluate?(str, target)
  return true if str.strip == "self"
  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.

Returns:



346
347
348
# File 'lib/pry/wrapped_module.rb', line 346

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. (i.e we skip __class_init__ because it's an odd rbx specific thing that causes tests to fail.)

Returns:



328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/pry/wrapped_module.rb', line 328

def all_relevant_methods_for(mod)
  methods = all_methods_for(mod).select(&:source_location).
    reject{ |x| x.name == '__class_init__' || 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_popularityObject (private)

A helper method.



311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/pry/wrapped_module.rb', line 311

def all_source_locations_by_popularity
  return @all_source_locations_by_popularity if @all_source_locations_by_popularity

  ims = all_relevant_methods_for(wrapped)
  @all_source_locations_by_popularity = ims.group_by { |v| Array(v.source_location).first }.
    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

#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.

Parameters:

  • rank (Fixnum)

Returns:

Raises:

  • (Pry::CommandError)

    If the rank is out of range. That is greater than number_of_candidates - 1.



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

def candidate(rank)
  @memoized_candidates[rank] ||= Candidate.new(self, rank)
end

#candidatesEnumerator, Array

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.

Returns on JRuby 1.9 and higher returns Array, on other rubies returns Enumerator

Returns:

  • (Enumerator, Array)

    on JRuby 1.9 and higher returns Array, on other rubies returns Enumerator

See Also:



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
  Pry::Helpers::BaseHelpers.jruby_19? ? enum.to_a : enum
end

#class?Boolean

Is this strictly a class?

Returns:

  • (Boolean)


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

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.

Parameters:

  • inherit (Boolean) (defaults to: true)

    Include the names of constants from included modules?



74
75
76
# File 'lib/pry/wrapped_module.rb', line 74

def constants(inherit = true)
  Module.instance_method(:constants).bind(@wrapped).call(inherit)
end

#docString

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.

Returns:

  • (String)

    The documentation for the module.

Raises:



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

def doc
  @doc ||= primary_candidate.doc
end

#fileString? Also known as: source_file

Returns The associated file for the module (i.e the primary candidate: highest ranked monkeypatch).

Returns:

  • (String, nil)

    The associated file for the module (i.e the primary candidate: highest ranked monkeypatch).



171
172
173
# File 'lib/pry/wrapped_module.rb', line 171

def file
  Array(source_location).first
end

#lineFixnum? Also known as: source_line

Returns The associated line for the module (i.e the primary candidate: highest ranked monkeypatch).

Returns:

  • (Fixnum, nil)

    The associated line for the module (i.e the primary candidate: highest ranked monkeypatch).



178
179
180
# File 'lib/pry/wrapped_module.rb', line 178

def line
  Array(source_location).last
end

#lines_for_file(file) ⇒ Object (private)

memoized lines for file



368
369
370
371
372
373
374
375
376
# File 'lib/pry/wrapped_module.rb', line 368

def lines_for_file(file)
  @lines_for_file ||= {}

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

#method_candidatesArray<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.

Returns:

  • (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.



303
304
305
306
307
308
# File 'lib/pry/wrapped_module.rb', line 303

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.

Returns:

  • (Boolean)


363
364
365
# File 'lib/pry/wrapped_module.rb', line 363

def method_defined_by_forwardable_module?(method)
  method.source_location.first =~ /forwardable\.rb/
end

#method_prefixObject

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

i.e. the "String." or "String#" in String.new and String#initialize.

Returns:

  • String



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/pry/wrapped_module.rb', line 83

def method_prefix
  if singleton_class?
    if Module === singleton_instance
      "#{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)

Returns:

  • (Boolean)


122
123
124
# File 'lib/pry/wrapped_module.rb', line 122

def module?
  wrapped.instance_of?(Module)
end

#nested_module?(parent, name) ⇒ Boolean (private)

Returns:

  • (Boolean)


350
351
352
353
354
355
356
# File 'lib/pry/wrapped_module.rb', line 350

def nested_module?(parent, name)
  return if safe_send(parent, :autoload?, name)
  child = safe_send(parent, :const_get, name)
  return unless Module === child
  return unless safe_send(child, :name) == "#{safe_send(parent, :name)}::#{name}"
  child
end

#nonblank_nameString

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

Returns:

  • (String)


98
99
100
101
102
103
104
# File 'lib/pry/wrapped_module.rb', line 98

def nonblank_name
  if name.to_s == ""
    wrapped.inspect
  else
    name
  end
end

#number_of_candidatesFixnum

Returns The number of candidate definitions for the current module.

Returns:

  • (Fixnum)

    The number of candidate definitions for the current module.



240
241
242
# File 'lib/pry/wrapped_module.rb', line 240

def number_of_candidates
  method_candidates.count
end

#primary_candidatePry::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.

Returns:

  • (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.



291
292
293
294
295
# File 'lib/pry/wrapped_module.rb', line 291

def primary_candidate
  @primary_candidate ||= candidates.find { |c| c.file } ||
    # This will raise an exception if there is no candidate at all.
    candidate(0)
end

#respond_to?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/pry/wrapped_module.rb', line 152

def respond_to?(method_name)
  super || wrapped.respond_to?(method_name)
end

#singleton_class?Boolean

Is this a singleton class?

Returns:

  • (Boolean)


108
109
110
111
112
113
114
115
116
117
118
# File 'lib/pry/wrapped_module.rb', line 108

def singleton_class?
  if Pry::Method.safe_send(wrapped, :respond_to?, :singleton_class?)
    Pry::Method.safe_send(wrapped, :singleton_class?)
  elsif defined?(Rubinius)
    # https://github.com/rubinius/rubinius/commit/2e71722dba53d1a92c54d5e3968d64d1042486fe singleton_class? added 30 Jul 2014
    # https://github.com/rubinius/rubinius/commit/4310f6b2ef3c8fc88135affe697db4e29e4621c4 has been around since 2011
    !!Rubinius::Type.singleton_class_object(wrapped)
  else
    wrapped != Pry::Method.safe_send(wrapped, :ancestors).first
  end
end

#singleton_instanceObject

Get the instance associated with this singleton class.

Returns:

Raises:

  • ArgumentError: tried to get instance of non singleton class



137
138
139
140
141
142
143
144
145
# File 'lib/pry/wrapped_module.rb', line 137

def singleton_instance
  raise ArgumentError, "tried to get instance of non singleton class" unless singleton_class?

  if Helpers::BaseHelpers.jruby?
    wrapped.to_java.attached
  else
    @singleton_instance ||= ObjectSpace.each_object(wrapped).detect{ |x| (class << x; self; end) == wrapped }
  end
end

#sourceString

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.

Returns:

  • (String)

    The source for the module.

Raises:



201
202
203
# File 'lib/pry/wrapped_module.rb', line 201

def source
  @source ||= primary_candidate.source
end

#source_locationArray<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.

Parameters:

  • mod (Module)

    The module (or class).

Returns:

  • (Array<String, Fixnum>, nil)

    The source location of the module (or class), or nil if no source location found.



163
164
165
166
167
# File 'lib/pry/wrapped_module.rb', line 163

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.

Parameters:

  • times (Fixnum) (defaults to: 1)

    How far to travel up the ancestor chain.

Returns:

  • (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.



270
271
272
273
274
275
276
277
278
279
280
# File 'lib/pry/wrapped_module.rb', line 270

def super(times=1)
  return self if times.zero?

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

  Pry::WrappedModule(sup) if sup
end

#yard_docString

Return the YARD docs for this module.

Returns:

  • (String)

    Return the YARD docs for this module.



218
219
220
# File 'lib/pry/wrapped_module.rb', line 218

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.

Returns:

  • (Boolean)

    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_fileString

Return the associated file for the module from YARD, if one exists.

Returns:

  • (String)

    Return the associated file for the module from YARD, if one exists.



207
208
209
# File 'lib/pry/wrapped_module.rb', line 207

def yard_file
  YARD::Registry.at(name).file if yard_docs?
end

#yard_lineFixnum

Return the associated line for the module from YARD, if one exists.

Returns:

  • (Fixnum)

    Return the associated line for the module from YARD, if one exists.



213
214
215
# File 'lib/pry/wrapped_module.rb', line 213

def yard_line
  YARD::Registry.at(name).line if yard_docs?
end