1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Extract object path resolution into a class

This commit is contained in:
Ryan Fitzgerald 2014-04-27 17:28:56 -07:00
parent 249dbabe59
commit 2467871169
5 changed files with 93 additions and 69 deletions

View file

@ -169,3 +169,4 @@ require "pry/indent"
require "pry/last_exception"
require "pry/prompt"
require "pry/inspector"
require 'pry/object_path'

View file

@ -21,7 +21,8 @@ class Pry
def process
state.old_stack ||= []
stack, state.old_stack = context_from_object_path(arg_string, _pry_, state.old_stack)
stack, state.old_stack =
ObjectPath.new(arg_string, _pry_.binding_stack, state.old_stack).resolve
_pry_.binding_stack = stack if stack
end
end

View file

@ -119,73 +119,6 @@ class Pry
Pry::Pager.page(text, out)
end
# @param [String] arg_string The object path expressed as a string.
# @param [Pry] _pry_ The relevant Pry instance.
# @param [Array<Binding>] old_stack The state of the old binding stack
# @return [Array<Array<Binding>, Array<Binding>>] An array
# containing two elements: The new `binding_stack` and the old `binding_stack`.
def context_from_object_path(arg_string, _pry_=nil, old_stack=[])
# Extract command arguments. Delete blank arguments like " ", but
# don't delete empty strings like "".
path = arg_string.split(/\//).delete_if { |a| a =~ /\A\s+\z/ }
stack = _pry_.binding_stack.dup
state_old_stack = old_stack
# Special case when we only get a single "/", return to root.
if path.empty?
state_old_stack = stack.dup unless old_stack.empty?
stack = [stack.first]
end
path.each_with_index do |context, i|
begin
case context.chomp
when ""
state_old_stack = stack.dup
stack = [stack.first]
when "::"
state_old_stack = stack.dup
stack.push(TOPLEVEL_BINDING)
when "."
next
when ".."
unless stack.size == 1
# Don't rewrite old_stack if we're in complex expression
# (e.g.: `cd 1/2/3/../4).
state_old_stack = stack.dup if path.first == ".."
stack.pop
end
when "-"
unless old_stack.empty?
# Interchange current stack and old stack with each other.
stack, state_old_stack = state_old_stack, stack
end
else
state_old_stack = stack.dup if i == 0
stack.push(Pry.binding_for(stack.last.eval(context)))
end
rescue RescuableException => e
# Restore old stack to its initial values.
state_old_stack = old_stack
msg = [
"Bad object path: #{arg_string}.",
"Failed trying to resolve: #{context}.",
e.inspect
].join(' ')
CommandError.new(msg).tap do |err|
err.set_backtrace e.backtrace
raise err
end
end
end
return stack, state_old_stack
end
end
end
end

View file

@ -60,7 +60,7 @@ class Pry::InputCompleter
if path.call.empty?
target = options[:target]
else
target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, @pry)
target, _ = Pry::ObjectPath.new(path.call, @pry.binding_stack).resolve
target = target.last
end

89
lib/pry/object_path.rb Normal file
View file

@ -0,0 +1,89 @@
class Pry
# `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.
# @see https://github.com/pry/pry/wiki/State-navigation
class ObjectPath
# @param [String] path_string The object path expressed as a string.
# @param [Array<Binding>] current_stack The current state of the binding
# stack.
# @param [Array<Binding>] old_stack The previous state of the binding
# stack, if applicable.
def initialize(path_string, current_stack, old_stack=[])
@path_string = path_string
@current_stack = current_stack
@old_stack = old_stack
end
# @return [Array(Array<Binding>, Array<Binding>)] an array
# containing two elements, the new binding stack and the old binding
# stack.
def resolve
# Extract command arguments. Delete blank arguments like " ", but
# don't delete empty strings like "".
path = @path_string.split(/\//).delete_if { |a| a =~ /\A\s+\z/ }
stack = @current_stack.dup
state_old_stack = @old_stack
# Special case when we only get a single "/", return to root.
if path.empty?
state_old_stack = stack.dup unless @old_stack.empty?
stack = [stack.first]
end
path.each_with_index do |context, i|
begin
case context.chomp
when ""
state_old_stack = stack.dup
stack = [stack.first]
when "::"
state_old_stack = stack.dup
stack.push(TOPLEVEL_BINDING)
when "."
next
when ".."
unless stack.size == 1
# Don't rewrite old_stack if we're in complex expression
# (e.g.: `cd 1/2/3/../4).
state_old_stack = stack.dup if path.first == ".."
stack.pop
end
when "-"
unless @old_stack.empty?
# Interchange current stack and old stack with each other.
stack, state_old_stack = state_old_stack, stack
end
else
state_old_stack = stack.dup if i == 0
stack.push(Pry.binding_for(stack.last.eval(context)))
end
rescue RescuableException => e
# Restore old stack to its initial values.
state_old_stack = @old_stack
msg = [
"Bad object path: #{@path_string}.",
"Failed trying to resolve: #{context}.",
e.inspect
].join(' ')
CommandError.new(msg).tap do |err|
err.set_backtrace e.backtrace
raise err
end
end
end
[stack, state_old_stack]
end
end
end