mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
added support for passing blocks to commands
* Example, using &block: Pry.commands.command "tobina", "desc", :takes_block => true do |&block| block.call end (pry)> tobina { puts "hi" } hi (pry)> * Works with process(&block) too (for create_command) * Works with do/end blocks and {} blocks, both single and multi-line * Example, using command_block: Pry.commands.command "tobina", "desc", :takes_block => true do command_block.call end
This commit is contained in:
parent
85eb6e28be
commit
90d6f3eefb
3 changed files with 227 additions and 4 deletions
|
@ -162,6 +162,13 @@ class Pry
|
|||
attr_accessor :command_set
|
||||
attr_accessor :_pry_
|
||||
|
||||
# The block we pass *into* a command if it defines `:takes_block => true`
|
||||
# @example
|
||||
# my-command do
|
||||
# puts "block content"
|
||||
# end
|
||||
attr_accessor :command_block
|
||||
|
||||
# Run a command from another command.
|
||||
# @param [String] command_string The string that invokes the command
|
||||
# @param [Array] args Further arguments to pass to the command
|
||||
|
@ -281,9 +288,39 @@ class Pry
|
|||
self.arg_string = arg_string
|
||||
self.captures = captures
|
||||
|
||||
pass_block(args) if command_options[:takes_block]
|
||||
|
||||
call_safely(*(captures + args))
|
||||
end
|
||||
|
||||
# Pass a block argument to a command.
|
||||
# @param [Array<String>] args The arguments passed to the command.
|
||||
# We inspect these for a 'do' or a '{' and if we find it we use it
|
||||
# to start a block input sequence. Once we have a complete
|
||||
# block, we save it to an accessor that can be retrieved from the command context.
|
||||
# Note that if we find the 'do' or '{' we delete this and the
|
||||
# elements following it from `args`.
|
||||
def pass_block(args)
|
||||
if !(idx = args.find_index { |v| /^do;?$/ =~ v })
|
||||
idx = args.find_index { |v| /^\{;?$/ =~ v }
|
||||
end
|
||||
|
||||
if idx
|
||||
block_init_string = args.slice!(idx..-1).join(" ")
|
||||
prime_string = "proc #{block_init_string}\n"
|
||||
|
||||
if !_pry_.complete_expression?(prime_string)
|
||||
command_string = _pry_.r(target, prime_string)
|
||||
else
|
||||
command_string = prime_string
|
||||
end
|
||||
|
||||
self.command_block = target.eval(command_string)
|
||||
end
|
||||
end
|
||||
|
||||
private :pass_block
|
||||
|
||||
# Run the command with the given {args}.
|
||||
#
|
||||
# This is a public wrapper around {#call} which ensures all preconditions are met.
|
||||
|
@ -367,7 +404,11 @@ class Pry
|
|||
# @param *String the arguments passed
|
||||
# @return Object the return value of the block
|
||||
def call(*args)
|
||||
instance_exec(*correct_arg_arity(block.arity, args), &block)
|
||||
|
||||
# we need to define this method so we can pass a block in (not
|
||||
# possible with `instance_exec`)
|
||||
class << self; self; end.send(:define_method, :dummy, &block)
|
||||
dummy(*correct_arg_arity(block.arity, args), &command_block)
|
||||
end
|
||||
|
||||
def help; description; end
|
||||
|
@ -403,7 +444,7 @@ class Pry
|
|||
output.puts slop.help
|
||||
void
|
||||
else
|
||||
process(*correct_arg_arity(method(:process).arity, args))
|
||||
process(*correct_arg_arity(method(:process).arity, args), &command_block)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -16,6 +16,14 @@ $VERBOSE = nil
|
|||
# Ensure we do not execute any rc files
|
||||
Pry::RC_FILES.clear
|
||||
|
||||
# inject a variable into a binding
|
||||
def inject_var(name, value, b)
|
||||
Thread.current[:__pry_local__] = value
|
||||
b.eval("#{name} = Thread.current[:__pry_local__]")
|
||||
ensure
|
||||
Thread.current[:__pry_local__] = nil
|
||||
end
|
||||
|
||||
# in case the tests call reset_defaults, ensure we reset them to
|
||||
# amended (test friendly) values
|
||||
class << Pry
|
||||
|
|
|
@ -340,5 +340,179 @@ describe "Pry::Command" do
|
|||
cmd.new.process_line %(grumpos)
|
||||
}.should.raise(Pry::CommandError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "block parameters" do
|
||||
before do
|
||||
@context = Object.new
|
||||
@set.command "walking-spanish", "down the hall", :takes_block => true do |&block|
|
||||
inject_var(:@x, block.call, target)
|
||||
end
|
||||
@set.import Pry::Commands
|
||||
end
|
||||
|
||||
describe ":takes_block option" do
|
||||
it 'should accept block when :takes_block => true' do
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }", "exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
it 'should NOT accept block when :takes_block => false' do
|
||||
@set.command "walking-spanish", "down the hall", :takes_block => false do |&block|
|
||||
inject_var(:@x, block, target)
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }", "exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == nil
|
||||
end
|
||||
end
|
||||
|
||||
it 'should accept multiline blocks' do
|
||||
redirect_pry_io(InputTester.new("walking-spanish do",
|
||||
" :jesus",
|
||||
"end",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
describe "single line blocks" do
|
||||
it 'should accept blocks with do ; end' do
|
||||
redirect_pry_io(InputTester.new("walking-spanish do ; :jesus; end",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
it 'should accept blocks with do; end' do
|
||||
redirect_pry_io(InputTester.new("walking-spanish do; :jesus; end",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
it 'should accept blocks with { }' do
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "block-related content removed from arguments" do
|
||||
describe "block_command" do
|
||||
it "should remove block-related content from arguments" do
|
||||
@set.block_command "walking-spanish", "down the hall", :takes_block => true do |x, y|
|
||||
inject_var(:@x, x, target)
|
||||
inject_var(:@y, y, target)
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == nil
|
||||
@context.instance_variable_get(:@y).should == nil
|
||||
end
|
||||
|
||||
it "should NOT remove block-related content from arguments if :takes_block => false" do
|
||||
@set.block_command "walking-spanish", "down the hall", :takes_block => false do |x, y|
|
||||
inject_var(:@x, x, target)
|
||||
inject_var(:@y, y, target)
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == "{"
|
||||
@context.instance_variable_get(:@y).should == ":jesus"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create_command" do
|
||||
it "should remove block-related content from arguments" do
|
||||
@set.create_command "walking-spanish", "down the hall", :takes_block => true do
|
||||
def process(x, y)
|
||||
inject_var(:@x, x, target)
|
||||
inject_var(:@y, y, target)
|
||||
end
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == nil
|
||||
@context.instance_variable_get(:@y).should == nil
|
||||
end
|
||||
|
||||
it "should NOT remove block-related content from arguments if :takes_block => false" do
|
||||
@set.create_command "walking-spanish", "down the hall", :takes_block => false do
|
||||
def process(x, y)
|
||||
inject_var(:@x, x, target)
|
||||
inject_var(:@y, y, target)
|
||||
end
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == "{"
|
||||
@context.instance_variable_get(:@y).should == ":jesus"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "exposing block parameter" do
|
||||
describe "block_command" do
|
||||
it "should expose block in command_block method" do
|
||||
@set.block_command "walking-spanish", "down the hall", :takes_block => true do
|
||||
inject_var(:@x, command_block.call, target)
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
# test for &block already completed many times in other sections
|
||||
end
|
||||
|
||||
describe "create_command" do
|
||||
it "should expose &block in create_command's process method" do
|
||||
@set.create_command "walking-spanish", "down the hall", :takes_block => true do
|
||||
def process(&block)
|
||||
inject_var(:@x, block.call, target)
|
||||
end
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
|
||||
it "should expose block in command_block method" do
|
||||
@set.create_command "walking-spanish", "down the hall", :takes_block => true do
|
||||
def process
|
||||
inject_var(:@x, command_block.call, target)
|
||||
end
|
||||
end
|
||||
redirect_pry_io(InputTester.new("walking-spanish { :jesus }",
|
||||
"exit-all"), out = StringIO.new) do
|
||||
Pry.start @context, :commands => @set
|
||||
end
|
||||
@context.instance_variable_get(:@x).should == :jesus
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue