mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
Split CommandProcessor responsibility between CommandSet and Command
The CommandSet is responsible for storing Commands, while the Commands themselves are responsible for all the parsing. (Before this change, the CommandProcessor used to do both the searching within the CommandSet's and also the tokenization of command arguments)
This commit is contained in:
parent
831540b84e
commit
45c6492e7e
8 changed files with 383 additions and 361 deletions
|
@ -52,16 +52,40 @@ class Pry
|
|||
klass
|
||||
end
|
||||
|
||||
# Should this command be called for the given line?
|
||||
#
|
||||
# @param String a line input at the REPL
|
||||
# @return Boolean
|
||||
def matches?(val)
|
||||
command_regex =~ val
|
||||
end
|
||||
|
||||
# Store hooks to be run before or after the command body.
|
||||
# @see {Pry::CommandSet#before_command}
|
||||
# @see {Pry::CommandSet#after_command}
|
||||
def hooks
|
||||
@hooks ||= {:before => [], :after => []}
|
||||
end
|
||||
|
||||
def command_regex
|
||||
prefix = convert_to_regex(Pry.config.command_prefix)
|
||||
prefix = "(?:#{prefix})?" unless options[:use_prefix]
|
||||
|
||||
/^#{prefix}#{convert_to_regex(name)}(?!\S)/
|
||||
end
|
||||
|
||||
def convert_to_regex(obj)
|
||||
case obj
|
||||
when String
|
||||
Regexp.escape(obj)
|
||||
else
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Properties of one execution of a command (passed by the {Pry::CommandProcessor} as a hash of
|
||||
# Properties of one execution of a command (passed by {Pry#run_command} as a hash of
|
||||
# context and expanded in {#initialize}
|
||||
attr_accessor :output
|
||||
attr_accessor :target
|
||||
|
@ -70,7 +94,6 @@ class Pry
|
|||
attr_accessor :arg_string
|
||||
attr_accessor :context
|
||||
attr_accessor :command_set
|
||||
attr_accessor :command_processor
|
||||
attr_accessor :_pry_
|
||||
|
||||
# Run a command from another command.
|
||||
|
@ -84,7 +107,7 @@ class Pry
|
|||
# run "amend-line", "5", 'puts "hello world"'
|
||||
def run(command_string, *args)
|
||||
complete_string = "#{command_string} #{args.join(" ")}"
|
||||
command_processor.process_commands(complete_string, eval_string, target)
|
||||
command_set.process_line(complete_string, context)
|
||||
end
|
||||
|
||||
def commands
|
||||
|
@ -110,17 +133,87 @@ class Pry
|
|||
self.context = context
|
||||
self.target = context[:target]
|
||||
self.output = context[:output]
|
||||
self.captures = context[:captures]
|
||||
self.eval_string = context[:eval_string]
|
||||
self.arg_string = context[:arg_string]
|
||||
self.command_set = context[:command_set]
|
||||
self._pry_ = context[:pry_instance]
|
||||
self.command_processor = context[:command_processor]
|
||||
end
|
||||
|
||||
# The value of {self} inside the {target} binding.
|
||||
def target_self; target.eval('self'); end
|
||||
|
||||
# Revaluate the string (str) and perform interpolation.
|
||||
# @param [String] str The string to reevaluate with interpolation.
|
||||
#
|
||||
# @return [String] The reevaluated string with interpolations
|
||||
# applied (if any).
|
||||
def interpolate_string(str)
|
||||
dumped_str = str.dump
|
||||
if dumped_str.gsub!(/\\\#\{/, '#{')
|
||||
target.eval(dumped_str)
|
||||
else
|
||||
str
|
||||
end
|
||||
end
|
||||
|
||||
# Display a warning if a command collides with a local/method in
|
||||
# the current scope.
|
||||
# @param [String] command_name_match The name of the colliding command.
|
||||
# @param [Binding] target The current binding context.
|
||||
def check_for_command_name_collision(command_name_match)
|
||||
if collision_type = target.eval("defined?(#{command_name_match})")
|
||||
output.puts "#{Pry::Helpers::Text.bold('WARNING:')} Command name collision with a #{collision_type}: '#{command_name_match}'\n\n"
|
||||
end
|
||||
rescue Pry::RescuableException
|
||||
end
|
||||
|
||||
# Extract necessary information from a line that Command.matches? this command.
|
||||
#
|
||||
# @param String the line of input
|
||||
# @return [
|
||||
# String the command name used, or portion of line that matched the command_regex
|
||||
# String a string of all the arguments (i.e. everything but the name)
|
||||
# Array the captures caught by the command_regex
|
||||
# Array args the arguments got by splitting the arg_string
|
||||
# ]
|
||||
def tokenize(val)
|
||||
val.replace(interpolate_string(val)) if command_options[:interpolate]
|
||||
|
||||
self.class.command_regex =~ val
|
||||
|
||||
# please call Command.matches? before Command#call_safely
|
||||
raise CommandError, "fatal: called a command which didn't match?!" unless Regexp.last_match
|
||||
captures = Regexp.last_match.captures
|
||||
pos = Regexp.last_match.end(0)
|
||||
|
||||
arg_string = val[pos..-1]
|
||||
|
||||
# remove the one leading space if it exists
|
||||
arg_string.slice!(0) if arg_string.start_with?(" ")
|
||||
|
||||
if arg_string
|
||||
args = command_options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
|
||||
else
|
||||
args = []
|
||||
end
|
||||
|
||||
[val[0..pos].rstrip, arg_string, captures, args]
|
||||
end
|
||||
|
||||
# Process a line that Command.matches? this command.
|
||||
#
|
||||
# @param String the line to process
|
||||
# @return Object or Command::VOID_VALUE
|
||||
def process_line(line)
|
||||
command_name, arg_string, captures, args = tokenize(line)
|
||||
|
||||
check_for_command_name_collision(command_name) if Pry.config.collision_warning
|
||||
|
||||
self.arg_string = arg_string
|
||||
self.captures = captures
|
||||
|
||||
call_safely(*(captures + args))
|
||||
end
|
||||
|
||||
# Run the command with the given {args}.
|
||||
#
|
||||
# This is a public wrapper around {#call} which ensures all preconditions are met.
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
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 == Command::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
|
||||
|
||||
def initialize(pry_instance)
|
||||
@pry_instance = pry_instance
|
||||
end
|
||||
|
||||
def_delegators :@pry_instance, :commands, :output
|
||||
|
||||
# Is the string a valid command?
|
||||
# @param [String] val The string passed in from the Pry prompt.
|
||||
# @param [Binding] target The context where the string should be
|
||||
# interpolated in.
|
||||
# @return [Boolean] Whether the string is a valid command.
|
||||
def valid_command?(val, target=binding)
|
||||
!!(command_matched(val, target)[0])
|
||||
end
|
||||
|
||||
# Convert the object to a form that can be interpolated into a
|
||||
# Regexp cleanly.
|
||||
# @return [String] The string to interpolate into a Regexp
|
||||
def convert_to_regex(obj)
|
||||
case obj
|
||||
when String
|
||||
Regexp.escape(obj)
|
||||
else
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
# Revaluate the string (str) and perform interpolation.
|
||||
# @param [String] str The string to reevaluate with interpolation.
|
||||
# @param [Binding] target The context where the string should be
|
||||
# interpolated in.
|
||||
# @return [String] The reevaluated string with interpolations
|
||||
# applied (if any).
|
||||
def interpolate_string(str, target)
|
||||
dumped_str = str.dump
|
||||
dumped_str.gsub!(/\\\#\{/, '#{')
|
||||
target.eval(dumped_str)
|
||||
end
|
||||
|
||||
# Determine whether a Pry command was matched and return command data
|
||||
# and argument string.
|
||||
# This method should not need to be invoked directly.
|
||||
# @param [String] val The line of input.
|
||||
# @param [Binding] target The binding to perform string
|
||||
# interpolation against.
|
||||
# @return [Array] The command data and arg string pair
|
||||
def command_matched(val, target)
|
||||
_, cmd_data = commands.commands.find do |name, data|
|
||||
prefix = convert_to_regex(Pry.config.command_prefix)
|
||||
prefix = "(?:#{prefix})?" unless data.options[:use_prefix]
|
||||
|
||||
command_regex = /^#{prefix}#{convert_to_regex(name)}(?!\S)/
|
||||
|
||||
if command_regex =~ val
|
||||
if data.options[:interpolate]
|
||||
val.replace(interpolate_string(val, target))
|
||||
command_regex =~ val # re-match with the interpolated string
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
[cmd_data, (Regexp.last_match ? Regexp.last_match.captures : nil), (Regexp.last_match ? Regexp.last_match.end(0) : nil)]
|
||||
end
|
||||
|
||||
# Display a warning if a command collides with a local/method in
|
||||
# the current scope.
|
||||
# @param [String] command_name_match The name of the colliding command.
|
||||
# @param [Binding] target The current binding context.
|
||||
def check_for_command_name_collision(command_name_match, target)
|
||||
if collision_type = target.eval("defined?(#{command_name_match})")
|
||||
pry_instance.output.puts "#{Pry::Helpers::Text.bold('WARNING:')} Command name collision with a #{collision_type}: '#{command_name_match}'\n\n"
|
||||
end
|
||||
rescue Pry::RescuableException
|
||||
end
|
||||
|
||||
# Process Pry commands. Pry commands are not Ruby methods and are evaluated
|
||||
# prior to Ruby expressions.
|
||||
# Commands can be modified/configured by the user: see `Pry::Commands`
|
||||
# This method should not need to be invoked directly - it is called
|
||||
# by `Pry#r`.
|
||||
# @param [String] val The current line of input.
|
||||
# @param [String] eval_string The cumulative lines of input for
|
||||
# multi-line input.
|
||||
# @param [Binding] target The receiver of the commands.
|
||||
# @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)
|
||||
|
||||
command, captures, pos = command_matched(val, target)
|
||||
|
||||
# no command was matched, so return to caller
|
||||
return Result.new(false) if !command
|
||||
|
||||
arg_string = val[pos..-1]
|
||||
|
||||
check_for_command_name_collision(val[0..pos].rstrip, target) if Pry.config.collision_warning
|
||||
|
||||
# remove the one leading space if it exists
|
||||
arg_string.slice!(0) if arg_string.start_with?(" ")
|
||||
|
||||
if arg_string
|
||||
args = command.options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
|
||||
else
|
||||
args = []
|
||||
end
|
||||
|
||||
context = {
|
||||
:val => val,
|
||||
:arg_string => arg_string,
|
||||
:eval_string => eval_string,
|
||||
:commands => commands.commands,
|
||||
:captures => captures,
|
||||
:pry_instance => @pry_instance,
|
||||
:output => output,
|
||||
:command_processor => self,
|
||||
:command_set => commands,
|
||||
:target => target
|
||||
}
|
||||
|
||||
ret = command.new(context).call_safely(*(captures + args))
|
||||
|
||||
Result.new(true, command.options[:keep_retval], ret)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -273,6 +273,38 @@ class Pry
|
|||
commands.keys
|
||||
end
|
||||
|
||||
# Find a command that matches the given line
|
||||
#
|
||||
# @param [String] the line that may be a command invocation
|
||||
# @return [Pry::Command, nil]
|
||||
def find_command(val)
|
||||
commands.values.detect{ |c| c.matches?(val) }
|
||||
end
|
||||
|
||||
# Is the given line a command invocation?
|
||||
#
|
||||
# @param [String]
|
||||
# @return [Boolean]
|
||||
def valid_command?(val)
|
||||
!!find_command(val)
|
||||
end
|
||||
|
||||
# Process the given line to see whether it needs executing as a command.
|
||||
#
|
||||
# @param String the line to execute
|
||||
# @param Hash the context to execute the commands with
|
||||
# @return CommandSet::Result
|
||||
#
|
||||
def process_line(val, context={})
|
||||
if command = find_command(val)
|
||||
context = context.merge(:command_set => self)
|
||||
retval = command.new(context).process_line(val)
|
||||
Result.new(true, retval)
|
||||
else
|
||||
Result.new(false)
|
||||
end
|
||||
end
|
||||
|
||||
# @nodoc used for testing
|
||||
def run_command(context, name, *args)
|
||||
command = commands[name] or raise NoCommandError.new(name, self)
|
||||
|
@ -358,4 +390,27 @@ class Pry
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# 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, retval = nil)
|
||||
@is_command, @retval = is_command, 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?
|
||||
retval == Command::VOID_VALUE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -214,7 +214,7 @@ class Pry
|
|||
|
||||
if opts.present?(:exclude)
|
||||
history.map!.with_index do |element, index|
|
||||
unless command_processor.valid_command? element
|
||||
unless command_set.valid_command? element
|
||||
if opts.present?(:'no-numbers')
|
||||
element
|
||||
else
|
||||
|
@ -262,7 +262,7 @@ class Pry
|
|||
output.puts "Saving history in #{file_name} ..."
|
||||
# exclude pry commands
|
||||
hist_array.reject! do |element|
|
||||
command_processor.valid_command?(element)
|
||||
command_set.valid_command?(element)
|
||||
end
|
||||
|
||||
File.open(file_name, 'w') do |f|
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
require "pry/command_processor.rb"
|
||||
require "pry/indent"
|
||||
|
||||
class Pry
|
||||
|
@ -37,7 +36,6 @@ class Pry
|
|||
def initialize(options={})
|
||||
refresh(options)
|
||||
|
||||
@command_processor = CommandProcessor.new(self)
|
||||
@binding_stack = []
|
||||
@indent = Pry::Indent.new
|
||||
end
|
||||
|
@ -353,7 +351,12 @@ class Pry
|
|||
# @param [Binding] target The target of the Pry session.
|
||||
# @return [Boolean] `true` if `val` is a command, `false` otherwise
|
||||
def process_command(val, eval_string, target)
|
||||
result = @command_processor.process_commands(val, eval_string, target)
|
||||
result = commands.process_line(val, {
|
||||
:target => target,
|
||||
:output => output,
|
||||
:eval_string => eval_string,
|
||||
:pry_instance => self
|
||||
})
|
||||
|
||||
# set a temporary (just so we can inject the value we want into eval_string)
|
||||
Thread.current[:__pry_cmd_result__] = result
|
||||
|
@ -382,7 +385,12 @@ class Pry
|
|||
# @example
|
||||
# pry_instance.run_command("ls -m")
|
||||
def run_command(val, eval_string = "", target = binding_stack.last)
|
||||
@command_processor.process_commands(val, eval_string, target)
|
||||
commands.process_line(val,
|
||||
:eval_string => eval_string,
|
||||
:target => target,
|
||||
:pry_instance => self,
|
||||
:output => output
|
||||
)
|
||||
Pry::Command::VOID_VALUE
|
||||
end
|
||||
|
||||
|
|
|
@ -142,12 +142,9 @@ describe "Pry::Command" do
|
|||
context = {
|
||||
:target => binding,
|
||||
:output => StringIO.new,
|
||||
:captures => [],
|
||||
:eval_string => "eval-string",
|
||||
:arg_string => "arg-string",
|
||||
:command_set => @set,
|
||||
:pry_instance => Object.new,
|
||||
:command_processor => Object.new
|
||||
:pry_instance => Object.new
|
||||
}
|
||||
|
||||
it 'should capture lots of stuff from the hash passed to new before setup' do
|
||||
|
@ -160,12 +157,9 @@ describe "Pry::Command" do
|
|||
end
|
||||
|
||||
define_method(:process) do
|
||||
captures.should.equal?(context[:captures])
|
||||
eval_string.should == "eval-string"
|
||||
arg_string.should == "arg-string"
|
||||
command_set.should == @set
|
||||
_pry_.should == context[:pry_instance]
|
||||
command_processor.should == context[:command_processor]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -230,4 +224,94 @@ describe "Pry::Command" do
|
|||
mock_command(cmd, %w(--four 4 four))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'tokenize' do
|
||||
it 'should interpolate string with #{} in them' do
|
||||
cmd = @set.command 'random-dent' do |*args|
|
||||
args.should == ["3", "8"]
|
||||
end
|
||||
|
||||
foo = 5
|
||||
|
||||
cmd.new(:target => binding).process_line 'random-dent #{1 + 2} #{3 + foo}'
|
||||
end
|
||||
|
||||
it 'should not fail if interpolation is not needed and target is not set' do
|
||||
cmd = @set.command 'the-book' do |*args|
|
||||
args.should == ['--help']
|
||||
end
|
||||
|
||||
cmd.new.process_line 'the-book --help'
|
||||
end
|
||||
|
||||
it 'should not interpolate commands with :interpolate => false' do
|
||||
cmd = @set.command 'thor', 'norse god', :interpolate => false do |*args|
|
||||
args.should == ['%(#{foo})']
|
||||
end
|
||||
|
||||
cmd.new.process_line 'thor %(#{foo})'
|
||||
end
|
||||
|
||||
it 'should use shell-words to split strings' do
|
||||
cmd = @set.command 'eccentrica' do |*args|
|
||||
args.should == ['gallumbits', 'eroticon', '6']
|
||||
end
|
||||
|
||||
cmd.new.process_line %(eccentrica "gallumbits" 'erot''icon' 6)
|
||||
end
|
||||
|
||||
it 'should split on spaces if shellwords is not used' do
|
||||
cmd = @set.command 'bugblatter-beast', 'would eat its grandmother', :shellwords => false do |*args|
|
||||
args.should == ['"of', 'traal"']
|
||||
end
|
||||
|
||||
cmd.new.process_line %(bugblatter-beast "of traal")
|
||||
end
|
||||
|
||||
it 'should add captures to arguments for regex commands' do
|
||||
cmd = @set.command /perfectly (normal)( beast)?/i do |*args|
|
||||
args.should == ['Normal', ' Beast', '(honest!)']
|
||||
end
|
||||
|
||||
cmd.new.process_line %(Perfectly Normal Beast (honest!))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'process_line' do
|
||||
it 'should check for command name collisions if configured' do
|
||||
old = Pry.config.collision_warning
|
||||
Pry.config.collision_warning = true
|
||||
|
||||
cmd = @set.command 'frankie' do
|
||||
|
||||
end
|
||||
|
||||
frankie = 'boyle'
|
||||
output = StringIO.new
|
||||
cmd.new(:target => binding, :output => output).process_line %(frankie mouse)
|
||||
|
||||
output.string.should =~ /Command name collision/
|
||||
|
||||
Pry.config.collision_warning = old
|
||||
end
|
||||
|
||||
it "should set the commands' arg_string and captures" do
|
||||
|
||||
cmd = @set.command /benj(ie|ei)/ do |*args|
|
||||
self.arg_string.should == "mouse"
|
||||
self.captures.should == ['ie']
|
||||
args.should == ['ie', 'mouse']
|
||||
end
|
||||
|
||||
cmd.new.process_line %(benjie mouse)
|
||||
end
|
||||
|
||||
it "should raise an error if the line doesn't match the command" do
|
||||
cmd = @set.command 'grunthos', 'the flatulent'
|
||||
|
||||
lambda {
|
||||
cmd.new.process_line %(grumpos)
|
||||
}.should.raise(Pry::CommandError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
require 'helper'
|
||||
|
||||
describe "Pry::CommandProcessor" do
|
||||
before do
|
||||
@pry = Pry.new
|
||||
@pry.commands = Pry::CommandSet.new
|
||||
@command_processor = Pry::CommandProcessor.new(@pry)
|
||||
end
|
||||
|
||||
after do
|
||||
@pry.commands = Pry::CommandSet.new
|
||||
end
|
||||
|
||||
it 'should accurately determine if a command is valid' do
|
||||
@pry.commands.command("test-command") {}
|
||||
valid = @command_processor.valid_command? "test-command"
|
||||
valid.should == true
|
||||
|
||||
valid = @command_processor.valid_command? "blah"
|
||||
valid.should == false
|
||||
end
|
||||
|
||||
it 'should correctly match a simple string command' do
|
||||
@pry.commands.command("test-command") {}
|
||||
command, captures, pos = @command_processor.command_matched "test-command", binding
|
||||
|
||||
command.name.should == "test-command"
|
||||
captures.should == []
|
||||
pos.should == "test-command".length
|
||||
end
|
||||
|
||||
it 'should correctly match a simple string command with parameters' do
|
||||
@pry.commands.command("test-command") { |arg|}
|
||||
command, captures, pos = @command_processor.command_matched "test-command hello", binding
|
||||
|
||||
command.name.should == "test-command"
|
||||
captures.should == []
|
||||
pos.should == "test-command".length
|
||||
end
|
||||
|
||||
it 'should not match when the relevant command does not exist' do
|
||||
command, captures, pos = @command_processor.command_matched "test-command", binding
|
||||
|
||||
command.should == nil
|
||||
captures.should == nil
|
||||
end
|
||||
|
||||
it 'should correctly match a regex command' do
|
||||
@pry.commands.command(/rue(.?)/) { }
|
||||
command, captures, pos = @command_processor.command_matched "rue hello", binding
|
||||
|
||||
command.name.should == /rue(.?)/
|
||||
captures.should == [""]
|
||||
pos.should == 3
|
||||
end
|
||||
|
||||
it 'should correctly match a regex command and extract the capture groups' do
|
||||
@pry.commands.command(/rue(.?)/) { }
|
||||
command, captures, pos = @command_processor.command_matched "rue5 hello", binding
|
||||
|
||||
command.name.should == /rue(.?)/
|
||||
captures.should == ["5"]
|
||||
pos.should == 4
|
||||
end
|
||||
|
||||
it 'should correctly match a string command with spaces in its name' do
|
||||
@pry.commands.command("test command") {}
|
||||
command, captures, pos = @command_processor.command_matched "test command", binding
|
||||
|
||||
command.name.should == "test command"
|
||||
captures.should == []
|
||||
pos.should == command.name.length
|
||||
end
|
||||
|
||||
it 'should correctly match a string command with spaces in its name with parameters' do
|
||||
@pry.commands.command("test command") {}
|
||||
command, captures, pos = @command_processor.command_matched "test command param1 param2", binding
|
||||
|
||||
command.name.should == "test command"
|
||||
captures.should == []
|
||||
pos.should == command.name.length
|
||||
end
|
||||
|
||||
it 'should correctly match a command preceded by the command_prefix if one is defined' do
|
||||
Pry.config.command_prefix = "%"
|
||||
|
||||
@pry.commands.command("test-command") {}
|
||||
command, captures, pos = @command_processor.command_matched "%test-command hello", binding
|
||||
|
||||
command.name.should == "test-command"
|
||||
captures.should == []
|
||||
pos.should == "test-command".length + "%".length
|
||||
|
||||
Pry.config.command_prefix = ''
|
||||
end
|
||||
|
||||
it 'should not match a command not preceded by the command_prefix if one is defined' do
|
||||
Pry.config.command_prefix = "%"
|
||||
|
||||
@pry.commands.command("test-command") {}
|
||||
command, captures, pos = @command_processor.command_matched "test-command hello", binding
|
||||
|
||||
command.should == nil
|
||||
captures.should == nil
|
||||
|
||||
Pry.config.command_prefix = ''
|
||||
end
|
||||
|
||||
it 'should match a command preceded by the command_prefix when :use_prefix => false' do
|
||||
Pry.config.command_prefix = "%"
|
||||
|
||||
@pry.commands.command("test-command", "", :use_prefix => false) {}
|
||||
command, captures, pos = @command_processor.command_matched "%test-command hello", binding
|
||||
|
||||
command.name.should == "test-command"
|
||||
captures.should == []
|
||||
pos.should == "test-command".length + "%".length
|
||||
|
||||
Pry.config.command_prefix = ''
|
||||
end
|
||||
|
||||
it 'should match a command not preceded by the command_prefix when :use_prefix => false' do
|
||||
Pry.config.command_prefix = "%"
|
||||
|
||||
@pry.commands.command("test-command", "", :use_prefix => false) {}
|
||||
command, captures, pos = @command_processor.command_matched "test-command hello", binding
|
||||
|
||||
command.name.should == "test-command"
|
||||
captures.should == []
|
||||
pos.should == "test-command".length
|
||||
|
||||
Pry.config.command_prefix = ''
|
||||
end
|
||||
|
||||
it 'should correctly match a regex command with spaces in its name' do
|
||||
regex_command_name = /test\s+(.+)\s+command/
|
||||
@pry.commands.command(regex_command_name) {}
|
||||
|
||||
sample_text = "test friendship command"
|
||||
command, captures, pos = @command_processor.command_matched sample_text, binding
|
||||
|
||||
command.name.should == regex_command_name
|
||||
captures.should == ["friendship"]
|
||||
pos.should == sample_text.size
|
||||
end
|
||||
|
||||
it 'should correctly match a complex regex command' do
|
||||
regex_command_name = /\.(.*)/
|
||||
@pry.commands.command(regex_command_name) {}
|
||||
|
||||
sample_text = ".cd ~/pry"
|
||||
command, captures, pos = @command_processor.command_matched sample_text, binding
|
||||
|
||||
command.name.should == regex_command_name
|
||||
captures.should == ["cd ~/pry"]
|
||||
pos.should == sample_text.size
|
||||
end
|
||||
|
||||
it 'should not interpolate commands that have :interpolate => false (interpolate_string should *not* be called)' do
|
||||
@pry.commands.command("boast", "", :interpolate => false) {}
|
||||
|
||||
# remember to use '' instead of "" when testing interpolation or
|
||||
# you'll cause yourself incredible confusion
|
||||
lambda { @command_processor.command_matched('boast #{c}', binding) }.should.not.raise NameError
|
||||
end
|
||||
|
||||
it 'should only execute the contents of an interpolation once' do
|
||||
$obj = 'a'
|
||||
|
||||
redirect_pry_io(InputTester.new('cat #{$obj.succ!}'), StringIO.new) do
|
||||
Pry.new.rep
|
||||
end
|
||||
|
||||
$obj.should == 'b'
|
||||
end
|
||||
end
|
|
@ -421,4 +421,128 @@ describe Pry::CommandSet do
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'find_command' do
|
||||
it 'should find commands with the right string' do
|
||||
cmd = @set.command('rincewind'){ }
|
||||
@set.find_command('rincewind').should == cmd
|
||||
end
|
||||
|
||||
it 'should not find commands with spaces before' do
|
||||
cmd = @set.command('luggage'){ }
|
||||
@set.find_command(' luggage').should == nil
|
||||
end
|
||||
|
||||
it 'should find commands with arguments after' do
|
||||
cmd = @set.command('vetinari'){ }
|
||||
@set.find_command('vetinari --knock 3').should == cmd
|
||||
end
|
||||
|
||||
it 'should find commands with names containing spaces' do
|
||||
cmd = @set.command('nobby nobbs'){ }
|
||||
@set.find_command('nobby nobbs --steal petty-cash').should == cmd
|
||||
end
|
||||
|
||||
it 'should find command defined by regex' do
|
||||
cmd = @set.command(/(capt|captain) vimes/i){ }
|
||||
@set.find_command('Capt Vimes').should == cmd
|
||||
end
|
||||
|
||||
it 'should find commands defined by regex with arguments' do
|
||||
cmd = @set.command(/(cpl|corporal) Carrot/i){ }
|
||||
@set.find_command('cpl carrot --write-home').should == cmd
|
||||
end
|
||||
|
||||
it 'should not find commands by listing' do
|
||||
cmd = @set.command(/werewol(f|ve)s?/, 'only once a month', :listing => "angua"){ }
|
||||
@set.find_command('angua').should == nil
|
||||
end
|
||||
|
||||
it 'should not find commands without command_prefix' do
|
||||
Pry.config.command_prefix = '%'
|
||||
cmd = @set.command('detritus'){ }
|
||||
@set.find_command('detritus').should == nil
|
||||
Pry.config.command_prefix = ''
|
||||
end
|
||||
|
||||
it "should find commands that don't use the prefix" do
|
||||
Pry.config.command_prefix = '%'
|
||||
cmd = @set.command('colon', 'Sergeant Fred', :use_prefix => false){ }
|
||||
@set.find_command('colon').should == cmd
|
||||
Pry.config.command_prefix = ''
|
||||
end
|
||||
end
|
||||
|
||||
describe '.valid_command?' do
|
||||
it 'should be true for commands that can be found' do
|
||||
cmd = @set.command('archchancellor')
|
||||
@set.valid_command?('archchancellor of_the?(:University)').should == true
|
||||
end
|
||||
|
||||
it 'should be false for commands that can\'' do
|
||||
@set.valid_command?('def monkey(ape)').should == false
|
||||
end
|
||||
end
|
||||
|
||||
describe '.process_line' do
|
||||
|
||||
it 'should return Result.new(false) if there is no matching command' do
|
||||
result = @set.process_line('1 + 42')
|
||||
result.command?.should == false
|
||||
result.void_command?.should == false
|
||||
result.retval.should == nil
|
||||
end
|
||||
|
||||
it 'should return Result.new(true, VOID) if the command is not keep_retval' do
|
||||
@set.command_class('mrs-cake') do
|
||||
def process; 42; end
|
||||
end
|
||||
|
||||
result = @set.process_line('mrs-cake')
|
||||
result.command?.should == true
|
||||
result.void_command?.should == true
|
||||
result.retval.should == Pry::Command::VOID_VALUE
|
||||
end
|
||||
|
||||
it 'should return Result.new(true, retval) if the command is keep_retval' do
|
||||
@set.command_class('magrat', 'the maiden', :keep_retval => true) do
|
||||
def process; 42; end
|
||||
end
|
||||
|
||||
result = @set.process_line('magrat')
|
||||
result.command?.should == true
|
||||
result.void_command?.should == false
|
||||
result.retval.should == 42
|
||||
end
|
||||
|
||||
it 'should pass through context' do
|
||||
ctx = {
|
||||
:eval_string => "bloomers",
|
||||
:pry_instance => Object.new,
|
||||
:output => StringIO.new,
|
||||
:target => binding
|
||||
}
|
||||
@set.command_class('agnes') do
|
||||
define_method(:process) do
|
||||
eval_string.should == ctx[:eval_string]
|
||||
output.should == ctx[:output]
|
||||
target.should == ctx[:target]
|
||||
_pry_.should == ctx[:pry_instance]
|
||||
end
|
||||
end
|
||||
|
||||
@set.process_line('agnes', ctx)
|
||||
end
|
||||
|
||||
it 'should add command_set to context' do
|
||||
set = @set
|
||||
@set.command_class(/nann+y ogg+/) do
|
||||
define_method(:process) do
|
||||
command_set.should == set
|
||||
end
|
||||
end
|
||||
|
||||
@set.process_line('nannnnnny oggggg')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue