Using a new Pry::CommandProcessor::Result class to manage the way command results are handled.

Behaviour now is:

(1) command that has keep_retval => false can replace eval_string() and when the multi-line expression is evaluated it will return value of the multi-line expression (old behaviour did not do this)
(2) command that has keep_retval => true will always replace whatever multi-line expression it's invoked in with the return value of the command
This commit is contained in:
John Mair 2011-09-05 01:50:27 +12:00
parent d5bbaefd3e
commit 256ddd712e
3 changed files with 105 additions and 13 deletions

View File

@ -2,6 +2,36 @@ require 'forwardable'
class Pry
class CommandProcessor
# Wraps the return result of process_commands, indicates if the
# result IS a command and what kind of command (e.g void)
class Result
attr_reader :retval
def initialize(is_command, keep_retval = false, retval = nil)
@is_command, @keep_retval, @retval = is_command, keep_retval, retval
end
# Is the result a command?
# @return [Boolean]
def command?
@is_command
end
# Is the result a command and if it is, is it a void command?
# (one that does not return a value)
# @return [Boolean]
def void_command?
(command? && !keep_retval?) || retval == CommandContext::VOID_VALUE
end
# Is the return value kept for this command? (i.e :keep_retval => true)
# @return [Boolean]
def keep_retval?
@keep_retval
end
end
extend Forwardable
attr_accessor :pry_instance
@ -80,13 +110,17 @@ class Pry
# @param [String] eval_string The cumulative lines of input for
# multi-line input.
# @param [Binding] target The receiver of the commands.
# @return [Object] The value returned by the
# command.
# @return [Pry::CommandProcessor::Result] A wrapper object
# containing info about the result of the command processing
# (indicating whether it is a command and if it is what kind of
# command it is.
def process_commands(val, eval_string, target)
# no command was matched, so return to caller
command, captures, pos = command_matched(val, target)
return if !command
# no command was matched, so return to caller
return Result.new(false) if !command
arg_string = val[pos..-1]
# remove the one leading space if it exists
@ -104,7 +138,7 @@ class Pry
ret = execute_command(target, command.name, options, *(captures + args))
[ret, command.options[:keep_retval]]
Result.new(true, command.options[:keep_retval], ret)
end
# Execute a Pry command.

View File

@ -246,7 +246,7 @@ class Pry
break if valid_expression?(eval_string)
end
@suppress_output = true if eval_string =~ /;\Z/ || null_input?(val)
@suppress_output = true if eval_string =~ /;\Z/ || null_input?(eval_string)
eval_string
end
@ -277,8 +277,9 @@ class Pry
# value.
# @param [String] val The input string.
# @return [Boolean] Whether the input is null.
def null_input?(val)
val.empty? && Thread.current[:__pry_cmd_ret_value__] == Pry::CommandContext::VOID_VALUE
def null_input?(eval_string)
result = Thread.current[:__pry_cmd_ret_value__]
eval_string.empty? || (eval_string.empty? && result.void_command?)
end
# Read a line of input and check for ^d, also determine prompt to use.
@ -306,12 +307,21 @@ class Pry
# @param [String] eval_string The cumulative lines of input.
# @param [Binding] target The target of the Pry session.
def process_line(val, eval_string, target)
Thread.current[:__pry_cmd_ret_value__], keep_retval = @command_processor.process_commands(val, eval_string, target)
result = @command_processor.process_commands(val, eval_string, target)
Thread.current[:__pry_cmd_result__] = result
if keep_retval && Thread.current[:__pry_cmd_ret_value__] != Pry::CommandContext::VOID_VALUE
eval_string << "Thread.current[:__pry_cmd_ret_value__]\n"
# note that `result` wraps the result of command processing; if a
# command was matched and invoked then `result.command?` returns true,
# otherwise it returns false.
if result.command? && !result.void_command?
# the command is non-void (has a return value) and so we make
# the value of the current expression equal to the return value
# of the command.
eval_string.replace "Thread.current[:__pry_cmd_result__].retval\n"
else
# only commands (with no ret_value) should have an empty `val` so this ignores their result
# only commands should have an empty `val`
# so this ignores their result
eval_string << "#{val.rstrip}\n" if !val.empty?
end
end

View File

@ -33,7 +33,6 @@ describe Pry do
end
end
describe "Pry.binding_for" do
# regression test for burg's bug (see git history)
@ -93,6 +92,18 @@ describe Pry do
o.instance_variable_get(:@x).should == 10
end
it 'should not output anything for no input' do
outp = StringIO.new
# note i could not use mock_pry() for this test for some
# reason, as i'd always get "\n" as output instead of ""
redirect_pry_io(StringIO.new(""), outp) do
Pry.new.rep(self)
end
outp.string.empty?.should == true
end
it 'should make self evaluate to the receiver of the rep session' do
o = :john
str_output = StringIO.new
@ -736,6 +747,43 @@ describe Pry do
str_output.string.empty?.should == true
end
it 'a command (with :keep_retval => false) that replaces eval_string with a valid expression should not have the expression value suppressed' do
klass = Pry::CommandSet.new do
command "hello", "" do
eval_string.replace("6")
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => str_output, :commands => klass).rep
str_output.string.should =~ /6/
end
it 'a command (with :keep_retval => true) that replaces eval_string with a valid expression should overwrite the eval_string with the return value' do
klass = Pry::CommandSet.new do
command "hello", "", :keep_retval => true do
eval_string.replace("6")
7
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => str_output, :commands => klass).rep
str_output.string.should =~ /7/
str_output.string.should.not =~ /6/
end
it 'a command that return a value in a multi-line expression should clear the expression and return the value' do
klass = Pry::CommandSet.new do
command "hello", "", :keep_retval => true do
5
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => str_output, :commands => klass).rep
str_output.string.should =~ /5/
end
it 'should set the commands default, and the default should be overridable' do
klass = Pry::CommandSet.new do
command "hello" do