Class: Pry::CodeObject

Inherits:
Object show all
Includes:
Helpers::CommandHelpers
Defined in:
lib/pry/code_object.rb

Overview

This class is responsible for taking a string (identifying a command/class/method/etc) and returning the relevant type of object. For example, if the user looks up "show-source" then a Pry::Command will be returned. Alternatively, if the user passes in "Pry#repl" then a Pry::Method object will be returned.

The CodeObject.lookup method is responsible for 1. figuring out what kind of object the user wants (applying precedence rules in doing so -- i.e methods get precedence over commands with the same name) and 2. Returning the appropriate object. If the user fails to provide a string identifer for the object (i.e they pass in nil or "") then the object looked up will be the 'current method' or 'current class' associated with the Binding.

TODO: This class is a clusterfuck. We need a much more robust concept of what a "Code Object" really is. Currently commands/classes/candidates/methods and so on just share a very ill-defined interface.

Defined Under Namespace

Modules: Helpers

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::CommandHelpers

#absolute_index_number, #absolute_index_range, #get_method_or_raise, #internal_binding?, #one_index_number, #one_index_range, #one_index_range_or_number, #restrict_to_lines, #set_file_and_dir_locals, #temp_file, #unindent

Methods included from Helpers::OptionsHelpers

#method_object, method_object, method_options, #method_options

Constructor Details

#initialize(str, pry_instance, options = {}) ⇒ CodeObject

Returns a new instance of CodeObject.



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

def initialize(str, pry_instance, options = {})
  options = {
    super: 0
  }.merge!(options)

  @str = str
  @pry_instance = pry_instance
  @target = pry_instance.current_context
  @super_level = options[:super]
end

Instance Attribute Details

#pry_instanceObject

Returns the value of attribute pry_instance.



79
80
81
# File 'lib/pry/code_object.rb', line 79

def pry_instance
  @pry_instance
end

#strObject

Returns the value of attribute str.



77
78
79
# File 'lib/pry/code_object.rb', line 77

def str
  @str
end

#super_levelObject

Returns the value of attribute super_level.



80
81
82
# File 'lib/pry/code_object.rb', line 80

def super_level
  @super_level
end

#targetObject

Returns the value of attribute target.



78
79
80
# File 'lib/pry/code_object.rb', line 78

def target
  @target
end

Class Method Details

.lookup(str, pry_instance, options = {}) ⇒ Object



69
70
71
72
73
74
# File 'lib/pry/code_object.rb', line 69

def lookup(str, pry_instance, options = {})
  co = new(str, pry_instance, options)

  co.default_lookup || co.method_or_class_lookup ||
    co.command_lookup || co.empty_lookup
end

Instance Method Details

#command_lookupObject

TODO: just make it so find_command_by_match_or_listing doesn't raise?



94
95
96
97
98
# File 'lib/pry/code_object.rb', line 94

def command_lookup
  pry_instance.commands.find_command_by_match_or_listing(str)
rescue StandardError
  nil
end

#default_lookupObject

lookup variables and constants and self that are not modules



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/pry/code_object.rb', line 118

def default_lookup
  # we skip instance methods as we want those to fall through to
  # method_or_class_lookup()
  if safe_to_evaluate?(str) && !looks_like_an_instance_method?(str)
    obj = target.eval(str)

    # restrict to only objects we KNOW for sure support the full API
    # Do NOT support just any object that responds to source_location
    if sourcable_object?(obj)
      Pry::Method(obj)
    elsif !obj.is_a?(Module)
      Pry::WrappedModule(obj.class)
    end
  end
rescue Pry::RescuableException
  nil
end

#empty_lookupObject

when no paramter is given (i.e CodeObject.lookup(nil)), then we lookup the 'current object' from the binding.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/pry/code_object.rb', line 102

def empty_lookup
  return nil if str && !str.empty?

  obj = if internal_binding?(target)
          mod = target_self.is_a?(Module) ? target_self : target_self.class
          Pry::WrappedModule(mod)
        else
          Pry::Method.from_binding(target)
        end

  # respect the super level (i.e user might have specified a
  # --super flag to show-source)
  lookup_super(obj, super_level)
end

#looks_like_an_instance_method?(str) ⇒ Boolean (private)

Returns true if str looks like a method, i.e Klass#method We need to consider this case because method lookups should fall through to the method_or_class_lookup() method but a defined?() on a "Klass#methodstring will see the#as a comment and only evaluate theKlass` part.

Parameters:

  • str (String)

Returns:

  • (Boolean)

    Whether the string looks like an instance method.



163
164
165
# File 'lib/pry/code_object.rb', line 163

def looks_like_an_instance_method?(str)
  str =~ /\S#\S/
end

#lookup_super(obj, super_level) ⇒ Object (private)

grab the nth (super_level) super of `obj

Parameters:

  • obj (Object)
  • super_level (Fixnum)

    How far up the super chain to ascend.

Raises:



188
189
190
191
192
193
194
195
# File 'lib/pry/code_object.rb', line 188

def lookup_super(obj, super_level)
  return unless obj

  sup = obj.super(super_level)
  raise Pry::CommandError, "No superclass found for #{obj.wrapped}" unless sup

  sup
end

#method_or_class_lookupObject



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/pry/code_object.rb', line 136

def method_or_class_lookup
  obj =
    case str
    when /\S+\(\)\z/
      Pry::Method.from_str(str.sub(/\(\)\z/, ''), target) ||
      Pry::WrappedModule.from_str(str, target)
    else
      Pry::WrappedModule.from_str(str, target) ||
      Pry::Method.from_str(str, target)
    end

  lookup_super(obj, super_level)
end

#safe_to_evaluate?(str) ⇒ 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::WrappedModule

Parameters:

  • str (String)

    The string to lookup

Returns:

  • (Boolean)


173
174
175
176
177
178
179
# File 'lib/pry/code_object.rb', line 173

def safe_to_evaluate?(str)
  return true if str.strip == "self"
  return false if str =~ /%/

  kind = target.eval("defined?(#{str})")
  kind =~ /variable|constant/
end

#sourcable_object?(obj) ⇒ Boolean (private)

Returns:

  • (Boolean)


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

def sourcable_object?(obj)
  [::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) }
end

#target_selfObject (private)



181
182
183
# File 'lib/pry/code_object.rb', line 181

def target_self
  target.eval('self')
end