From 548b4f82d8bab7d52759d34b1c69350eaac6c4f3 Mon Sep 17 00:00:00 2001 From: John Mair Date: Fri, 21 Jan 2011 03:41:41 +1300 Subject: [PATCH] added import_from, delete, run to command API. Also added examples, added more tests, and changed the way that commands work using split instead of regex captures. Also added output and trget methods to Pry::CommandBase --- README.markdown | 84 ++++++++++++++++---- examples/example_command_override.rb | 27 +++++++ examples/example_commands.rb | 36 +++++++++ examples/example_hooks.rb | 12 +++ examples/example_image_edit.rb | 70 +++++++++++++++++ examples/example_input.rb | 10 +++ examples/example_output.rb | 14 ++++ examples/example_print.rb | 9 +++ examples/example_prompt.rb | 12 +++ lib/pry/command_base.rb | 83 +++++++++++++------- lib/pry/commands.rb | 41 +++++----- lib/pry/pry_instance.rb | 34 ++++---- test/test.rb | 111 +++++++++++++++++++++++++-- 13 files changed, 458 insertions(+), 85 deletions(-) create mode 100644 examples/example_command_override.rb create mode 100644 examples/example_commands.rb create mode 100644 examples/example_hooks.rb create mode 100644 examples/example_image_edit.rb create mode 100644 examples/example_input.rb create mode 100644 examples/example_output.rb create mode 100644 examples/example_print.rb create mode 100644 examples/example_prompt.rb diff --git a/README.markdown b/README.markdown index b4f02480..a92a5a3c 100644 --- a/README.markdown +++ b/README.markdown @@ -343,14 +343,19 @@ and executed before a Ruby eval takes place. Pry comes with a default command set (`Pry::Commands`), but these commands can be augmented or overriden by user-specified ones. +The Pry command API is quite sophisticated supporting features such as: +command set inheritance, importing of specific commands from another +command set, deletion of commands, calling of commands within other +commands, and so on. + A valid Pry command object must inherit from `Pry::CommandBase` and use the special command API: #### Example: Defining a command object and setting it globally class MyCommands < Pry::CommandBase - command "hello", "Output hello to the user." do |name| - opts[:output].puts "hello #{name}!" + command greet", "Greet the user." do |name| + output.puts "Hello #{name.capitalize}, how are you?" end end @@ -358,8 +363,8 @@ A valid Pry command object must inherit from Then inside a pry session: - pry(main)> hello john - hello john! + pry(main)> greet john + hello John, how are you? => nil #### Example: Using a command object in a specific session @@ -367,7 +372,7 @@ Then inside a pry session: As in the case of `input` and `output`: ##### At session start: -nnnnn + Pry.start(self, :commands => MyCommands) ##### At runtime: @@ -377,9 +382,9 @@ nnnnn #### The command API The command API is defined by the `Pry::CommandBase` class (hence why -all commands must inherit from it). The API works as follows: +all commands must inherit from it or a subclass). The API works as follows: -The `command` method defines a new command, its parameter is the +* The `command` method defines a new command, its parameter is the name of the command and an optional second parameter is a description of the command. @@ -398,23 +403,64 @@ for the command name - all these strings will be valid names for the command. command ["ls", "dir"], "show a list of local vars" do - opts[:output].puts opts[:target].eval("local_variables") + output.puts target.eval("local_variables") end +* The `delete` method deletes a command or a group of a commands; it + can be useful when inheriting from another command set when you decide + to keep only a portion of inherited commands. -#### opts hash + class MyCommands < Pry::Commands + delete "show_method", "show_imethod" + end -Note that in the example above we are using `opts[:output]` for output; this is the output -object in use by the current pry session. Other hash values accessible -within a `command` block include: +* The `import_from` method enables you to specifically select which + commands will be copied across from another command set, useful when + you only want a small number of commands and so inheriting and then + deleting would be inefficient. The first parameter to `import_from` + is the class to import from and the other paramters are the names of + the commands to import: + + class MyCommands < Pry::CommandBase + import_from Pry::Commands, "ls", "status", "!" + end + +* The `run` command invokes one command from within another. + The first parameter is the name of the command to invoke + and the remainder of the parameters will be passed on to the command + being invoked: + + class MyCommands < Pry::Commands + command "ls_with_hello" do + output.puts "hello!" + run "ls" + end + end + +#### Utility methods for commands + +All commands can access the special `output` and `target` methods. The +`output` method returns the `output` object for the active pry session. +Ensuring that your commands invoke `puts` on this rather than using +the top-level `puts` will ensure that all your session output goes to +the same place. + +The `target` method returns the `Binding` object the Pry session is currently +active on - useful when your commands need to manipulate or examine +the state of the object. E.g, the "ls" command is implemented as follows + + command "ls" do + output.puts target.eval("local_variables + instance_variables").inspect + end + +#### The opts hash + +These are miscellaneous variables that may be useful to your own commands: -* `opts[:output]` - The session's output object. * `opts[:val]` - The line of input that invoked the command. * `opts[:eval_string]` - The cumulative lines of input for multi-line input. -* `opts[:target]` - The object the Pry session is currently on. -* `opts[:captures]` - The array of regex captures generated by the command (if any). * `opts[:nesting]` - Lowlevel session nesting information. -* `opts[:command_info]` - Lowlevel information about all Pry commands. +* `opts[:commands]` - Lowlevel data of all Pry commands. (see commands.rb for examples of how some of these options are used) @@ -424,7 +470,11 @@ The `Pry::CommandBase` class automatically defines a `help` command for you. Typing `help` in a Pry session will show a list of commands to the user followed by their descriptions. Passing a parameter to `help` with the command name will just return the description of that -specific command. +specific command. If a description is left out it will automatically +be given the description "No description.". + +If the description is explicitly set to `""` then this command will +not be displayed in `help`. ### Hooks diff --git a/examples/example_command_override.rb b/examples/example_command_override.rb new file mode 100644 index 00000000..49f5abed --- /dev/null +++ b/examples/example_command_override.rb @@ -0,0 +1,27 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +# inherit standard command set, but tweak them by deleting some and +# overriding others +class MyCommands < Pry::CommandBase + + # Override ls command + command "ls", "An unhelpful ls" do + output.puts "No, i refuse to display any useful information." + end + + # Invoke one command from within another using `run` + command "status2" do |x| + output.puts "About to show status, are you ready?" + run "status", x + output.puts "Finished showing status." + end + + import_from Pry::Commands, "quit", "show_method", "ls" +end + +# Start a Pry session using the commands defined in MyCommands +# Type 'help' in Pry to get a list of the commands and their descriptions +Pry.start(TOPLEVEL_BINDING, :commands => MyCommands) diff --git a/examples/example_commands.rb b/examples/example_commands.rb new file mode 100644 index 00000000..d53561f2 --- /dev/null +++ b/examples/example_commands.rb @@ -0,0 +1,36 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +class MathCommands < Pry::CommandBase + command "greet", "Greet a person, e.g: greet john" do |name| + output.puts "Good afternoon #{name.capitalize}! Do you like Math?" + end + + command "add", "Add a list of numbers together, e.g: add 1 2 3 4" do |*args| + output.puts "Total: #{args.map(&:to_f).inject(&:+)}" + end + + command "multiply", "Multiply a list of numbers together, e.g: multiply 1 2 3 4" do |*args| + output.puts "Total: #{args.map(&:to_f).inject(&:*)}" + end + + # Explicitly giving a description of "" to prevent command being + # displayed in 'help' + command "exit", "" do + throw :breakout, 0 + end +end + +# Since we provide math commands, let's have mathematical +# before_session and after_session hooks, and a mathematical prompt +math_prompt = proc { "math> " } +math_hooks = { + :before_session => proc { |output, *| output.puts "Welcome! Let's do some math! Type 'help' for a list of commands." }, + :after_session => proc { |output, *| output.puts "Goodbye!" } +} + +# Start a Pry session using the commands defined in MyCommands +# Type 'help' in Pry to get a list of the commands and their descriptions +Pry.start(TOPLEVEL_BINDING, :commands => MathCommands, :prompt => math_prompt, :hooks => math_hooks) diff --git a/examples/example_hooks.rb b/examples/example_hooks.rb new file mode 100644 index 00000000..a5789c63 --- /dev/null +++ b/examples/example_hooks.rb @@ -0,0 +1,12 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +my_hooks = { + :before_session => proc { |out, obj| out.puts "Opening #{obj}." }, + :after_session => proc { |out, obj| out.puts "Closing #{obj}." } +} + +# Start a Pry session using the hooks hash defined in my_hooks +Pry.start(TOPLEVEL_BINDING, :hooks => my_hooks) diff --git a/examples/example_image_edit.rb b/examples/example_image_edit.rb new file mode 100644 index 00000000..ad29de60 --- /dev/null +++ b/examples/example_image_edit.rb @@ -0,0 +1,70 @@ +# Note: this requires you to have Gosu and TexPlay installed. +# `gem install gosu` +# `gem install texplay` +# +# Extra instructions for installing Gosu on Linux can be found here: +# http://code.google.com/p/gosu/wiki/GettingStartedOnLinux +# +# Instructions for using TexPlay can be found here: +# http://banisterfiend.wordpress.com/2008/08/23/texplay-an-image-manipulation-tool-for-ruby-and-gosu/ +# +# Have fun! :) + +direc = File.dirname(__FILE__) + +require 'rubygems' +require "texplay" +require "#{direc}/../lib/pry" + +WIDTH = 640 +HEIGHT = 480 + +IMAGE_PROMPT = [ proc { "(image edit)> " }, proc { "(image edit)* " } ] + +class ImageCommands < Pry::CommandBase + command "drawing_methods", "Show a list of TexPlay methods" do + output.puts "#{Pry.view(TexPlay.public_instance_methods)}" + end + + command "exit", "Exit the program." do + output.puts "Thanks for dropping by!" + exit + end + + import_from Pry::Commands, "ls", "ls_methods", "!" +end + +class WinClass < Gosu::Window + + def initialize + super(WIDTH, HEIGHT, false) + @img = TexPlay.create_image(self, 200, 200) + @img.rect 0, 0, @img.width - 1, @img.height - 1 + + @binding = @img.__binding__ + + @pry_instance = Pry.new(:commands => ImageCommands, :prompt => IMAGE_PROMPT) + end + + def draw + @img.draw_rot(WIDTH / 2, HEIGHT / 2, 1, 0, 0.5, 0.5) + end + + def update + exit if button_down?(Gosu::KbEscape) + + # We do not want a REPL session as the loop prevents the image + # being updated; instead we do a REP session, and let the image + # update each time the user presses enter. We maintain the same + # binding object to keep locals between calls to `Pry#rep()` + @pry_instance.rep(@binding) + end +end + +puts "Welcome to ImageEdit; type `help` for a list of commands and `drawing_methods` for a list of drawing methods available." +puts "--" +puts "Example: Try typing 'circle width/2, height/2, 95, :color => :blue, :fill => true'" +puts "If you want to save your image, type: save(\"img.png\")" + +WinClass.new.show + diff --git a/examples/example_input.rb b/examples/example_input.rb new file mode 100644 index 00000000..dd7c8d4e --- /dev/null +++ b/examples/example_input.rb @@ -0,0 +1,10 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +# Create a StringIO that contains the input data +str_input = StringIO.new("puts 'hello world!'\nputs \"I am in \#{self}\"\nexit") + +# Start a Pry session on the Fixnum 5 using the input data in str_input +Pry.start(5, :input => str_input) diff --git a/examples/example_output.rb b/examples/example_output.rb new file mode 100644 index 00000000..e54dad77 --- /dev/null +++ b/examples/example_output.rb @@ -0,0 +1,14 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +# Create a StringIO to contain the output data +str_output = StringIO.new + +# Start a Pry session on the Fixnum 5 using str_output to store the +# output (not writing to $stdout) +Pry.start(5, :output => str_output) + +# Display all the output accumulated during the session +puts str_output.string diff --git a/examples/example_print.rb b/examples/example_print.rb new file mode 100644 index 00000000..0b55a31a --- /dev/null +++ b/examples/example_print.rb @@ -0,0 +1,9 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +my_print = proc { |out, value| out.puts "Output is: #{value.inspect}" } + +# Start a Pry session using the print object defined in my_print +Pry.start(TOPLEVEL_BINDING, :print => my_print) diff --git a/examples/example_prompt.rb b/examples/example_prompt.rb new file mode 100644 index 00000000..44b68092 --- /dev/null +++ b/examples/example_prompt.rb @@ -0,0 +1,12 @@ +direc = File.dirname(__FILE__) + +require 'rubygems' +require "#{direc}/../lib/pry" + +# Remember, first prompt in array is the main prompt, second is the wait +# prompt (used for multiline input when more input is required) +my_prompt = [ proc { |obj, *| "inside #{obj}> " }, + proc { |obj, *| "inside #{obj}* "} ] + +# Start a Pry session using the prompt defined in my_prompt +Pry.start(TOPLEVEL_BINDING, :prompt => my_prompt) diff --git a/lib/pry/command_base.rb b/lib/pry/command_base.rb index 36893d5c..378dc51f 100644 --- a/lib/pry/command_base.rb +++ b/lib/pry/command_base.rb @@ -6,7 +6,7 @@ class Pry class << self attr_accessor :commands attr_accessor :command_info - attr_accessor :opts + attr_accessor :opts, :output, :target # private because we want to force function style invocation. We require # that the location where the block is defined has the `opts` @@ -14,7 +14,7 @@ class Pry private # Defines a new Pry command. - # @param [String, Array] name The name of the command (or array of + # @param [String, Array] names The name of the command (or array of # command name aliases). # @param [String] description A description of the command. # @yield The action to perform. The parameters in the block @@ -34,42 +34,73 @@ class Pry # # Good afternoon John! # # pry(main)> help greet # # Greet somebody - def command(name, description="No description.", &block) + def command(names, description="No description.", &block) @commands ||= {} - @command_info ||= {} - arg_match = '(?:\s+(\S+))?' * 20 - if name.is_a?(Array) - name.each do |n| - matcher = /^#{n}(?!\S)#{arg_match}?/ - commands[matcher] = block - command_info[n] = description - end - else - matcher = /^#{name}(?!\S)#{arg_match}?/ - commands[matcher] = block - command_info[name] = description + Array(names).each do |name| + commands[name] = { :description => description, :action => block } end + end + # Delete a command or an array of commands. + # Useful when inheriting from another command set and pruning + # those commands down to the ones you want. + # @param [Array] names The command name or array + # of command names you want to delete + # @example Deleteing inherited commands + # class MyCommands < Pry::Commands + # delete "show_method", "show_imethod", "show_doc", "show_idoc" + # end + # Pry.commands = MyCommands + def delete(*names) + names.each { |name| commands.delete(name) } + end + + # Execute a command (this enables commands to call other commands). + # @param [String] name The command to execute + # @param [Array] args The parameters to pass to the command. + # @example Wrap one command with another + # class MyCommands < Pry::Commands + # command "ls2" do + # output.puts "before ls" + # run "ls" + # output.puts "after ls" + # end + # end + def run(name, *args) + action = opts[:commands][name][:action] + instance_exec(*args, &action) + end + + # Import commands from another command object. + # @param [Pry::CommandBase] klass The class to import from (must + # be a subclass of `Pry::CommandBase`) + # @param [Array] names The commands to import. + # @example + # class MyCommands < Pry::CommandBase + # import_from Pry::Commands, "ls", "show_method", "cd" + # end + def import_from(klass, *names) + imported_hash = Hash[klass.commands.select { |k, v| names.include?(k) }] + commands.merge!(imported_hash) end end + command "help", "This menu." do |cmd| - out = opts[:output] - command_info = opts[:command_info] + command_info = opts[:commands] param = cmd if !param - out.puts "Command list:" - out.puts "--" - command_info.each do |k, v| - out.puts "#{k}".ljust(18) + v + output.puts "Command list:" + output.puts "--" + command_info.each do |k, data| + output.puts "#{k}".ljust(18) + data[:description] if !data[:description].empty? end else - key = command_info.keys.find { |v| Array(v).any? { |k| k === param } } - if key - out.puts command_info[key] + if command_info[param] + output.puts command_info[param][:description] else - out.puts "No info for command: #{param}" + output.puts "No info for command: #{param}" end end end @@ -77,7 +108,7 @@ class Pry # Ensures that commands can be inherited def self.inherited(klass) klass.commands = commands.dup - klass.command_info = command_info.dup end + end end diff --git a/lib/pry/commands.rb b/lib/pry/commands.rb index 2bc54a64..f134ea77 100644 --- a/lib/pry/commands.rb +++ b/lib/pry/commands.rb @@ -7,12 +7,12 @@ class Pry class Commands < CommandBase command "!", "Refresh the REPL" do - opts[:output].puts "Refreshed REPL" + output.puts "Refreshed REPL" opts[:eval_string].clear end command "!pry", "Start a Pry session on current self; this even works mid-expression." do - Pry.start(opts[:target]) + Pry.start(target) end command ["exit_program", "quit_program"], "End the current program." do @@ -20,7 +20,7 @@ class Pry end command "nesting", "Show nesting information." do - out = opts[:output] + out = output nesting = opts[:nesting] out.puts "Nesting status:" @@ -35,9 +35,8 @@ class Pry end command "status", "Show status information." do - out = opts[:output] + out = output nesting = opts[:nesting] - target = opts[:target] out.puts "Status:" out.puts "--" @@ -53,37 +52,37 @@ class Pry end command "ls", "Show the list of vars in the current scope." do - opts[:output].puts "#{Pry.view(opts[:target].eval('local_variables + instance_variables'))}" + output.puts "#{Pry.view(target.eval('local_variables + instance_variables'))}" end command "cat", "Show output of .inspect." do |obj| - out = opts[:output] - out.puts opts[:target].eval("#{obj}.inspect") + out = output + out.puts target.eval("#{obj}.inspect") end command "cd", "Start a Pry session on (use `cd ..` to go back)" do |obj| throw(:breakout, opts[:nesting].level) if obj == ".." - opts[:target].eval("#{obj}.pry") + target.eval("#{obj}.pry") end command "show_doc", "Show the comments above " do |meth_name| - doc = opts[:target].eval("method(:#{meth_name})").comment - opts[:output].puts doc + doc = target.eval("method(:#{meth_name})").comment + output.puts doc end command "show_idoc", "Show the comments above instance method " do |meth_name| - doc = opts[:target].eval("instance_method(:#{meth_name})").comment - opts[:output].puts doc + doc = target.eval("instance_method(:#{meth_name})").comment + output.puts doc end command "show_method", "Show sourcecode for method ." do |meth_name| - doc = opts[:target].eval("method(:#{meth_name})").source - opts[:output].puts doc + doc = target.eval("method(:#{meth_name})").source + output.puts doc end command "show_imethod", "Show sourcecode for instance method ." do |meth_name| - doc = opts[:target].eval("instance_method(:#{meth_name})").source - opts[:output].puts doc + doc = target.eval("instance_method(:#{meth_name})").source + output.puts doc end command "jump_to", "Jump to a Pry session further up the stack, exiting all sessions below." do |break_level| @@ -92,21 +91,21 @@ class Pry case break_level when nesting.level - opts[:output].puts "Already at nesting level #{nesting.level}" + output.puts "Already at nesting level #{nesting.level}" when (0...nesting.level) throw(:breakout, break_level + 1) else max_nest_level = nesting.level - 1 - opts[:output].puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}." + output.puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}." end end command "ls_methods", "List all methods defined on class of receiver." do - opts[:output].puts "#{Pry.view(opts[:target].eval('public_methods(false) + private_methods(false) + protected_methods(false)'))}" + output.puts "#{Pry.view(target.eval('public_methods(false) + private_methods(false) + protected_methods(false)'))}" end command "ls_imethods", "List all instance methods defined on class of receiver." do - opts[:output].puts "#{Pry.view(opts[:target].eval('public_instance_methods(false) + private_instance_methods(false) + protected_instance_methods(false)'))}" + output.puts "#{Pry.view(target.eval('public_instance_methods(false) + private_instance_methods(false) + protected_instance_methods(false)'))}" end command ["exit", "quit", "back"], "End the current Pry session." do diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb index 0475170c..3923aa9f 100644 --- a/lib/pry/pry_instance.rb +++ b/lib/pry/pry_instance.rb @@ -125,7 +125,7 @@ class Pry target = binding_for(target) if input == Readline - Readline.completion_proc = Pry::InputCompleter.build_completion_proc(target, Pry.commands.command_info.keys.flatten) + Readline.completion_proc = Pry::InputCompleter.build_completion_proc(target, commands.commands.keys) end # eval the expression and save to last_result @@ -182,28 +182,29 @@ class Pry def val.clear() replace("") end def eval_string.clear() replace("") end - pattern, action = commands.commands.find { |k, v| k === val } + pattern, data = commands.commands.find do |name, data| + /^#{name}(?!\S)(?:\s+(.+))?/ =~ val + end if pattern - captures = Regexp.last_match.captures - captures.compact! + args_string = $1 + args = args_string ? args_string.split : [] + action = data[:action] options = { - :captures => captures, + :captures => args_string, :eval_string => eval_string, :target => target, :val => val, :nesting => nesting, :output => output, - :command_info => commands.command_info + :commands => commands.commands } - # because procs are defined in different places (e.g 'help' in CommandBase) - # we cannot simply use `commands.opts=...`; instead we have to - # retrieve the object where the block was defined; since that is - # where the `opts` method the block will have access to is defined. - action_self = action.binding.eval('self') - action_self.opts = options + # set some useful methods to be used by the action blocks + commands.opts = options + commands.target = target + commands.output = output # send the correct number of parameters to the block (to avoid # warnings in 1.8.7) @@ -212,14 +213,19 @@ class Pry # if arity is negative then we have a *args in 1.8.7. # In 1.9 we have default values or *args - action.call(*captures) + # Use instance_exec() to make the opts, target, output, etc methods available + commands.instance_exec(*args, &action) when 1, 0 # ensure that we get the right number of parameters; # using values_at we pad out missing parameters with nils so # that 1.8.7 doesn't complain about incorrect arity (1.9.2 # doesn't care) - action.call(*captures.values_at(*0..(action.arity - 1))) + args_with_corrected_arity = args.values_at *0..(action.arity - 1) + + # Use instance_exec() to make the opts, target, output, etc methods + # available + commands.instance_exec(*args_with_corrected_arity, &action) end val.clear diff --git a/test/test.rb b/test/test.rb index 4b6e73db..cf057fd9 100644 --- a/test/test.rb +++ b/test/test.rb @@ -298,25 +298,122 @@ describe Pry do end Command2.commands.keys.size.should == 2 - Command2.command_info.keys.include?("help").should == true - Command2.command_info.keys.include?("h").should == true + Command2.commands.keys.include?("help").should == true + Command2.commands.keys.include?("h").should == true Object.remove_const(:Command2) end - it 'should inherit comands from Pry::Commands' do + it 'should inherit commands from Pry::Commands' do class Command3 < Pry::Commands command "v" do end end - Command3.command_info.include?("nesting").should == true - Command3.command_info.include?("jump_to").should == true - Command3.command_info.include?("cd").should == true - Command3.command_info.include?("v").should == true + Command3.commands.include?("nesting").should == true + Command3.commands.include?("jump_to").should == true + Command3.commands.include?("cd").should == true + Command3.commands.include?("v").should == true Object.remove_const(:Command3) end + + it 'should run a command from within a command' do + class Command3 < Pry::Commands + command "v" do + output.puts "v command" + end + + command "run_v" do + run "v" + end + end + + str_output = StringIO.new + Pry.new(:input => InputTester.new("run_v"), :output => str_output, :commands => Command3).rep + str_output.string.should =~ /v command/ + + Object.remove_const(:Command3) + end + + it 'should enable an inherited method to access opts and output and target, due to instance_exec' do + class Command3 < Pry::Commands + command "v" do + output.puts "#{target.eval('self')}" + end + end + + class Command4 < Command3 + end + + str_output = StringIO.new + Pry.new(:print => proc {}, :input => InputTester.new("v"), + :output => str_output, :commands => Command4).rep("john") + str_output.string.chomp.should == "john" + + Object.remove_const(:Command3) + Object.remove_const(:Command4) + end + + it 'should import commands from another command object' do + class Command3 < Pry::CommandBase + import_from Pry::Commands, "status", "jump_to" + end + + str_output = StringIO.new + Pry.new(:print => proc {}, :input => InputTester.new("status"), + :output => str_output, :commands => Command3).rep("john") + str_output.string.should =~ /Status:/ + + Object.remove_const(:Command3) + end + + it 'should delete some inherited commands when using delete method' do + class Command3 < Pry::Commands + command "v" do + end + + delete "show_doc", "show_method" + delete "ls" + end + + Command3.commands.include?("nesting").should == true + Command3.commands.include?("jump_to").should == true + Command3.commands.include?("cd").should == true + Command3.commands.include?("v").should == true + Command3.commands.include?("show_doc").should == false + Command3.commands.include?("show_method").should == false + Command3.commands.include?("ls").should == false + + Object.remove_const(:Command3) + end + + it 'should override some inherited commands' do + class Command3 < Pry::Commands + command "jump_to" do + output.puts "jump_to the music" + end + + command "help" do + output.puts "help to the music" + end + end + + # suppress evaluation output + Pry.print = proc {} + + str_output = StringIO.new + Pry.new(:input => InputTester.new("jump_to"), :output => str_output, :commands => Command3).rep + str_output.string.chomp.should == "jump_to the music" + + str_output = StringIO.new + Pry.new(:input => InputTester.new("help"), :output => str_output, :commands => Command3).rep + str_output.string.chomp.should == "help to the music" + + Object.remove_const(:Command3) + + Pry.reset_defaults + end end it "should set the print default, and the default should be overridable" do