From 2fdfbad0399f15e8d720d85dd421d42557a4b738 Mon Sep 17 00:00:00 2001 From: John Mair Date: Thu, 12 Jan 2012 16:54:48 +1300 Subject: [PATCH] added Pry::Hooks#dup/merge/merge! and tests --- lib/pry/cli.rb | 2 +- lib/pry/hooks.rb | 53 ++++++++++++++++++++ pry.gemspec | 24 ++++----- test/test_hooks.rb | 119 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 13 deletions(-) diff --git a/lib/pry/cli.rb b/lib/pry/cli.rb index b699031c..56ba1981 100644 --- a/lib/pry/cli.rb +++ b/lib/pry/cli.rb @@ -14,7 +14,7 @@ class Pry # @return [Array] The Procs that process the parsed options. attr_accessor :option_processors - # Add another set of CLI options + # Add another set of CLI options (a Slop block) def add_options(&block) if options old_options = options diff --git a/lib/pry/hooks.rb b/lib/pry/hooks.rb index 15dabd9f..2c7d954f 100644 --- a/lib/pry/hooks.rb +++ b/lib/pry/hooks.rb @@ -5,6 +5,59 @@ class Pry @hooks = {} end + # Ensure that duplicates have their @hooks object + def initialize_copy(orig) + hooks_dup = @hooks.dup + @hooks.each do |k, v| + hooks_dup[k] = v.dup + end + + @hooks = hooks_dup + end + + def hooks + @hooks + end + protected :hooks + + # Destructively merge the contents of two `Pry:Hooks` instances. + # @param [Pry::Hooks] other The `Pry::Hooks` instance to merge + + + + # TODO: implement by iterating over parameter and only overwriting + # elements in receiver if they exist in parameter, and adding + # other paramater elements to the end of the original's array + def merge!(other) + @hooks.merge!(other.dup.hooks) do |key, v1, v2| + merge_arrays(v1, v2) + end + + self + end + + def merge_arrays(array1, array2) + keys = array1.map {|(k,v)| k} + hash = {} + array1.each{|(k,v)| hash[k]=v} + keys.push(*array2.map {|(k,v)| k}) + array2.each{|(k,v)| hash[k]=v} + result = [] + keys.uniq! + keys.each{|k| result.push([k,hash[k]]) } + result + end + private :merge_arrays + + # Return a new `Pry::Hooks` instance containing a merge of the contents of two `Pry:Hooks` instances, + # @param [Pry::Hooks] other The `Pry::Hooks` instance to merge + # @return [Pry::Hooks] The new hash. + def merge(other) + self.dup.tap do |v| + v.merge!(other) + end + end + # Add a new hook to be executed for the `name` even. # @param [Symbol] event_name The name of the event. # @param [Symbol] hook_name The name of the hook. diff --git a/pry.gemspec b/pry.gemspec index 13a9d6db..e646a6e5 100644 --- a/pry.gemspec +++ b/pry.gemspec @@ -1,21 +1,21 @@ # -*- encoding: utf-8 -*- Gem::Specification.new do |s| - s.name = "pry" + s.name = %q{pry} s.version = "0.9.8pre3" s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= - s.authors = ["John Mair (banisterfiend)"] - s.date = "2012-01-06" - s.description = "An IRB alternative and runtime developer console" - s.email = "jrmair@gmail.com" - s.executables = ["pry"] - s.files = [".document", ".gemtest", ".gitignore", ".travis.yml", ".yardopts", "CHANGELOG", "CONTRIBUTORS", "Gemfile", "LICENSE", "README.markdown", "Rakefile", "TODO", "bin/pry", "examples/example_basic.rb", "examples/example_command_override.rb", "examples/example_commands.rb", "examples/example_hooks.rb", "examples/example_image_edit.rb", "examples/example_input.rb", "examples/example_input2.rb", "examples/example_output.rb", "examples/example_print.rb", "examples/example_prompt.rb", "examples/helper.rb", "lib/pry.rb", "lib/pry/cli.rb", "lib/pry/command.rb", "lib/pry/command_set.rb", "lib/pry/commands.rb", "lib/pry/completion.rb", "lib/pry/config.rb", "lib/pry/core_extensions.rb", "lib/pry/custom_completions.rb", "lib/pry/default_commands/basic.rb", "lib/pry/default_commands/context.rb", "lib/pry/default_commands/documentation.rb", "lib/pry/default_commands/easter_eggs.rb", "lib/pry/default_commands/gems.rb", "lib/pry/default_commands/input.rb", "lib/pry/default_commands/introspection.rb", "lib/pry/default_commands/ls.rb", "lib/pry/default_commands/shell.rb", "lib/pry/extended_commands/experimental.rb", "lib/pry/extended_commands/user_command_api.rb", "lib/pry/helpers.rb", "lib/pry/helpers/base_helpers.rb", "lib/pry/helpers/command_helpers.rb", "lib/pry/helpers/options_helpers.rb", "lib/pry/helpers/text.rb", "lib/pry/history.rb", "lib/pry/history_array.rb", "lib/pry/hooks.rb", "lib/pry/indent.rb", "lib/pry/method.rb", "lib/pry/plugins.rb", "lib/pry/pry_class.rb", "lib/pry/pry_instance.rb", "lib/pry/rbx_method.rb", "lib/pry/rbx_path.rb", "lib/pry/version.rb", "lib/pry/wrapped_module.rb", "man/pry.1", "man/pry.1.html", "man/pry.1.ronn", "pry.gemspec", "test/helper.rb", "test/test_cli.rb", "test/test_command.rb", "test/test_command_helpers.rb", "test/test_command_set.rb", "test/test_completion.rb", "test/test_default_commands.rb", "test/test_default_commands/test_context.rb", "test/test_default_commands/test_documentation.rb", "test/test_default_commands/test_gems.rb", "test/test_default_commands/test_input.rb", "test/test_default_commands/test_introspection.rb", "test/test_default_commands/test_ls.rb", "test/test_default_commands/test_shell.rb", "test/test_exception_whitelist.rb", "test/test_history_array.rb", "test/test_hooks.rb", "test/test_indent.rb", "test/test_input_stack.rb", "test/test_method.rb", "test/test_pry.rb", "test/test_pry_history.rb", "test/test_pry_output.rb", "test/test_special_locals.rb", "test/test_syntax_checking.rb", "test/test_wrapped_module.rb", "test/testrc", "test/testrcbad", "wiki/Customizing-pry.md", "wiki/Home.md"] - s.homepage = "http://pry.github.com" - s.require_paths = ["lib"] - s.rubygems_version = "1.8.11" - s.summary = "An IRB alternative and runtime developer console" - s.test_files = ["test/helper.rb", "test/test_cli.rb", "test/test_command.rb", "test/test_command_helpers.rb", "test/test_command_set.rb", "test/test_completion.rb", "test/test_default_commands.rb", "test/test_default_commands/test_context.rb", "test/test_default_commands/test_documentation.rb", "test/test_default_commands/test_gems.rb", "test/test_default_commands/test_input.rb", "test/test_default_commands/test_introspection.rb", "test/test_default_commands/test_ls.rb", "test/test_default_commands/test_shell.rb", "test/test_exception_whitelist.rb", "test/test_history_array.rb", "test/test_hooks.rb", "test/test_indent.rb", "test/test_input_stack.rb", "test/test_method.rb", "test/test_pry.rb", "test/test_pry_history.rb", "test/test_pry_output.rb", "test/test_special_locals.rb", "test/test_syntax_checking.rb", "test/test_wrapped_module.rb", "test/testrc", "test/testrcbad"] + s.authors = [%q{John Mair (banisterfiend)}] + s.date = %q{2012-01-12} + s.description = %q{An IRB alternative and runtime developer console} + s.email = %q{jrmair@gmail.com} + s.executables = [%q{pry}] + s.files = [%q{.document}, %q{.gemtest}, %q{.gitignore}, %q{.travis.yml}, %q{.yardopts}, %q{CHANGELOG}, %q{CONTRIBUTORS}, %q{Gemfile}, %q{LICENSE}, %q{README.markdown}, %q{Rakefile}, %q{TODO}, %q{bin/pry}, %q{examples/example_basic.rb}, %q{examples/example_command_override.rb}, %q{examples/example_commands.rb}, %q{examples/example_hooks.rb}, %q{examples/example_image_edit.rb}, %q{examples/example_input.rb}, %q{examples/example_input2.rb}, %q{examples/example_output.rb}, %q{examples/example_print.rb}, %q{examples/example_prompt.rb}, %q{examples/helper.rb}, %q{lib/pry.rb}, %q{lib/pry/cli.rb}, %q{lib/pry/command.rb}, %q{lib/pry/command_set.rb}, %q{lib/pry/commands.rb}, %q{lib/pry/completion.rb}, %q{lib/pry/config.rb}, %q{lib/pry/core_extensions.rb}, %q{lib/pry/custom_completions.rb}, %q{lib/pry/default_commands/basic.rb}, %q{lib/pry/default_commands/context.rb}, %q{lib/pry/default_commands/documentation.rb}, %q{lib/pry/default_commands/easter_eggs.rb}, %q{lib/pry/default_commands/gems.rb}, %q{lib/pry/default_commands/input.rb}, %q{lib/pry/default_commands/introspection.rb}, %q{lib/pry/default_commands/ls.rb}, %q{lib/pry/default_commands/shell.rb}, %q{lib/pry/extended_commands/experimental.rb}, %q{lib/pry/extended_commands/user_command_api.rb}, %q{lib/pry/helpers.rb}, %q{lib/pry/helpers/base_helpers.rb}, %q{lib/pry/helpers/command_helpers.rb}, %q{lib/pry/helpers/options_helpers.rb}, %q{lib/pry/helpers/text.rb}, %q{lib/pry/history.rb}, %q{lib/pry/history_array.rb}, %q{lib/pry/hooks.rb}, %q{lib/pry/indent.rb}, %q{lib/pry/method.rb}, %q{lib/pry/plugins.rb}, %q{lib/pry/pry_class.rb}, %q{lib/pry/pry_instance.rb}, %q{lib/pry/rbx_method.rb}, %q{lib/pry/rbx_path.rb}, %q{lib/pry/version.rb}, %q{lib/pry/wrapped_module.rb}, %q{man/pry.1}, %q{man/pry.1.html}, %q{man/pry.1.ronn}, %q{pry.gemspec}, %q{test/helper.rb}, %q{test/test_cli.rb}, %q{test/test_command.rb}, %q{test/test_command_helpers.rb}, %q{test/test_command_set.rb}, %q{test/test_completion.rb}, %q{test/test_default_commands.rb}, %q{test/test_default_commands/test_context.rb}, %q{test/test_default_commands/test_documentation.rb}, %q{test/test_default_commands/test_gems.rb}, %q{test/test_default_commands/test_input.rb}, %q{test/test_default_commands/test_introspection.rb}, %q{test/test_default_commands/test_ls.rb}, %q{test/test_default_commands/test_shell.rb}, %q{test/test_exception_whitelist.rb}, %q{test/test_history_array.rb}, %q{test/test_hooks.rb}, %q{test/test_indent.rb}, %q{test/test_input_stack.rb}, %q{test/test_method.rb}, %q{test/test_pry.rb}, %q{test/test_pry_history.rb}, %q{test/test_pry_output.rb}, %q{test/test_special_locals.rb}, %q{test/test_syntax_checking.rb}, %q{test/test_wrapped_module.rb}, %q{test/testrc}, %q{test/testrcbad}, %q{wiki/Customizing-pry.md}, %q{wiki/Home.md}] + s.homepage = %q{http://pry.github.com} + s.require_paths = [%q{lib}] + s.rubygems_version = %q{1.8.6} + s.summary = %q{An IRB alternative and runtime developer console} + s.test_files = [%q{test/helper.rb}, %q{test/test_cli.rb}, %q{test/test_command.rb}, %q{test/test_command_helpers.rb}, %q{test/test_command_set.rb}, %q{test/test_completion.rb}, %q{test/test_default_commands.rb}, %q{test/test_default_commands/test_context.rb}, %q{test/test_default_commands/test_documentation.rb}, %q{test/test_default_commands/test_gems.rb}, %q{test/test_default_commands/test_input.rb}, %q{test/test_default_commands/test_introspection.rb}, %q{test/test_default_commands/test_ls.rb}, %q{test/test_default_commands/test_shell.rb}, %q{test/test_exception_whitelist.rb}, %q{test/test_history_array.rb}, %q{test/test_hooks.rb}, %q{test/test_indent.rb}, %q{test/test_input_stack.rb}, %q{test/test_method.rb}, %q{test/test_pry.rb}, %q{test/test_pry_history.rb}, %q{test/test_pry_output.rb}, %q{test/test_special_locals.rb}, %q{test/test_syntax_checking.rb}, %q{test/test_wrapped_module.rb}, %q{test/testrc}, %q{test/testrcbad}] if s.respond_to? :specification_version then s.specification_version = 3 diff --git a/test/test_hooks.rb b/test/test_hooks.rb index 424dddd7..18bd43da 100644 --- a/test/test_hooks.rb +++ b/test/test_hooks.rb @@ -53,6 +53,125 @@ describe Pry::Hooks do end end + describe "Pry::Hooks#merge" do + describe "merge!" do + it 'should merge in the Pry::Hooks' do + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} + h2 = Pry::Hooks.new + + h2.merge!(h1) + h2.get_hook(:test_hook, :testing).should == h1.get_hook(:test_hook, :testing) + end + + it 'should not share merged elements with original' do + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} + h2 = Pry::Hooks.new + + h2.merge!(h1) + h2.add_hook(:test_hook, :testing2) {} + h2.get_hook(:test_hook, :testing2).should.not == h1.get_hook(:test_hook, :testing2) + end + + it 'should NOT overwrite hooks belonging to shared event in receiver' do + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} + callable = proc {} + h2 = Pry::Hooks.new.add_hook(:test_hook, :testing2, callable) + + h2.merge!(h1) + h2.get_hook(:test_hook, :testing2).should == callable + end + + it 'should overwrite identical hook in receiver' do + callable1 = proc { :one } + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing, callable1) + callable2 = proc { :two } + h2 = Pry::Hooks.new.add_hook(:test_hook, :testing, callable2) + + h2.merge!(h1) + h2.get_hook(:test_hook, :testing).should == callable1 + h2.hook_count(:test_hook).should == 1 + end + + it 'should preserve hook order' do + name = "" + h1 = Pry::Hooks.new + h1.add_hook(:test_hook, :testing3) { name << "h" } + h1.add_hook(:test_hook, :testing4) { name << "n" } + + h2 = Pry::Hooks.new + h2.add_hook(:test_hook, :testing1) { name << "j" } + h2.add_hook(:test_hook, :testing2) { name << "o" } + + h2.merge!(h1) + h2.exec_hook(:test_hook) + + name.should == "john" + end + + describe "merge" do + it 'should return a fresh, independent instance' do + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} + h2 = Pry::Hooks.new + + h3 = h2.merge(h1) + h3.should.not == h1 + h3.should.not == h2 + end + + it 'should contain hooks from original instance' do + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} + h2 = Pry::Hooks.new.add_hook(:test_hook2, :testing) {} + + h3 = h2.merge(h1) + h3.get_hook(:test_hook, :testing).should == h1.get_hook(:test_hook, :testing) + h3.get_hook(:test_hook2, :testing).should == h2.get_hook(:test_hook2, :testing) + end + + it 'should not affect original instances when new hooks are added' do + h1 = Pry::Hooks.new.add_hook(:test_hook, :testing) {} + h2 = Pry::Hooks.new.add_hook(:test_hook2, :testing) {} + + h3 = h2.merge(h1) + h3.add_hook(:test_hook3, :testing) {} + + h1.get_hook(:test_hook3, :testing).should == nil + h2.get_hook(:test_hook3, :testing).should == nil + end + end + + end + end + + describe "dupping a Pry::Hooks instance" do + it 'should share hooks with original' do + @hooks.add_hook(:test_hook, :testing) do + :none_such + end + + hooks_dup = @hooks.dup + 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 + @hooks.add_hook(:test_hook, :testing) { :none_such } + hooks_dup = @hooks.dup + + hooks_dup.add_hook(:other_test_hook, :testing) { :okay_man } + + 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 + @hooks.add_hook(:test_hook, :testing) { :none_such } + hooks_dup = @hooks.dup + + hooks_dup.add_hook(:test_hook, :testing2) { :okay_man } + + hooks_dup.get_hook(:test_hook, :testing2).should.not == @hooks.get_hook(:test_hook, :testing2) + end + + end + describe "getting hooks" do describe "get_hook" do it 'should return the correct requested hook' do