Class: Pry::WrappedModule

Inherits:
Object show all
Includes:
Helpers::DocumentationHelpers
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 Helpers::DocumentationHelpers

#process_comment_markup, #process_rdoc, #process_yardoc, #process_yardoc_tag, #strip_comments_from_c_code, #strip_leading_hash_and_whitespace_from_ruby_comments, #strip_leading_whitespace

Constructor Details

#initialize(mod) ⇒ WrappedModule

Returns a new instance of WrappedModule.

Parameters:

  • mod (Module)

Raises:

  • (ArgumentError)

    if the argument is not a Module



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

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

#method_missing(method_name, *args, &block) ⇒ Object

Forward method invocations to the wrapped module



105
106
107
# File 'lib/pry/wrapped_module.rb', line 105

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

Instance Attribute Details

#wrappedObject (readonly, private)

Returns the value of attribute wrapped.



19
20
21
# File 'lib/pry/wrapped_module.rb', line 19

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
38
39
40
41
# File 'lib/pry/wrapped_module.rb', line 29

def self.from_str(mod_name, target=TOPLEVEL_BINDING)
  kind = target.eval("defined?(#{mod_name})")

  # if we dont limit it to constants then from_str could end up
  # executing methods which is not good, i.e `show-source pry`
  if (kind == "constant" && target.eval(mod_name).is_a?(Module))
    Pry::WrappedModule.new(target.eval(mod_name))
  else
    nil
  end
rescue RescuableException
  nil
end

Instance Method Details

#all_from_common(mod, method_type) ⇒ Object (private)

FIXME: a variant of this method is also found in Pry::Method



250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/pry/wrapped_module.rb', line 250

def all_from_common(mod, method_type)
  %w(public protected private).map do |visibility|
    safe_send(mod, :"#{visibility}_#{method_type}s", false).select do |method_name|
      if method_type == :method
        safe_send(mod, method_type, method_name).owner == class << mod; self; end
      else
        safe_send(mod, method_type, method_name).owner == mod
      end
    end.map do |method_name|
      Pry::Method.new(safe_send(mod, method_type, method_name), :visibility => visibility.to_sym)
    end
  end.flatten
end

#all_methods_for(mod) ⇒ Object (private)

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



245
246
247
# File 'lib/pry/wrapped_module.rb', line 245

def all_methods_for(mod)
  all_from_common(mod, :instance_method) + all_from_common(mod, :method)
end

#all_source_locations_by_popularityObject (private)

A helper method.



231
232
233
234
235
236
237
238
239
240
241
# File 'lib/pry/wrapped_module.rb', line 231

def all_source_locations_by_popularity
  return @all_source_locations_by_popularity if @all_source_locations_by_popularity

  ims = all_methods_for(wrapped)

  # reject __class_init__ because it's an odd rbx specific thing that causes tests to fail
  ims = ims.select(&:source_location).reject{ |x| x.name == '__class_init__' }

  @all_source_locations_by_popularity = ims.group_by { |v| Array(v.source_location).first }.
    sort_by { |k, v| -v.size }
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.



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

def candidate(rank)
  @memoized_candidates[rank] ||= Candidate.new(self, rank)
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:



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

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



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

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



135
136
137
# File 'lib/pry/wrapped_module.rb', line 135

def line
  Array(source_location).last
end

#lines_for_file(file) ⇒ Object (private)

memoized lines for file



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

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.



223
224
225
226
227
228
# File 'lib/pry/wrapped_module.rb', line 223

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_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



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/pry/wrapped_module.rb', line 60

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

#nonblank_nameString

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

Returns:

  • (String)


75
76
77
78
79
80
81
# File 'lib/pry/wrapped_module.rb', line 75

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.



198
199
200
# File 'lib/pry/wrapped_module.rb', line 198

def number_of_candidates
  method_candidates.count
end

#primary_candidatePry::WrappedModule::Candidate (private)

Returns The candidate of rank 0, that is the 'monkey patch' of this module with the highest number of methods. It is considered the 'canonical' definition for the module.

Returns:

  • (Pry::WrappedModule::Candidate)

    The candidate of rank 0, that is the 'monkey patch' of this module with the highest number of methods. It is considered the 'canonical' definition for the module.



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

def primary_candidate
  @primary_candidate ||= candidate(0)
end

#process_doc(doc) ⇒ String (private)

Returns Process docstring markup and strip leading white space.

Parameters:

  • doc (String)

    The raw docstring to process.

Returns:

  • (String)

    Process docstring markup and strip leading white space.



282
283
284
# File 'lib/pry/wrapped_module.rb', line 282

def process_doc(doc)
   process_comment_markup(strip_leading_hash_and_whitespace_from_ruby_comments(doc))
end

#respond_to?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/pry/wrapped_module.rb', line 109

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

#safe_send(obj, method, *args, &block) ⇒ Object (private)

FIXME: this method is also found in Pry::Method



276
277
278
# File 'lib/pry/wrapped_module.rb', line 276

def safe_send(obj, method, *args, &block)
    (Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block)
end

#singleton_class?Boolean

Is this a singleton class?

Returns:

  • (Boolean)


85
86
87
# File 'lib/pry/wrapped_module.rb', line 85

def singleton_class?
  wrapped != wrapped.ancestors.first
end

#singleton_instanceObject

Get the instance associated with this singleton class.

Returns:

Raises:

  • ArgumentError: tried to get instance of non singleton class



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

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:



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

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.



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

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

#yard_docString

Return the YARD docs for this module.

Returns:

  • (String)

    Return the YARD docs for this module.



175
176
177
# File 'lib/pry/wrapped_module.rb', line 175

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.



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

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.



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

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.



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

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