diff --git a/lib/pry/command_processor.rb b/lib/pry/command_processor.rb index 68fc254a..cf07b41d 100644 --- a/lib/pry/command_processor.rb +++ b/lib/pry/command_processor.rb @@ -161,17 +161,19 @@ class Pry # Execute a Pry command. # This method should not need to be invoked directly. # @param [Binding] target The target of the Pry session. - # @param [String] command The name of the command to be run. + # @param [Pry::CommandSet::Command] command The command object. # @param [Hash] options The options to set on the Commands object. # @param [Array] args The command arguments. # @return [Object] The value returned by the command def execute_command(target, command, options, *args) ret = nil - if command.block.is_a?(Proc) + if command.callable.is_a?(Proc) context = CommandContext.new else - context = command.block + + # in the case of non-procs the callable *is* the context + context = command.callable end # set some useful methods to be used by the action blocks @@ -187,19 +189,19 @@ class Pry end def setup_context(target, command, context, options) - context.opts = options - context.target = target - context.target_self = target.eval('self') - context.output = output - context.captures = options[:captures] - context.eval_string = options[:eval_string] - context.arg_string = options[:arg_string] + context.opts = options + context.target = target + context.target_self = target.eval('self') + context.output = output + context.captures = options[:captures] + context.eval_string = options[:eval_string] + context.arg_string = options[:arg_string] context.command_set = commands context.command_name = command.options[:listing] - context._pry_ = @pry_instance + context._pry_ = @pry_instance - context.command_processor = self + context.command_processor = self end end end diff --git a/lib/pry/command_set.rb b/lib/pry/command_set.rb index 61650ff8..0422f283 100644 --- a/lib/pry/command_set.rb +++ b/lib/pry/command_set.rb @@ -8,17 +8,19 @@ class Pry # This class is used to create sets of commands. Commands can be imported from # different sets, aliased, removed, etc. class CommandSet - class Command < Struct.new(:name, :description, :options, :block) + class Command < Struct.new(:name, :description, :options, :callable) def call(context, *args) if stub_block = options[:stub_info] context.instance_eval(&stub_block) else - if block.is_a?(Proc) - ret = context.instance_exec(*correct_arg_arity(block.arity, args), &block) + if callable.is_a?(Proc) + ret = context.instance_exec(*correct_arg_arity(callable.arity, args), &callable) else - ret = block.call(*correct_arg_arity(block.method(:call).arity, args)) + + # in the case of non-procs the callable *is* the context + ret = callable.call(*correct_arg_arity(callable.method(:call).arity, args)) end if options[:keep_retval] @@ -157,13 +159,18 @@ class Pry # end def before_command(name, &block) cmd = find_command_by_name_or_listing(name) - prev_block = cmd.block + prev_callable = cmd.callable wrapper_block = proc do |*args| instance_exec(*args, &block) - instance_exec(*args, &prev_block) + + if prev_callable.is_a?(Proc) + instance_exec(*args, &prev_callable) + else + prev_callable.call(*args) + end end - cmd.block = wrapper_block + cmd.callable = wrapper_block end # Execute a block of code after a command is invoked. The block also @@ -177,13 +184,18 @@ class Pry # end def after_command(name, &block) cmd = find_command_by_name_or_listing(name) - prev_block = cmd.block + prev_callable = cmd.callable wrapper_block = proc do |*args| - instance_exec(*args, &prev_block) + if prev_callable.is_a?(Proc) + instance_exec(*args, &prev_callable) + else + prev_callable.call(*args) + end + instance_exec(*args, &block) end - cmd.block = wrapper_block + cmd.callable = wrapper_block end def each &block @@ -279,6 +291,8 @@ class Pry # @raise [NoCommandError] If the command is not defined in this set def run_command(context, command_name, *args) command = commands[command_name] + + context.extend helper_module if command.nil? diff --git a/lib/pry/default_commands/introspection.rb b/lib/pry/default_commands/introspection.rb index b84585bc..2a5f5064 100644 --- a/lib/pry/default_commands/introspection.rb +++ b/lib/pry/default_commands/introspection.rb @@ -69,7 +69,7 @@ class Pry end if find_command(command_name) - block = Pry::Method.new(find_command(command_name).block) + block = Pry::Method.new(find_command(command_name).callable) next unless block.source set_file_and_dir_locals(block.source_file) diff --git a/test/test_class_based_commands.rb b/test/test_class_based_commands.rb new file mode 100644 index 00000000..267ef15a --- /dev/null +++ b/test/test_class_based_commands.rb @@ -0,0 +1,61 @@ +require 'helper' + +# integration tests +describe "integration tests for class-based commands" do + before do + @set = Pry::CommandSet.new + end + + it 'should invoke a class-based command from the REPL' do + c = Class.new(Pry::CommandContext) do + def call + output.puts "yippee!" + end + end + + @set.command 'foo', "desc", :definition => c.new + @set.import_from Pry::Commands, "exit-all" + + redirect_pry_io(InputTester.new("foo", "exit-all"), out =StringIO.new) do + Pry.start binding, :commands => @set + end + + out.string.should =~ /yippee!/ + end + + it 'should return specified value with :keep_retval => true' do + c = Class.new(Pry::CommandContext) do + def call + :i_enjoyed_the_song_new_flame_by_simply_red_as_a_child_wandering_around_supermarkets + end + end + + @set.command 'foo', "desc", :keep_retval => true, :definition => c.new + @set.import_from Pry::Commands, "exit-all" + + redirect_pry_io(InputTester.new("foo", "exit-all"), out =StringIO.new) do + Pry.start binding, :commands => @set + end + + out.string.should =~ /i_enjoyed_the_song_new_flame_by_simply_red_as_a_child_wandering_around_supermarkets/ + end + + it 'should NOT return specified value with :keep_retval => false' do + c = Class.new(Pry::CommandContext) do + def call + :i_enjoyed_the_song_new_flame_by_simply_red_as_a_child_wandering_around_supermarkets + end + end + + @set.command 'foo', "desc", :keep_retval => false, :definition => c.new + @set.import_from Pry::Commands, "exit-all" + + redirect_pry_io(InputTester.new("foo", "exit-all"), out =StringIO.new) do + Pry.start binding, :commands => @set + end + + out.string.should !~ /i_enjoyed_the_song_new_flame_by_simply_red_as_a_child_wandering_around_supermarkets/ + end + + +end diff --git a/test/test_command_set.rb b/test/test_command_set.rb index 430979ac..70f517a3 100644 --- a/test/test_command_set.rb +++ b/test/test_command_set.rb @@ -376,7 +376,7 @@ describe Pry::CommandSet do it 'should share the context with the original command' do @ctx.target = "test target string" - after_val = nil + after_val = nil orig_val = nil @set.command('foo') { orig_val = target } @set.after_command('foo') { after_val = target } @@ -429,7 +429,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command ctx, 'foo', 1, 2, 3 end @@ -444,7 +444,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command ctx, 'foo', 1 end @@ -458,7 +458,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command ctx, 'foo', 1, 2, 3, 4 end @@ -471,7 +471,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command(ctx, 'foo').should == Pry::CommandContext::VOID_VALUE end @@ -484,7 +484,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :keep_retval => true, :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command(ctx, 'foo').should == :i_have_a_dog_called_tobina end @@ -503,7 +503,7 @@ describe Pry::CommandSet do end end - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command ctx, 'foo' end @@ -518,7 +518,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.run_command ctx, 'foo' @set.run_command ctx, 'foo' ctx.state.should == 2 @@ -535,7 +535,7 @@ describe Pry::CommandSet do @set.command 'foo', "desc", :definition => c.new - ctx = @set.commands['foo'].block + ctx = @set.commands['foo'].callable @set.before_command('foo') { foo << 2 } @set.run_command(ctx, 'foo') @@ -543,5 +543,24 @@ describe Pry::CommandSet do end end + describe "after_command" do + it 'should be called before the original command' do + foo = [] + c = Class.new(Pry::CommandContext) do + define_method(:call) do + foo << 1 + end + end + + @set.command 'foo', "desc", :definition => c.new + + ctx = @set.commands['foo'].callable + @set.after_command('foo') { foo << 2 } + @set.run_command(ctx, 'foo') + + foo.should == [1, 2] + end + end + end end diff --git a/test/test_pry.rb b/test/test_pry.rb index 53b1b99b..fcc9f2bf 100644 --- a/test/test_pry.rb +++ b/test/test_pry.rb @@ -2,16 +2,6 @@ require 'helper' describe Pry do - # if RUBY_PLATFORM !~ /mingw/ && RUBY_PLATFORM !~ /mswin/ && RUBY_PLATFORM != 'java' - # describe 'warning emissions' do - # it 'should emit no warnings' do - # Open4.popen4 'ruby -I lib -rubygems -r"pry" -W -e "exit"' do |pid, stdin, stdout, stderr| - # stderr.read.empty?.should == true - # end - # end - # end - # end - if RUBY_VERSION =~ /1.9/ describe "Exotic object support" do # regression test for exotic object support @@ -792,7 +782,7 @@ describe Pry do str_output = StringIO.new Pry.new(:input => StringIO.new("hello\n"), :output => str_output, :commands => klass).rep str_output.string.should =~ /nil/ - str_output.string.should =~ /=>/ + str_output.string.should =~ /=>/ end it 'should define a command that keeps its return value but does not return when value is void' do @@ -806,7 +796,7 @@ 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 + 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") @@ -818,45 +808,45 @@ describe Pry do 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 + 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 - end - str_output = StringIO.new - Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => str_output, :commands => klass).rep + 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 - output.puts "hello world" + 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 - Pry.commands = klass - str_output = StringIO.new - Pry.new(:input => InputTester.new("hello"), :output => str_output).rep - str_output.string.should =~ /hello world/ + it 'should set the commands default, and the default should be overridable' do + klass = Pry::CommandSet.new do + command "hello" do + output.puts "hello world" + end + end - other_klass = Pry::CommandSet.new do + Pry.commands = klass + + str_output = StringIO.new + Pry.new(:input => InputTester.new("hello"), :output => str_output).rep + str_output.string.should =~ /hello world/ + + other_klass = Pry::CommandSet.new do command "goodbye", "" do output.puts "goodbye world" end @@ -896,7 +886,7 @@ describe Pry do klass = Pry::CommandSet.new do alias_command "help2", "help" end - klass.commands["help2"].block.should == klass.commands["help"].block + klass.commands["help2"].callable.should == klass.commands["help"].callable end it 'should change description of a command using desc' do @@ -1312,7 +1302,7 @@ describe Pry do str_output = StringIO.new Pry.new(:output => str_output, :hooks => Pry::Hooks.new. - add_hook(:before_session, :my_name) { |out,_,_| out.puts "OPEN" } + add_hook(:before_session, :my_name) { |out,_,_| out.puts "OPEN" } ).repl str_output.string.should =~ /OPEN/ @@ -1321,7 +1311,7 @@ describe Pry do str_output = StringIO.new Pry.new(:output => str_output, :hooks => Pry::Hooks.new. - add_hook(:after_session, :my_name) { |out,_,_| out.puts "CLOSE" } + add_hook(:after_session, :my_name) { |out,_,_| out.puts "CLOSE" } ).repl str_output.string.should =~ /CLOSE/