diff --git a/lib/pry.rb b/lib/pry.rb index d889bcc1..169ac732 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -4,19 +4,19 @@ require 'pp' require 'pry/helpers/base_helpers' +require 'pry/hooks' + class Pry # The default hooks - display messages when beginning and ending Pry sessions. - DEFAULT_HOOKS = { - :before_session => proc do |out, target, _pry_| - # ensure we're actually in a method - file = target.eval('__FILE__') + DEFAULT_HOOKS = Pry::Hooks.new.add_hook(:before_session) do |out, target, _pry_| + # ensure we're actually in a method + file = target.eval('__FILE__') - # /unknown/ for rbx - if file == Pry.eval_path || (file !~ /(\(.*\))|<.*>/ && file !~ /__unknown__/ && file != "" && file != "-e") - _pry_.process_line("whereami 5", "", target) - end + # /unknown/ for rbx + if file !~ /(\(.*\))|<.*>/ && file !~ /__unknown__/ && file != "" && file != "-e" + _pry_.process_line("whereami 5", "", target) end - } + end # The default print DEFAULT_PRINT = proc do |output, value| diff --git a/lib/pry/hooks.rb b/lib/pry/hooks.rb new file mode 100644 index 00000000..a93b3212 --- /dev/null +++ b/lib/pry/hooks.rb @@ -0,0 +1,52 @@ +class Pry + class Hooks + + def initialize + @hooks = {} + end + + # Add a new callable to be executed for the `name` hook. + # @param [Symbol] name The name of the hook. + # @param [#call] callable The callable. + # @yield The block to use as the callable (if `callable` parameter not provided) + def add_hook(name, callable=nil, &block) + @hooks[name] ||= [] + + if block + @hooks[name] << block + elsif callable + @hooks[name] << callable + else + raise ArgumentError, "Must provide a block or callable." + end + + self + end + + # Execute the list of callables for the `name` hook. + # @param [Symbol] name The name of the hook to execute. + # @param [Array] args The arguments to pass to each callable. + def exec_hook(name, *args, &block) + Array(@hooks[name]).each { |v| v.call(*args, &block) } + end + + # Return the number of callables registered for the `name` hook. + # @param [Symbol] name The name of the hook. + def hook_count(name) + @hooks[name] ||= [] + @hooks[name].size + end + + # Clear the list of callables for the `name` hook. + # @param [Symbol] The name of the hook to delete. + def delete_hook(name) + @hooks[name] = [] + end + + # Clear all hooks. + def reset + @hooks = {} + end + + end +end diff --git a/lib/pry/method.rb b/lib/pry/method.rb index d4c50da3..7de13793 100644 --- a/lib/pry/method.rb +++ b/lib/pry/method.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- class Pry class Method include RbxMethod if Helpers::BaseHelpers.rbx? @@ -178,7 +179,7 @@ class Pry if Helpers::BaseHelpers.rbx? && core? code = core_code elsif pry_method? - code = Pry.new(:input => StringIO.new(Pry.line_buffer[source_line..-1].join), :prompt => proc {""}, :hooks => {}).r + code = Pry.new(:input => StringIO.new(Pry.line_buffer[source_line..-1].join), :prompt => proc {""}, :hooks => Pry::Hooks.new).r else code = @method.source end diff --git a/lib/pry/pry_class.rb b/lib/pry/pry_class.rb index 37528c96..a4c60938 100644 --- a/lib/pry/pry_class.rb +++ b/lib/pry/pry_class.rb @@ -165,7 +165,7 @@ class Pry output = options[:show_output] ? options[:output] : StringIO.new - Pry.new(:output => output, :input => StringIO.new(command_string), :commands => options[:commands], :prompt => proc {""}, :hooks => {}).rep(options[:context]) + Pry.new(:output => output, :input => StringIO.new(command_string), :commands => options[:commands], :prompt => proc {""}, :hooks => Pry::Hooks.new).rep(options[:context]) end def self.default_editor_for_platform diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb index 0221c17f..1111a2e2 100644 --- a/lib/pry/pry_instance.rb +++ b/lib/pry/pry_instance.rb @@ -149,7 +149,7 @@ class Pry # Initialize the repl session. # @param [Binding] target The target binding for the session. def repl_prologue(target) - exec_hook :before_session, output, target, self + hooks.exec_hook :before_session, output, target, self initialize_special_locals(target) @input_array << nil # add empty input so _in_ and _out_ match @@ -161,7 +161,7 @@ class Pry # Clean-up after the repl session. # @param [Binding] target The target binding for the session. def repl_epilogue(target) - exec_hook :after_session, output, target, self + hooks.exec_hook :after_session, output, target, self Pry.active_sessions -= 1 binding_stack.pop @@ -235,9 +235,10 @@ class Pry result = set_last_result(target.eval(code, Pry.eval_path, Pry.current_line), target) result rescue RescuableException => e - set_last_exception(e, target) + result = set_last_exception(e, target) ensure update_input_history(code) + hooks.exec_hook :after_eval, result, self end # Perform a read. @@ -270,6 +271,7 @@ class Pry @suppress_output = true if eval_string =~ /;\Z/ || eval_string.empty? + hooks.exec_hook :after_read, eval_string, self eval_string end diff --git a/test/helper.rb b/test/helper.rb index 31462fc5..0b020824 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -25,7 +25,7 @@ class << Pry Pry.config.history.should_load = false Pry.config.history.should_save = false Pry.config.auto_indent = false - Pry.config.hooks = { } + Pry.config.hooks = Pry::Hooks.new end end diff --git a/test/test_default_commands/test_input.rb b/test/test_default_commands/test_input.rb index 3c69b535..ea9cbbce 100644 --- a/test/test_default_commands/test_input.rb +++ b/test/test_default_commands/test_input.rb @@ -154,7 +154,7 @@ describe "Pry::DefaultCommands::Input" do b = binding b.eval('x = "\"hello\""') redirect_pry_io(InputTester.new("play x", "exit-all"), str_output = StringIO.new) do - Pry.start b, :hooks => {} + Pry.start b, :hooks => Pry::Hooks.new end str_output.string.should =~ /hello/ end @@ -163,7 +163,7 @@ describe "Pry::DefaultCommands::Input" do b = binding b.eval('x = "\"hello\"\n\"goodbye\"\n\"love\""') redirect_pry_io(InputTester.new("play x --lines 1", "exit-all"), str_output = StringIO.new) do - Pry.start b, :hooks => {} + Pry.start b, :hooks => Pry::Hooks.new end str_output.string.should =~ /hello/ str_output.string.should.not =~ /love/ diff --git a/test/test_hooks.rb b/test/test_hooks.rb new file mode 100644 index 00000000..e2a4f64f --- /dev/null +++ b/test/test_hooks.rb @@ -0,0 +1,85 @@ +require 'helper' + +describe Pry::Hooks do + before do + @hooks = Pry::Hooks.new + end + + describe "adding a new hook" do + it 'should not execute hook while adding it' do + @hooks.add_hook(:test_hook) { @test_var = true } + @test_var.should == nil + end + + it 'should return a count of 0 for an empty hook' do + @hooks.hook_count(:test_hook).should == 0 + end + + it 'should create a new hook with a block' do + @hooks.add_hook(:test_hook) { } + @hooks.hook_count(:test_hook).should == 1 + end + + it 'should create a new hook with a callable' do + @hooks.add_hook(:test_hook, proc { }) + @hooks.hook_count(:test_hook).should == 1 + end + + it 'should use just block if given both block and callable' do + @hooks.add_hook(:test_hook, proc { }) { } + @hooks.hook_count(:test_hook).should == 1 + end + + it 'should raise if not given a block or any other object' do + lambda { @hooks.add_hook(:test_hook) }.should.raise ArgumentError + end + + it 'should create a hook with multiple callables' do + @hooks.add_hook(:test_hook) {} + @hooks.add_hook(:test_hook) {} + @hooks.hook_count(:test_hook).should == 2 + end + end + + describe "executing a hook" do + before do + @test_var = nil + end + + it 'should execute block hook' do + @hooks.add_hook(:test_hook) { @test_var = true } + @hooks.exec_hook(:test_hook) + @test_var.should == true + end + + it 'should execute proc hook' do + @hooks.add_hook(:test_hook, proc { @test_var = true }) + @hooks.exec_hook(:test_hook) + @test_var.should == true + end + + it 'should execute a general callable hook' do + callable = Object.new.tap do |obj| + obj.instance_variable_set(:@test_var, nil) + class << obj + attr_accessor :test_var + def call() @test_var = true; end + end + end + + @hooks.add_hook(:test_hook, callable) + @hooks.exec_hook(:test_hook) + callable.test_var.should == true + end + + it 'should execute multiple callables for a hook if more than one is defined' do + x = nil + y = nil + @hooks.add_hook(:test_hook) { x = true } + @hooks.add_hook(:test_hook) { y = true } + @hooks.exec_hook(:test_hook) + x.should == true + y.should == true + end + end +end diff --git a/test/test_pry.rb b/test/test_pry.rb index e8c0338d..93f8b031 100644 --- a/test/test_pry.rb +++ b/test/test_pry.rb @@ -1281,10 +1281,9 @@ describe Pry do it 'should set the hooks default, and the default should be overridable' do Pry.input = InputTester.new("exit-all") - Pry.hooks = { - :before_session => proc { |out,_,_| out.puts "HELLO" }, - :after_session => proc { |out,_,_| out.puts "BYE" } - } + Pry.hooks = Pry::Hooks.new. + add_hook(:before_session) { |out,_,_| out.puts "HELLO" }. + add_hook(:after_session) { |out,_,_| out.puts "BYE" } str_output = StringIO.new Pry.new(:output => str_output).repl @@ -1295,10 +1294,9 @@ describe Pry do str_output = StringIO.new Pry.new(:output => str_output, - :hooks => { - :before_session => proc { |out,_,_| out.puts "MORNING" }, - :after_session => proc { |out,_,_| out.puts "EVENING" } - } + :hooks => Pry::Hooks.new. + add_hook( :before_session) { |out,_,_| out.puts "MORNING" }. + add_hook(:after_session) { |out,_,_| out.puts "EVENING" } ).repl str_output.string.should =~ /MORNING/ @@ -1308,9 +1306,8 @@ describe Pry do Pry.input.rewind str_output = StringIO.new Pry.new(:output => str_output, - :hooks => { - :before_session => proc { |out,_,_| out.puts "OPEN" } - } + :hooks => Pry::Hooks.new. + add_hook(:before_session) { |out,_,_| out.puts "OPEN" } ).repl str_output.string.should =~ /OPEN/ @@ -1318,9 +1315,8 @@ describe Pry do Pry.input.rewind str_output = StringIO.new Pry.new(:output => str_output, - :hooks => { - :after_session => proc { |out,_,_| out.puts "CLOSE" } - } + :hooks => Pry::Hooks.new. + add_hook(:after_session) { |out,_,_| out.puts "CLOSE" } ).repl str_output.string.should =~ /CLOSE/