Class: Pry::Method

Inherits:
Object show all
Extended by:
Forwardable, Helpers::BaseHelpers
Includes:
CodeObject::Helpers, Helpers::BaseHelpers, Helpers::DocumentationHelpers
Defined in:
lib/pry/method.rb,
lib/pry/method/patcher.rb,
lib/pry/method/disowned.rb,
lib/pry/method/weird_method_locator.rb

Overview

This class wraps the normal ‘Method` and `UnboundMethod` classes to provide extra functionality useful to Pry.

Direct Known Subclasses

Disowned

Defined Under Namespace

Classes: Disowned, Patcher, WeirdMethodLocator

Constant Summary

Constants included from Helpers::DocumentationHelpers

Helpers::DocumentationHelpers::YARD_TAGS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::BaseHelpers

colorize_code, find_command, heading, highlight, not_a_real_file?, safe_send, silence_warnings, stagger_output, use_ansi_codes?

Methods included from Forwardable

def_private_delegators

Methods included from CodeObject::Helpers

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

Methods included from Helpers::DocumentationHelpers

get_comment_content, process_comment_markup, process_rdoc, process_yardoc, process_yardoc_tag, strip_comments_from_c_code, strip_leading_whitespace

Constructor Details

#initialize(method, known_info = {}) ⇒ Pry::Method

A new instance of ‘Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.



263
264
265
266
# File 'lib/pry/method.rb', line 263

def initialize(method, known_info = {})
  @method = method
  @visibility = known_info[:visibility]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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

Delegate any unknown calls to the wrapped method.



503
504
505
506
507
508
509
# File 'lib/pry/method.rb', line 503

def method_missing(method_name, *args, &block)
  if @method.respond_to?(method_name)
    @method.__send__(method_name, *args, &block)
  else
    super
  end
end

Class Method Details

.all_from_class(klass, include_super = true) ⇒ Array[Pry::Method]

Get all of the instance methods of a ‘Class` or `Module`



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/pry/method.rb', line 161

def all_from_class(klass, include_super = true)
  %w[public protected private].flat_map do |visibility|
    safe_send(
      klass, :"#{visibility}_instance_methods", include_super
    ).map do |method_name|
      new(
        safe_send(klass, :instance_method, method_name),
        visibility: visibility.to_sym
      )
    end
  end
end

.all_from_obj(obj, include_super = true) ⇒ Array[Pry::Method]

Get all of the methods on an ‘Object`



184
185
186
# File 'lib/pry/method.rb', line 184

def all_from_obj(obj, include_super = true)
  all_from_class(singleton_class_of(obj), include_super)
end

.from_binding(binding) ⇒ Pry::Method?

Given a ‘Binding`, try to extract the `::Method` it originated from and use it to instantiate a `Pry::Method`. Return `nil` if this isn’t possible.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/pry/method.rb', line 77

def from_binding(binding)
  meth_name = binding.eval('::Kernel.__method__')
  if [:__script__, nil].include?(meth_name)
    nil
  else
    method =
      begin
        if Object === binding.eval('self') # rubocop:disable Style/CaseEquality
          new(
            Kernel.instance_method(:method)
              .bind(binding.eval("self"))
              .call(meth_name)
          )
        else
          str = 'class << self; self; end' \
                '.instance_method(::Kernel.__method__).bind(self)'
          new(binding.eval(str))
        end
      rescue NameError, NoMethodError # rubocop:disable Lint/ShadowedException
        Disowned.new(binding.eval('self'), meth_name.to_s)
      end

    if WeirdMethodLocator.weird_method?(method, binding)
      WeirdMethodLocator.new(method, binding).find_method || method
    else
      method
    end
  end
end

.from_class(klass, name, target = TOPLEVEL_BINDING) ⇒ Pry::Method? Also known as: from_module

Given a ‘Class` or `Module` and the name of a method, try to instantiate a `Pry::Method` containing the instance method of that name. Return `nil` if no such method exists.



136
137
138
139
140
# File 'lib/pry/method.rb', line 136

def from_class(klass, name, target = TOPLEVEL_BINDING)
  new(lookup_method_via_binding(klass, name, :instance_method, target))
rescue StandardError
  nil
end

.from_obj(obj, name, target = TOPLEVEL_BINDING) ⇒ Pry::Method?

Given an object and the name of a method, try to instantiate a ‘Pry::Method` containing the method of that name bound to that object. Return `nil` if no such method exists.



151
152
153
154
155
# File 'lib/pry/method.rb', line 151

def from_obj(obj, name, target = TOPLEVEL_BINDING)
  new(lookup_method_via_binding(obj, name, :method, target))
rescue StandardError
  nil
end

.from_str(name, target = TOPLEVEL_BINDING, options = {}) ⇒ Pry::Method?

Given a string representing a method name and optionally a binding to search in, find and return the requested method wrapped in a ‘Pry::Method` instance.

Options Hash (options):

  • :instance (Boolean)

    Look for an instance method if ‘name` doesn’t contain any context.

  • :methods (Boolean)

    Look for a bound/singleton method if ‘name` doesn’t contain any context.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/pry/method.rb', line 43

def from_str(name, target = TOPLEVEL_BINDING, options = {})
  if name.nil?
    nil
  elsif name.to_s =~ /(.+)\#(\S+)\Z/
    context = Regexp.last_match(1)
    meth_name = Regexp.last_match(2)
    from_module(target.eval(context), meth_name, target)
  elsif name.to_s =~ /(.+)(\[\])\Z/
    context = Regexp.last_match(1)
    meth_name = Regexp.last_match(2)
    from_obj(target.eval(context), meth_name, target)
  elsif name.to_s =~ /(.+)(\.|::)(\S+)\Z/
    context = Regexp.last_match(1)
    meth_name = Regexp.last_match(3)
    from_obj(target.eval(context), meth_name, target)
  elsif options[:instance]
    from_module(target.eval("self"), name, target)
  elsif options[:methods]
    from_obj(target.eval("self"), name, target)
  else
    from_str(name, target, instance: true) ||
      from_str(name, target, methods: true)
  end
rescue Pry::RescuableException
  nil
end

.instance_method_definition?(name, definition_line) ⇒ Boolean



227
228
229
230
231
232
# File 'lib/pry/method.rb', line 227

def instance_method_definition?(name, definition_line)
  regexp =
    /^define_method\(?\s*[:\"\']#{Regexp.escape(name)}|
     ^def\s*#{Regexp.escape(name)}/x
  regexp =~ definition_line.strip
end

.instance_resolution_order(klass) ⇒ Array[Class, Module]

Get every ‘Class` and `Module`, in order, that will be checked when looking for methods on instances of the given `Class` or `Module`. This does not treat singleton classes of classes specially.



210
211
212
213
# File 'lib/pry/method.rb', line 210

def instance_resolution_order(klass)
  # include klass in case it is a singleton class,
  ([klass] + Pry::Method.safe_send(klass, :ancestors)).uniq
end

.lookup_method_via_binding(obj, method_name, method_type, target = TOPLEVEL_BINDING) ⇒ Method, UnboundMethod

In order to support 2.0 Refinements we need to look up methods inside the relevant Binding.



114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/pry/method.rb', line 114

def lookup_method_via_binding(
  obj, method_name, method_type, target = TOPLEVEL_BINDING
)
  Pry.current[:obj] = obj
  Pry.current[:name] = method_name
  receiver = obj.is_a?(Module) ? "Module" : "Kernel"
  target.eval(
    "::#{receiver}.instance_method(:#{method_type})" \
    ".bind(Pry.current[:obj]).call(Pry.current[:name])"
  )
ensure
  Pry.current[:obj] = Pry.current[:name] = nil
end

.method_definition?(name, definition_line) ⇒ Boolean



215
216
217
218
# File 'lib/pry/method.rb', line 215

def method_definition?(name, definition_line)
  singleton_method_definition?(name, definition_line) ||
    instance_method_definition?(name, definition_line)
end

.resolution_order(obj) ⇒ Array[Class, Module]

Get every ‘Class` and `Module`, in order, that will be checked when looking for an instance method to call on this object.



192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/pry/method.rb', line 192

def resolution_order(obj)
  if Class === obj # rubocop:disable Style/CaseEquality
    singleton_class_resolution_order(obj) + instance_resolution_order(Class)
  else
    klass = begin
              singleton_class_of(obj)
            rescue StandardError
              obj.class
            end
    instance_resolution_order(klass)
  end
end

.singleton_class_of(obj) ⇒ Object



247
248
249
250
251
# File 'lib/pry/method.rb', line 247

def singleton_class_of(obj)
  class << obj; self; end
rescue TypeError # can't define singleton. Fixnum, Symbol, Float, ...
  obj.class
end

.singleton_class_resolution_order(klass) ⇒ Object

Get the singleton classes of superclasses that could define methods on the given class object, and any modules they include. If a module is included at multiple points in the ancestry, only the lowest copy will be returned.



238
239
240
241
242
243
244
245
# File 'lib/pry/method.rb', line 238

def singleton_class_resolution_order(klass)
  ancestors = Pry::Method.safe_send(klass, :ancestors)
  resolution_order = ancestors.grep(Class).flat_map do |anc|
    [singleton_class_of(anc), *singleton_class_of(anc).included_modules]
  end

  resolution_order.reverse.uniq.reverse - Class.included_modules
end

.singleton_method_definition?(name, definition_line) ⇒ Boolean



220
221
222
223
224
225
# File 'lib/pry/method.rb', line 220

def singleton_method_definition?(name, definition_line)
  regexp =
    /^define_singleton_method\(?\s*[:\"\']#{Regexp.escape(name)}|
     ^def\s*self\.#{Regexp.escape(name)}/x
  regexp =~ definition_line.strip
end

Instance Method Details

#==(other) ⇒ Boolean



483
484
485
486
487
# File 'lib/pry/method.rb', line 483

def ==(other)
  return other == @method if other.is_a?(Pry::Method)

  @method == other
end

#alias?Boolean



478
479
480
# File 'lib/pry/method.rb', line 478

def alias?
  name != original_name
end

#aliasesArray<String>



461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/pry/method.rb', line 461

def aliases
  owner = @method.owner
  # Avoid using `to_sym` on {Method#name}, which returns a `String`, because
  # it won't be garbage collected.
  name = @method.name

  all_methods_to_compare = owner.instance_methods | owner.private_instance_methods
  alias_list = all_methods_to_compare.combination(2).select do |pair|
    pair.include?(name) &&
      owner.instance_method(pair.first) == owner.instance_method(pair.last)
  end.flatten
  alias_list.delete(name)

  alias_list.map(&:to_s)
end

#bound_method?Boolean



446
447
448
# File 'lib/pry/method.rb', line 446

def bound_method?
  is_a?(::Method)
end

#commentObject



515
516
517
# File 'lib/pry/method.rb', line 515

def comment
  Pry::Code.from_file(source_file).comment_describing(source_line)
end

#docString?



329
330
331
332
333
334
335
336
337
338
# File 'lib/pry/method.rb', line 329

def doc
  @doc ||=
    case source_type
    when :c
      info = pry_doc_info
      info.docstring if info
    when :ruby
      get_comment_content(comment)
    end
end

#dynamically_defined?Boolean



436
437
438
# File 'lib/pry/method.rb', line 436

def dynamically_defined?
  !!(source_file && source_file =~ /(\(.*\))|<.*>/)
end

#is_a?(klass) ⇒ Boolean Also known as: kind_of?



491
492
493
# File 'lib/pry/method.rb', line 491

def is_a?(klass)
  (klass == Pry::Method) || @method.is_a?(klass)
end

#nameString

Get the name of the method as a String, regardless of the underlying Method#name type.



272
273
274
# File 'lib/pry/method.rb', line 272

def name
  @method.name.to_s
end

#name_with_ownerString

Get the name of the method including the class on which it was defined.

Examples:

method(:puts).method_name
=> "Kernel.puts"


299
300
301
# File 'lib/pry/method.rb', line 299

def name_with_owner
  "#{wrapped_owner.method_prefix}#{name}"
end

#original_nameString?



429
430
431
432
433
# File 'lib/pry/method.rb', line 429

def original_name
  return nil if source_type != :ruby

  method_name_from_first_line(source.lines.first)
end

#pry_method?Boolean



456
457
458
# File 'lib/pry/method.rb', line 456

def pry_method?
  source_file == Pry.eval_path
end

#redefine(source) ⇒ Object

Update the live copy of the method’s source.



314
315
316
317
# File 'lib/pry/method.rb', line 314

def redefine(source)
  Patcher.new(self).patch_in_ram source
  Pry::Method(owner.instance_method(name))
end

#respond_to?(method_name, include_all = false) ⇒ Boolean



498
499
500
# File 'lib/pry/method.rb', line 498

def respond_to?(method_name, include_all = false)
  super || @method.respond_to?(method_name, include_all)
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean



511
512
513
# File 'lib/pry/method.rb', line 511

def respond_to_missing?(method_name, include_private = false)
  @method.respond_to?(method_name) || super
end

#signatureString



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/pry/method.rb', line 391

def signature
  if respond_to?(:parameters)
    args = parameters.inject([]) do |args_array, (arg_type, name)|
      name ||= (arg_type == :block ? 'block' : "arg#{args_array.size + 1}")
      args_array.push(
        case arg_type
        when :req    then name.to_s
        when :opt    then "#{name}=?"
        when :rest   then "*#{name}"
        when :block  then "&#{name}"
        when :key    then "#{name}:?"
        when :keyreq then "#{name}:"
        else '?'
        end
      )
    end
  else
    args = (1..arity.abs).map { |i| "arg#{i}" }
    args[-1] = "*#{args[-1]}" if arity < 0
  end

  "#{name}(#{args.join(', ')})"
end

#singleton_method?Boolean



451
452
453
# File 'lib/pry/method.rb', line 451

def singleton_method?
  wrapped_owner.singleton_class?
end

#sourceString?



304
305
306
307
308
309
310
311
# File 'lib/pry/method.rb', line 304

def source
  @source ||= case source_type
              when :c
                c_source
              when :ruby
                ruby_source
              end
end

#source?Boolean

Can we get the source code for this method?



321
322
323
324
325
# File 'lib/pry/method.rb', line 321

def source?
  !!source
rescue MethodSource::SourceNotFoundError
  false
end

#source_fileString?



348
349
350
351
352
353
354
355
356
357
# File 'lib/pry/method.rb', line 348

def source_file
  if source_location.nil?
    if source_type == :c
      info = pry_doc_info
      info.file if info
    end
  else
    source_location.first
  end
end

#source_lineFixnum?



361
362
363
# File 'lib/pry/method.rb', line 361

def source_line
  source_location.nil? ? nil : source_location.last
end

#source_rangeRange?



367
368
369
# File 'lib/pry/method.rb', line 367

def source_range
  source_location.nil? ? nil : (source_line)..(source_line + source.lines.count - 1)
end

#source_typeSymbol



342
343
344
# File 'lib/pry/method.rb', line 342

def source_type
  source_location.nil? ? :c : :ruby
end

#super(times = 1) ⇒ Pry::Method?



417
418
419
420
421
422
423
424
425
# File 'lib/pry/method.rb', line 417

def super(times = 1)
  if @method.is_a?(UnboundMethod)
    sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times)
  else
    sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times)
    sup &&= sup.bind(receiver)
  end
  Pry::Method.new(sup) if sup
end

#unbound_method?Boolean



441
442
443
# File 'lib/pry/method.rb', line 441

def unbound_method?
  is_a?(::UnboundMethod)
end

#undefined?Boolean

Is the method undefined? (aka ‘Disowned`)



290
291
292
# File 'lib/pry/method.rb', line 290

def undefined?
  false
end

#visibilitySymbol



373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/pry/method.rb', line 373

def visibility
  @visibility ||=
    if owner.public_instance_methods.any? { |m| m.to_s == name }
      :public
    elsif owner.protected_instance_methods.any? { |m| m.to_s == name }
      :protected
    elsif owner.private_instance_methods.any? { |m| m.to_s == name }
      :private
    else
      :none
    end
end

#wrappedMethod, ...

Get underlying object wrapped by this Pry::Method instance



284
285
286
# File 'lib/pry/method.rb', line 284

def wrapped
  @method
end

#wrapped_ownerPry::Module

Get the owner of the method as a Pry::Module



278
279
280
# File 'lib/pry/method.rb', line 278

def wrapped_owner
  @wrapped_owner ||= Pry::WrappedModule.new(owner)
end