1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

saving before refactor to implement action block instead of action proc for commands. All tests passing in 1.8 and 1.9.

This commit is contained in:
John Mair 2011-01-12 12:16:04 +11:00
parent 8e006829fe
commit ebdcfdf145
8 changed files with 359 additions and 275 deletions

80
lib/pry/command_base.rb Normal file
View file

@ -0,0 +1,80 @@
class Pry
class CommandBase
class << self
attr_accessor :commands
attr_accessor :command_info
end
class Command
Elements = [:name, :describe, :pattern, :action]
Elements.each do |e|
define_method(e) { |s| instance_variable_set("@#{e}", s) }
define_method("get_#{e}") { instance_variable_get("@#{e}") }
end
# define action here since it needs to take a block
def action(&block)
@action = block
end
end
def self.check_command(c)
c.pattern(c.get_name) if !c.get_pattern
c.describe "No description." if !c.get_describe
Command::Elements.each do |e|
raise "command has no #{e}!" if !c.send("get_#{e}")
end
end
def self.command(name, &block)
@commands ||= {}
@command_info ||= {}
c = Command.new
c.name name
c.instance_eval(&block)
check_command(c)
@commands.merge! c.get_pattern => c.get_action
@command_info.merge! c.get_name => c.get_describe
end
command "help" do
pattern /^help\s*(.+)?/
describe "This menu."
action do |opts|
out = opts[:output]
command_info = opts[:command_info]
param = opts[:captures].first
if !param
out.puts "Command list:"
out.puts "--"
command_info.each do |k, v|
puts "#{Array(k).first}".ljust(18) + v
end
else
key = command_info.keys.find { |v| Array(v).any? { |k| k === param } }
if key
out.puts command_info[key]
else
out.puts "No info for command: #{param}"
end
end
opts[:val].clear
end
end
def self.inherited(klass)
klass.commands = @commands.dup
klass.command_info = @command_info.dup
end
end
end

View file

@ -1,81 +1,12 @@
direc = File.dirname(__FILE__)
require "#{direc}/command_base"
class Pry class Pry
class CommandBase
class << self
attr_accessor :commands
attr_accessor :command_info
end
@commands = {}
@command_info = {}
class Command
Elements = [:name, :description, :pattern, :action]
Elements.each do |e|
define_method(e) { |s| instance_variable_set("@#{e}", s) }
define_method("get_#{e}") { instance_variable_get("@#{e}") }
end
end
def self.check_command(c)
c.pattern(c.get_name) if !c.get_pattern
Command::Elements.each do |e|
raise "command has no #{e}!" if !c.send("get_#{e}")
end
end
def self.command(name, &block)
c = Command.new
c.name name
c.instance_eval(&block)
check_command(c)
commands.merge! c.get_pattern => c.get_action
command_info.merge! c.get_name => c.get_description
end
command "help" do
pattern /^help\s*(.+)?/
description "This menu."
action proc { |opts|
out = opts[:output]
command_info = opts[:command_info]
param = opts[:captures].first
puts opts[:captures].inspect
if !param
out.puts "Command list:"
out.puts "--"
command_info.each do |k, v|
puts "#{Array(k).first}".ljust(18) + v
end
else
key = command_info.keys.find { |v| Array(v).any? { |k| k === param } }
if key
out.puts command_info[key]
else
out.puts "No info for command: #{param}"
end
end
opts[:val].clear
}
end
end
# Default commands used by Pry. # Default commands used by Pry.
# @note # @note
# If you plan to replace the default Commands class with a custom # If you plan to replace the default Commands class with a custom
# one then it must have a `commands` method that returns a Hash. # one then it must have a `commands` method that returns a Hash.
class Commands class Commands < CommandBase
# This method returns a hash that defines the commands implemented for the REPL session. # This method returns a hash that defines the commands implemented for the REPL session.
# The hash has the following form: # The hash has the following form:
@ -107,165 +38,183 @@ class Pry
# opts[:output].puts "hello #{opts[:captures].first}" # opts[:output].puts "hello #{opts[:captures].first}"
# } # }
# end # end
def commands command "!" do
@commands ||= { describe "Refresh the REPL"
"!" => proc do |opts| action do |opts|
opts[:output].puts "Refreshed REPL" opts[:output].puts "Refreshed REPL"
opts[:val].clear
opts[:eval_string].clear
end,
"!pry" => proc do |opts|
Pry.start(opts[:target])
opts[:val].clear
end,
["exit_program", "quit_program"] => proc do
exit
end,
/^help\s*(.+)?/ => proc do |opts|
param = opts[:captures].first
self.show_help(opts[:output], param)
opts[:val].clear
end,
"nesting" => proc do |opts|
self.show_nesting(opts[:output], opts[:nesting])
opts[:val].clear
end,
"status" => proc do |opts|
self.show_status(opts[:output], opts[:nesting], opts[:target])
opts[:val].clear
end,
"exit_all" => proc do
throw(:breakout, 0)
end,
["exit", "quit", "back", /^cd\s*\.\./] => proc do |opts|
throw(:breakout, opts[:nesting].level)
end,
"ls" => proc do |opts|
opts[:output].puts "#{opts[:target].eval('Pry.view(local_variables + instance_variables)')}"
opts[:val].clear
end,
/^cat\s+(.+)/ => proc do |opts|
obj = opts[:captures].first
opts[:output].puts opts[:target].eval("#{obj}.inspect")
opts[:val].clear
end,
/^cd\s+(.+)/ => proc do |opts|
obj = opts[:captures].first
throw(:breakout, opts[:nesting].level) if obj == ".." opts[:eval_string].clear
end
opts[:target].eval("#{obj}.pry")
opts[:val].clear
end,
/^show_doc\s*(.+)/ => proc do |opts|
meth_name = opts[:captures].first
doc = opts[:target].eval("method(:#{meth_name})").comment
opts[:output].puts doc
opts[:val].clear
end,
/^show_idoc\s*(.+)/ => proc do |opts|
meth_name = opts[:captures].first
doc = opts[:target].eval("instance_method(:#{meth_name})").comment
opts[:val].clear
end,
/^show_method\s*(.+)/ => proc do |opts|
meth_name = opts[:captures].first
code = opts[:target].eval("method(:#{meth_name})").source
opts[:output].puts code
opts[:val].clear
end,
/^show_imethod\s*(.+)/ => proc do |opts|
meth_name = opts[:captures].first
code = opts[:target].eval("instance_method(:#{meth_name})").source
opts[:val].clear
end,
/^jump_to\s*(\d*)/ => proc do |opts|
break_level = opts[:captures].first.to_i
nesting = opts[:nesting]
case break_level
when nesting.level
opts[:output].puts "Already at nesting level #{nesting.level}"
opts[:val].clear
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}."
opts[:val].clear
end
end,
"ls_methods" => proc do |opts|
opts[:output].puts "#{Pry.view(opts[:target].eval('public_methods(false)'))}"
opts[:val].clear
end,
"ls_imethods" => proc do |opts|
opts[:output].puts "#{Pry.view(opts[:target].eval('public_instance_methods(false)'))}"
opts[:val].clear
end
}
end end
def command_info command "!pry" do
@command_info ||= { describe "Start a Pry session on current self; this even works mid-expression."
"!" => "Refresh the REPL.", action do |opts|
"!pry" => "Start a Pry session on current self; this even works mid-expression.", Pry.start(opts[:target])
["exit_program", "quit_program"] => "end the current program.",
"help" => "This menu.", end
"nesting" => "Show nesting information.",
"status" => "Show status information.",
"exit_all" => "End all nested Pry sessions",
["exit", "quit", "back", /cd\s*\.\./] => "End the current Pry session.",
"ls" => "Show the list of vars in the current scope.",
"cat" => "Show output of <var>.inspect",
"cd" => "Start a Pry session on <var> (use `cd ..` to go back)",
"show_doc" => "Show the comments above <methname>",
"show_idoc" => "Show the comments above instance method <methname>",
"show_method" => "Show sourcecode for method <methname>",
"show_imethod" => "Show sourcecode for instance method <methname>",
"jump_to" => "Jump to a Pry session further up the stack, exiting all sessions below.",
"ls_methods" => "List public methods defined on class of receiver.",
"ls_imethods" => "List public instance methods defined on receiver."
}
end end
def show_help(out, param) command ["exit_program", "quit_program"] do
if !param describe "End the current program."
out.puts "Command list:" action { |opts| exit }
end
command "nesting" do
describe "Show nesting information."
action do |opts|
out = opts[:output]
nesting = opts[:nesting]
out.puts "Nesting status:"
out.puts "--" out.puts "--"
command_info.each do |k, v| nesting.each do |level, obj|
puts "#{Array(k).first}".ljust(18) + v if level == 0
out.puts "#{level}. #{Pry.view(obj)} (Pry top level)"
else
out.puts "#{level}. #{Pry.view(obj)}"
end
end end
else
key = command_info.keys.find { |v| Array(v).any? { |k| k === param } }
if key end
out.puts command_info[key] end
command "status" do
describe "Show status information."
action do |opts|
out = opts[:output]
nesting = opts[:nesting]
target = opts[:target]
out.puts "Status:"
out.puts "--"
out.puts "Receiver: #{Pry.view(target.eval('self'))}"
out.puts "Nesting level: #{nesting.level}"
out.puts "Local variables: #{Pry.view(target.eval('local_variables'))}"
out.puts "Pry instance: #{Pry.active_instance}"
out.puts "Last result: #{Pry.view(Pry.last_result)}"
end
end
command "exit_all" do
describe "End all nested Pry sessions."
action { |opts| throw(:breakout, 0) }
end
command "ls" do
describe "Show the list of vars in the current scope."
action do |opts|
opts[:output].puts "#{opts[:target].eval('Pry.view(local_variables + instance_variables)')}"
end
end
command "cat" do
describe "Show output of <var>.inspect."
pattern /^cat\s+(.+)/
action do |opts|
out = opts[:output]
obj = opts[:captures].first
out.puts opts[:target].eval("#{obj}.inspect")
end
end
command "cd" do
pattern /^cd\s+(.+)/
describe "Start a Pry session on <var> (use `cd ..` to go back)"
action do |opts|
obj = opts[:captures].first
throw(:breakout, opts[:nesting].level) if obj == ".."
opts[:target].eval("#{obj}.pry")
end
end
command "show_doc" do
pattern /^show_doc\s*(.+)/
describe "Show the comments above <methname>"
action do |opts|
meth_name = opts[:captures].first
doc = opts[:target].eval("method(:#{meth_name})").comment
opts[:output].puts doc
end
end
command "show_idoc" do
pattern /^show_idoc\s*(.+)/
describe "Show the comments above instance method <methname>"
action do |opts|
meth_name = opts[:captures].first
doc = opts[:target].eval("instance_method(:#{meth_name})").comment
opts[:output].puts doc
end
end
command "show_method" do
pattern /^show_method\s*(.+)/
describe "Show sourcecode for method <methname>."
action do |opts|
meth_name = opts[:captures].first
doc = opts[:target].eval("method(:#{meth_name})").source
opts[:output].puts doc
end
end
command "show_imethod" do
pattern /^show_imethod\s*(.+)/
describe "Show sourcecode for instance method <methname>."
action do |opts|
meth_name = opts[:captures].first
doc = opts[:target].eval("instance_method(:#{meth_name})").source
opts[:output].puts doc
end
end
command "jump_to" do
pattern /^jump_to\s*(\d*)/
describe "Jump to a Pry session further up the stack, exiting all sessions below."
action do |opts|
break_level = opts[:captures].first.to_i
nesting = opts[:nesting]
case break_level
when nesting.level
opts[:output].puts "Already at nesting level #{nesting.level}"
when (0...nesting.level)
throw(:breakout, break_level + 1)
else else
out.puts "No info for command: #{param}" max_nest_level = nesting.level - 1
opts[:output].puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}."
end end
end end
end end
def show_nesting(out, nesting) command "ls_methods" do
out.puts "Nesting status:" describe "List public methods defined on class of receiver."
out.puts "--"
nesting.each do |level, obj| action do |opts|
if level == 0 opts[:output].puts "#{Pry.view(opts[:target].eval('public_methods(false)'))}"
out.puts "#{level}. #{Pry.view(obj)} (Pry top level)"
else
out.puts "#{level}. #{Pry.view(obj)}"
end
end end
end end
def show_status(out, nesting, target) command "ls_imethods" do
out.puts "Status:" describe "List public instance methods defined on class of receiver."
out.puts "--"
out.puts "Receiver: #{Pry.view(target.eval('self'))}" action do |opts|
out.puts "Nesting level: #{nesting.level}" opts[:output].puts "#{Pry.view(opts[:target].eval('public_instance_methods(false)'))}"
out.puts "Local variables: #{Pry.view(target.eval('local_variables'))}" end
out.puts "Pry instance: #{Pry.active_instance}" end
out.puts "Last result: #{Pry.view(Pry.last_result)}"
command ["exit", "quit", "back"] do
describe "End the current Pry session."
action do |opts|
throw(:breakout, opts[:nesting].level)
end
end end
end end
end end

View file

@ -1,10 +1,16 @@
# stolen from irb # taken from irb
require "readline" require "readline"
class Pry class Pry
module InputCompleter module InputCompleter
if Readline.respond_to?("basic_word_break_characters=")
Readline.basic_word_break_characters= " \t\n\"\\'`><=;|&{("
end
Readline.completion_append_character = nil
ReservedWords = [ ReservedWords = [
"BEGIN", "END", "BEGIN", "END",
"alias", "and", "alias", "and",
@ -30,7 +36,7 @@ class Pry
"[]", "[]=", "^", "!", "!=", "!~"] "[]", "[]=", "^", "!", "!=", "!~"]
def self.build_completion_proc(target, commands=[""]) def self.build_completion_proc(target, commands=[""])
proc { |input| proc do |input|
bind = target bind = target
case input case input
@ -75,7 +81,6 @@ class Pry
candidates = Object.constants.collect{|m| m.to_s} candidates = Object.constants.collect{|m| m.to_s}
candidates.grep(/^#{receiver}/).collect{|e| "::" + e} candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
# when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
when /^([A-Z].*)::([^:.]*)$/ when /^([A-Z].*)::([^:.]*)$/
# Constant or class methods # Constant or class methods
receiver = $1 receiver = $1
@ -124,8 +129,6 @@ class Pry
regmessage = Regexp.new(Regexp.quote($1)) regmessage = Regexp.new(Regexp.quote($1))
candidates = global_variables.collect{|m| m.to_s}.grep(regmessage) candidates = global_variables.collect{|m| m.to_s}.grep(regmessage)
# when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
# when /^((\.?[^.]+)+)\.([^.]*)$/
when /^([^."].*)\.([^.]*)$/ when /^([^."].*)\.([^.]*)$/
# variable # variable
receiver = $1 receiver = $1
@ -175,11 +178,9 @@ class Pry
(candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/) (candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/)
end end
} end
end end
def self.select_message(receiver, message, candidates) def self.select_message(receiver, message, candidates)
candidates.grep(/^#{message}/).collect do |e| candidates.grep(/^#{message}/).collect do |e|
case e case e
@ -194,7 +195,3 @@ class Pry
end end
end end
if Readline.respond_to?("basic_word_break_characters=")
Readline.basic_word_break_characters= " \t\n\"\\'`><=;|&{("
end
Readline.completion_append_character = nil

View file

@ -1,3 +1,5 @@
require 'readline'
# @author John Mair (banisterfiend) # @author John Mair (banisterfiend)
class Pry class Pry
@ -83,7 +85,7 @@ class Pry
def self.reset_defaults def self.reset_defaults
@input = Readline @input = Readline
@output = $stdout @output = $stdout
@commands = Commands.new @commands = Commands
@prompt = DEFAULT_PROMPT @prompt = DEFAULT_PROMPT
@print = DEFAULT_PRINT @print = DEFAULT_PRINT
@hooks = DEFAULT_HOOKS @hooks = DEFAULT_HOOKS

View file

@ -1,3 +1,5 @@
require 'readline'
class Pry class Pry
# The list of configuration options. # The list of configuration options.
@ -10,7 +12,7 @@ class Pry
# @param [Hash] options The optional configuration parameters. # @param [Hash] options The optional configuration parameters.
# @option options [#read] :input The object to use for input. (see input.rb) # @option options [#read] :input The object to use for input. (see input.rb)
# @option options [#puts] :output The object to use for output. (see output.rb) # @option options [#puts] :output The object to use for output. (see output.rb)
# @option options [#commands] :commands The object to use for # @option options [Pry::CommandBase] :commands The object to use for
# commands. (see commands.rb) # commands. (see commands.rb)
# @option options [Hash] :hooks The defined hook Procs (see hooks.rb) # @option options [Hash] :hooks The defined hook Procs (see hooks.rb)
# @option options [Array<Proc>] :default_prompt The array of Procs # @option options [Array<Proc>] :default_prompt The array of Procs
@ -110,7 +112,6 @@ class Pry
def re(target=TOPLEVEL_BINDING) def re(target=TOPLEVEL_BINDING)
target = binding_for(target) target = binding_for(target)
# FIXME!!!!!!!! Should not hardcode command_info in here!
if input == Readline 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, Pry.commands.command_info.keys.flatten)
end end
@ -167,6 +168,7 @@ class Pry
# @param [Binding] target The receiver of the commands. # @param [Binding] target The receiver of the commands.
def process_commands(val, eval_string, target) def process_commands(val, eval_string, target)
def val.clear() replace("") end def val.clear() replace("") end
def eval_string.clear() replace("") end
pattern, action = commands.commands.find { |k, v| Array(k).any? { |a| a === val } } pattern, action = commands.commands.find { |k, v| Array(k).any? { |a| a === val } }
@ -184,6 +186,7 @@ class Pry
} }
action.call(options) action.call(options)
val.clear
end end
end end
@ -191,7 +194,7 @@ class Pry
# This method should not need to be invoked directly. # This method should not need to be invoked directly.
# @param [String] current_prompt The prompt to use for input. # @param [String] current_prompt The prompt to use for input.
# @return [String] The next line of input. # @return [String] The next line of input.
def readline(current_prompt) def readline(current_prompt="> ")
if input == Readline if input == Readline

View file

@ -1,3 +1,3 @@
class Pry class Pry
VERSION = "0.4.0" VERSION = "0.4.0pre1"
end end

View file

@ -5,8 +5,6 @@ require 'bacon'
require "#{direc}/../lib/pry" require "#{direc}/../lib/pry"
require "#{direc}/test_helper" require "#{direc}/test_helper"
NOT_FOR_RUBY_18 = [/show_doc/, /show_idoc/, /show_method/, /show_imethod/]
puts "Ruby Version #{RUBY_VERSION}" puts "Ruby Version #{RUBY_VERSION}"
puts "Testing Pry #{Pry::VERSION}" puts "Testing Pry #{Pry::VERSION}"
puts "With method_source version #{MethodSource::VERSION}" puts "With method_source version #{MethodSource::VERSION}"
@ -105,9 +103,9 @@ describe Pry do
describe "commands" do describe "commands" do
it 'should run command1' do it 'should run command1' do
pry_tester = Pry.new pry_tester = Pry.new
pry_tester.commands = CommandTester.new pry_tester.commands = CommandTester
pry_tester.input = InputTester.new("command1", "exit_all") pry_tester.input = InputTester.new("command1", "exit_all")
pry_tester.commands = CommandTester.new pry_tester.commands = CommandTester
str_output = StringIO.new str_output = StringIO.new
pry_tester.output = str_output pry_tester.output = str_output
@ -119,9 +117,9 @@ describe Pry do
it 'should run command2' do it 'should run command2' do
pry_tester = Pry.new pry_tester = Pry.new
pry_tester.commands = CommandTester.new pry_tester.commands = CommandTester
pry_tester.input = InputTester.new("command2 horsey", "exit_all") pry_tester.input = InputTester.new("command2 horsey", "exit_all")
pry_tester.commands = CommandTester.new pry_tester.commands = CommandTester
str_output = StringIO.new str_output = StringIO.new
pry_tester.output = str_output pry_tester.output = str_output
@ -262,31 +260,69 @@ describe Pry do
str_output2.string.should =~ /7/ str_output2.string.should =~ /7/
end end
it 'should set the commands default, and the default should be overridable' do describe "commands" do
commands = {
"hello" => proc { |opts| opts[:output].puts "hello world"; opts[:val].clear }
}
def commands.commands() self end it 'should set the commands default, and the default should be overridable' do
class Command0 < Pry::CommandBase
command "hello" do
describe ""
action { |opts| opts[:output].puts "hello world"; opts[:val].clear }
end
end
Pry.commands = commands Pry.commands = Command0
str_output = StringIO.new str_output = StringIO.new
Pry.new(:input => InputTester.new("hello"), :output => str_output).rep Pry.new(:input => InputTester.new("hello"), :output => str_output).rep
str_output.string.should =~ /hello world/ str_output.string.should =~ /hello world/
commands = { class Command1 < Pry::CommandBase
"goodbye" => proc { |opts| opts[:output].puts "goodbye world"; opts[:val].clear } command "goodbye" do
} describe ""
action { |opts| opts[:output].puts "goodbye world"; opts[:val].clear }
end
end
def commands.commands() self end str_output = StringIO.new
str_output = StringIO.new
Pry.new(:input => InputTester.new("goodbye"), :output => str_output, :commands => commands).rep Pry.new(:input => InputTester.new("goodbye"), :output => str_output, :commands => Command1).rep
str_output.string.should =~ /goodbye world/ str_output.string.should =~ /goodbye world/
Object.remove_const(:Command0)
Object.remove_const(:Command1)
end
it 'should inherit "help" command from Pry::CommandBase' do
class Command2 < Pry::CommandBase
command "h" do |v|
v.describe "h command"
v.action { }
end
end
Command2.commands.keys.size.should == 2
Command2.command_info.keys.include?("help").should == true
Command2.command_info.keys.include?("h").should == true
Object.remove_const(:Command2)
end
it 'should inherit comands from Pry::Commands' do
class Command3 < Pry::Commands
command "v" do
action {}
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
Object.remove_const(:Command3)
end
end end
it "should set the print default, and the default should be overridable" do it "should set the print default, and the default should be overridable" do
new_print = proc { |out, value| out.puts value } new_print = proc { |out, value| out.puts value }
Pry.print = new_print Pry.print = new_print

View file

@ -1,6 +1,5 @@
class Object class Module
def test_method public :remove_const
end
end end
class InputTester class InputTester
@ -18,15 +17,33 @@ class InputTester
end end
end end
class CommandTester class CommandTester < Pry::CommandBase
def commands
@commands ||= { command "command1" do
"command1" => proc { |opts| opts[:output].puts "command1"; opts[:val].clear }, describe "command 1 test"
/command2\s*(.*)/ => proc do |opts| action { |opts| opts[:output].puts "command1"; opts[:val].clear }
arg = opts[:captures].first end
opts[:output].puts arg
opts[:val].clear command "command2" do
end describe "command 2 test"
pattern /command2\s*(.*)/
action { |opts|
arg = opts[:captures].first
opts[:output].puts arg
opts[:val].clear
} }
end end
# def commands
# @commands ||= {
# "command1" => proc { |opts| opts[:output].puts "command1"; opts[:val].clear },
# /command2\s*(.*)/ => proc do |opts|
# arg = opts[:captures].first
# opts[:output].puts arg
# opts[:val].clear
# end
# }
# end
# e
end end