diff --git a/lib/pry/config.rb b/lib/pry/config.rb index 999722ae..64e3467c 100644 --- a/lib/pry/config.rb +++ b/lib/pry/config.rb @@ -49,10 +49,11 @@ class Pry # @param [Pry::Hooks] v Only accept `Pry::Hooks` now! def hooks=(v) if v.is_a?(Hash) - raise ObsoleteError, "Hash is now obsolete! Use a Pry::Hooks object instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + @hooks = Pry::Hooks.from_hash(v) + else + @hooks = v end - - @hooks = v end # Get/Set the stack of input objects that a Pry instance switches diff --git a/lib/pry/hooks.rb b/lib/pry/hooks.rb index 2b846b61..2c9ce925 100644 --- a/lib/pry/hooks.rb +++ b/lib/pry/hooks.rb @@ -1,6 +1,31 @@ class Pry + + # Implements a hooks system for Pry. A hook is a callable that is + # associated with an event. A number of events are currently + # provided by Pry, these include: `:when_started`, `:before_session`, `:after_session`. + # A hook must have a name, and is connected with an event by the + # `Pry::Hooks#add_hook` method. + # @example Adding a hook for the `:before_session` event. + # Pry.config.hooks.add_hook(:before_session, :say_hi) do + # puts "hello" + # end class Hooks + # Converts a hash to a `Pry::Hooks` instance. All hooks defined + # this way are anonymous. This functionality is primarily to + # provide backwards-compatibility with the old hash-based hook + # system in Pry versions < 0.9.8 + # @param [Hash] hash The hash to convert to `Pry::Hooks`. + # @return [Pry::Hooks] The resulting `Pry::Hooks` instance. + def self.from_hash(hash) + instance = new + hash.each do |k, v| + instance.add_hook(k, nil, v) + end + + instance + end + def initialize @hooks = {} end @@ -26,14 +51,18 @@ class Pry # FIXME: # This is a hack to alert people of the new API. - def [](*) - raise ObsoleteError, "`Pry.hooks[]` is no longer supported. Please use the new Pry::Hooks API! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + def [](event_name) + warn "`Pry.hooks[]` is deprecated! Please use the new `Pry::Hooks` API! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + + get_hook(event_name, nil) end # FIXME: # This is a hack to alert people of the new API. - def []=(*) - raise ObsoleteError, "`Pry.hooks[]=` is no longer supported. Please use the new Pry::Hooks API! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + def []=(event_name, callable) + warn "`Pry.hooks[]=` is deprecated! Please use the new `Pry::Hooks` API! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + + add_hook(event_name, nil, callable) end # Destructively merge the contents of two `Pry:Hooks` instances. @@ -85,15 +114,23 @@ class Pry def add_hook(event_name, hook_name, callable=nil, &block) @hooks[event_name] ||= [] - # do not allow duplicates - raise ArgumentError, "Hook with name '#{hook_name}' already defined!" if hook_exists?(event_name, hook_name) + # do not allow duplicates, but allow multiple `nil` hooks + # (anonymous hooks) + if hook_exists?(event_name, hook_name) && !hook_name.nil? + raise ArgumentError, "Hook with name '#{hook_name}' already defined!" + end + + if !block && !callable + raise ArgumentError, "Must provide a block or callable." + end + + # ensure we only have one anonymous hook + @hooks[event_name].delete_if { |h, k| h.nil? } if hook_name.nil? if block @hooks[event_name] << [hook_name, block] elsif callable @hooks[event_name] << [hook_name, callable] - else - raise ArgumentError, "Must provide a block or callable." end self @@ -194,6 +231,15 @@ class Pry alias_method :clear, :delete_hooks + # Remove all events and hooks, clearing out the Pry::Hooks + # instance completely. + # @example + # my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" } + # my_hooks.clear_all + def clear_all + @hooks = {} + end + # @param [Symbol] event_name Name of the event. # @param [Symbol] hook_name Name of the hook. # @return [Boolean] Whether the hook by the name `hook_name` diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb index 92c04774..700fff27 100644 --- a/lib/pry/pry_instance.rb +++ b/lib/pry/pry_instance.rb @@ -33,10 +33,11 @@ class Pry # @param [Pry::Hooks] v Only accept `Pry::Hooks` now! def hooks=(v) if v.is_a?(Hash) - raise ObsoleteError, "Error: Hash is now obsolete! Use a Pry::Hooks object instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks" + @hooks = Pry::Hooks.from_hash(v) + else + @hooks = v end - - @hooks = v end # Create a new `Pry` object. diff --git a/test/test_hooks.rb b/test/test_hooks.rb index 1fc1436c..a86098cc 100644 --- a/test/test_hooks.rb +++ b/test/test_hooks.rb @@ -152,7 +152,7 @@ describe Pry::Hooks do hooks_dup.get_hook(:test_hook, :testing).should == @hooks.get_hook(:test_hook, :testing) end - it 'adding a new event to dupped instance should not affect original' do + it 'adding a new event to dupped instance should not affect original' do @hooks.add_hook(:test_hook, :testing) { :none_such } hooks_dup = @hooks.dup @@ -161,7 +161,7 @@ describe Pry::Hooks do hooks_dup.get_hook(:other_test_hook, :testing).should.not == @hooks.get_hook(:other_test_hook, :testing) end - it 'adding a new hook to dupped instance should not affect original' do + it 'adding a new hook to dupped instance should not affect original' do @hooks.add_hook(:test_hook, :testing) { :none_such } hooks_dup = @hooks.dup @@ -417,50 +417,74 @@ describe Pry::Hooks do end end - describe "obsolete API" do - describe "Pry.config.hooks" do - it 'should raise a Pry::ObsoleteError when assigning a hash' do - begin - Pry.config.hooks = {} - rescue => ex - end + describe "anonymous hooks" do + it 'should allow adding of hook without a name' do + @hooks.add_hook(:test_hook, nil) {} + @hooks.hook_count(:test_hook).should == 1 + end - ex.is_a?(Pry::ObsoleteError).should == true + it 'should only allow one anonymous hook to exist' do + @hooks.add_hook(:test_hook, nil) { } + @hooks.add_hook(:test_hook, nil) { } + @hooks.hook_count(:test_hook).should == 1 + end + + it 'should execute most recently added anonymous hook' do + x = nil + y = nil + @hooks.add_hook(:test_hook, nil) { y = 1 } + @hooks.add_hook(:test_hook, nil) { x = 2 } + @hooks.exec_hook(:test_hook) + y.should == nil + x.should == 2 + end + end + + describe "deprecated hash-based API" do + after do + Pry.config.hooks.clear_all if Pry.config.hooks + end + + describe "Pry.config.hooks" do + it 'should allow a hash-assignment' do + Pry.config.hooks = { :before_session => proc { :hello } } + Pry.config.hooks.get_hook(:before_session, nil).call.should == :hello end describe "Pry.config.hooks[]" do - it 'should raise a Pry::ObsoleteError when accessing it in a hash style, Pry.config.hooks[]' do - begin - Pry.config.hooks[:before_session] - rescue => ex - end - - ex.is_a?(Pry::ObsoleteError).should == true + it 'should return the only anonymous hook' do + Pry.config.hooks = { :before_session => proc { :hello } } + Pry.config.hooks[:before_session].call.should == :hello end - it 'should raise a Pry::ObsoleteError when accessing it in a hash style, Pry.config.hooks[]=' do - begin - Pry.config.hooks[:before_session] - rescue => ex - end - - ex.is_a?(Pry::ObsoleteError).should == true + it 'should add an anonymous hook when using Pry.config.hooks[]=' do + Pry.config.hooks[:before_session] = proc { :bing } + Pry.config.hooks.hook_count(:before_session).should == 1 end + it 'should add overwrite previous anonymous hooks with new one when calling Pry.config.hooks[]= multiple times' do + x = nil + Pry.config.hooks[:before_session] = proc { x = 1 } + Pry.config.hooks[:before_session] = proc { x = 2 } + + Pry.config.hooks.exec_hook(:before_session) + Pry.config.hooks.hook_count(:before_session).should == 1 + x.should == 2 + end end - end describe "Pry.start" do - it 'should raise a Pry::ObsoleteError when passing in a :hooks option with a hash' do - begin - Pry.start binding, :hooks => { :before_session => proc { puts 'yo' } } - rescue => ex + it 'should accept a hash for :hooks parameter' do + + redirect_pry_io(InputTester.new("exit-all"), out=StringIO.new) do + Pry.start binding, :hooks => { :before_session => proc { |output, _, _| output.puts 'hello friend' } } end - ex.is_a?(Pry::ObsoleteError).should == true + out.string.should =~ /hello friend/ end end end + end