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?, context_from_object_path, #context_from_object_path, 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?, #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

- (WrappedModule) initialize(mod)

Returns a new instance of WrappedModule

Raises:

  • (ArgumentError)

    if the argument is not a Module



58
59
60
61
62
63
64
65
66
# 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
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

- (Object) method_missing(method_name, *args, &block)

Forward method invocations to the wrapped module



143
144
145
# File 'lib/pry/wrapped_module.rb', line 143

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

Instance Attribute Details

- (Object) wrapped (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

+ (Module?) from_str(mod_name, target = TOPLEVEL_BINDING)

Convert a string to a module.

Examples:

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


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

+ (Boolean) safe_to_evaluate?(str, target) (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



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

- (Array<Pry::Method>) all_methods_for(mod) (private)

Return all methods (instance methods and class methods) for a given module.



341
342
343
# File 'lib/pry/wrapped_module.rb', line 341

def all_methods_for(mod)
  Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false)
end

- (Array<Pry::Method>) all_relevant_methods_for(mod) (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.)



323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/pry/wrapped_module.rb', line 323

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).map do |const_name|
    if const = nested_module?(mod, const_name)
      all_relevant_methods_for(const)
    else
      []
    end
  end.flatten
end

- (Object) all_source_locations_by_popularity (private)

A helper method.



306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/pry/wrapped_module.rb', line 306

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

- (Pry::WrappedModule::Candidate) candidate(rank)

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.

Raises:

  • (Pry::CommandError)

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



229
230
231
# File 'lib/pry/wrapped_module.rb', line 229

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

- (Enumerator, Array) candidates

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



245
246
247
248
249
250
251
252
# File 'lib/pry/wrapped_module.rb', line 245

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

- (Boolean) class?

Is this strictly a class?



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

def class?
  wrapped.instance_of?(Class)
end

- (Object) constants(inherit = true)

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.



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

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

- (String) doc

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.

Raises:



185
186
187
# File 'lib/pry/wrapped_module.rb', line 185

def doc
  @doc ||= primary_candidate.doc
end

- (String?) file Also known as: source_file



166
167
168
# File 'lib/pry/wrapped_module.rb', line 166

def file
  Array(source_location).first
end

- (Fixnum?) line Also known as: source_line



173
174
175
# File 'lib/pry/wrapped_module.rb', line 173

def line
  Array(source_location).last
end

- (Object) lines_for_file(file) (private)

memoized lines for file



363
364
365
366
367
368
369
370
371
# File 'lib/pry/wrapped_module.rb', line 363

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

- (Array<Array<Pry::Method>>) method_candidates (private)



298
299
300
301
302
303
# File 'lib/pry/wrapped_module.rb', line 298

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

- (Boolean) method_defined_by_forwardable_module?(method) (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.



358
359
360
# File 'lib/pry/wrapped_module.rb', line 358

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

- (Object) method_prefix

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

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



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

def method_prefix
  if singleton_class?
    if Module === singleton_instance
      "#{WrappedModule.new(singleton_instance).nonblank_name}."
    else
      "self."
    end
  else
    "#{nonblank_name}#"
  end
end

- (Boolean) module?

Is this strictly a module? (does not match classes)



117
118
119
# File 'lib/pry/wrapped_module.rb', line 117

def module?
  wrapped.instance_of?(Module)
end

- (Boolean) nested_module?(parent, name) (private)



345
346
347
348
349
350
351
# File 'lib/pry/wrapped_module.rb', line 345

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

- (String) nonblank_name

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



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

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

- (Fixnum) number_of_candidates



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

def number_of_candidates
  method_candidates.count
end

- (Pry::WrappedModule::Candidate) primary_candidate (private)



286
287
288
289
290
# File 'lib/pry/wrapped_module.rb', line 286

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

- (Boolean) respond_to?(method_name)



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

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

- (Boolean) singleton_class?

Is this a singleton class?



107
108
109
110
111
112
113
# File 'lib/pry/wrapped_module.rb', line 107

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

- (Object) singleton_instance

Get the instance associated with this singleton class.

Raises:

  • ArgumentError: tried to get instance of non singleton class



132
133
134
135
136
137
138
139
140
# File 'lib/pry/wrapped_module.rb', line 132

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

- (String) 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.

Raises:



196
197
198
# File 'lib/pry/wrapped_module.rb', line 196

def source
  @source ||= primary_candidate.source
end

- (Array<String, Fixnum>?) source_location

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.



158
159
160
161
162
# File 'lib/pry/wrapped_module.rb', line 158

def source_location
  @source_location ||= primary_candidate.source_location
rescue Pry::RescuableException
  nil
end

- (Pry::WrappedModule?) super(times = 1)



265
266
267
268
269
270
271
272
273
274
275
# File 'lib/pry/wrapped_module.rb', line 265

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

- (String) yard_doc



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

def yard_doc
  YARD::Registry.at(name).docstring.to_s if yard_docs?
end

- (Boolean) yard_docs?



255
256
257
# File 'lib/pry/wrapped_module.rb', line 255

def yard_docs?
  !!(defined?(YARD) && YARD::Registry.at(name))
end

- (String) yard_file



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

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

- (Fixnum) yard_line



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

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