Class: Pry::ObjectPath

Inherits:
Object show all
Defined in:
lib/pry/object_path.rb

Overview

ObjectPath implements the resolution of "object paths", which are strings that are similar to filesystem paths but meant for traversing Ruby objects. Examples of valid object paths include:

x
@foo/@bar
"string"/upcase
Pry/Method

Object paths are mostly relevant in the context of the cd command.

Constant Summary collapse

SPECIAL_TERMS =
["", "::", ".", ".."].freeze

Instance Method Summary collapse

Constructor Details

#initialize(path_string, current_stack) ⇒ ObjectPath


23
24
25
26
# File 'lib/pry/object_path.rb', line 23

def initialize(path_string, current_stack)
  @path_string   = path_string
  @current_stack = current_stack
end

Instance Method Details

#complete?(segment) ⇒ Boolean (private)


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

def complete?(segment)
  SPECIAL_TERMS.include?(segment) || Pry::Code.complete_expression?(segment)
end

#handle_failure(context, err) ⇒ Object (private)


78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/pry/object_path.rb', line 78

def handle_failure(context, err)
  msg = [
    "Bad object path: #{@path_string.inspect}",
    "Failed trying to resolve: #{context.inspect}",
    "Exception: #{err.inspect}"
  ].join("\n")

  command_error = CommandError.new(msg)
  command_error.set_backtrace(err.backtrace)

  raise command_error
end

#resolveArray<Binding>


30
31
32
33
34
35
36
37
38
39
40
41
42
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
69
70
# File 'lib/pry/object_path.rb', line 30

def resolve
  scanner = StringScanner.new(@path_string.strip)
  stack   = @current_stack.dup

  loop do
    begin
      next_segment = ""

      loop do
        # Scan for as long as we don't see a slash
        next_segment += scanner.scan(%r{[^/]*})

        if complete?(next_segment) || scanner.eos?
          scanner.getch # consume the slash
          break
        else
          next_segment += scanner.getch # append the slash
        end
      end

      case next_segment.chomp
      when ""
        stack = [stack.first]
      when "::"
        stack.push(TOPLEVEL_BINDING)
      when "."
        next
      when ".."
        stack.pop unless stack.size == 1
      else
        stack.push(Pry.binding_for(stack.last.eval(next_segment)))
      end
    rescue RescuableException => e
      return handle_failure(next_segment, e)
    end

    break if scanner.eos?
  end

  stack
end