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,146 +38,35 @@ 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 opts[:eval_string].clear
end, end
"!pry" => proc do |opts| end
command "!pry" do
describe "Start a Pry session on current self; this even works mid-expression."
action do |opts|
Pry.start(opts[:target]) 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 == ".." end
end
opts[:target].eval("#{obj}.pry") command ["exit_program", "quit_program"] do
opts[:val].clear describe "End the current program."
end, action { |opts| exit }
/^show_doc\s*(.+)/ => proc do |opts| end
meth_name = opts[:captures].first
doc = opts[:target].eval("method(:#{meth_name})").comment command "nesting" do
opts[:output].puts doc describe "Show nesting information."
opts[:val].clear
end, action do |opts|
/^show_idoc\s*(.+)/ => proc do |opts| out = opts[:output]
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] 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
def command_info
@command_info ||= {
"!" => "Refresh the REPL.",
"!pry" => "Start a Pry session on current self; this even works mid-expression.",
["exit_program", "quit_program"] => "end the current program.",
"help" => "This menu.",
"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
def show_help(out, param)
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
end
def show_nesting(out, nesting)
out.puts "Nesting status:" out.puts "Nesting status:"
out.puts "--" out.puts "--"
nesting.each do |level, obj| nesting.each do |level, obj|
@ -256,9 +76,19 @@ class Pry
out.puts "#{level}. #{Pry.view(obj)}" out.puts "#{level}. #{Pry.view(obj)}"
end end
end end
end
end end
def show_status(out, nesting, target) command "status" do
describe "Show status information."
action do |opts|
out = opts[:output]
nesting = opts[:nesting]
target = opts[:target]
out.puts "Status:" out.puts "Status:"
out.puts "--" out.puts "--"
out.puts "Receiver: #{Pry.view(target.eval('self'))}" out.puts "Receiver: #{Pry.view(target.eval('self'))}"
@ -268,4 +98,123 @@ class Pry
out.puts "Last result: #{Pry.view(Pry.last_result)}" out.puts "Last result: #{Pry.view(Pry.last_result)}"
end end
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
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
command "ls_methods" do
describe "List public methods defined on class of receiver."
action do |opts|
opts[:output].puts "#{Pry.view(opts[:target].eval('public_methods(false)'))}"
end
end
command "ls_imethods" do
describe "List public instance methods defined on class of receiver."
action do |opts|
opts[:output].puts "#{Pry.view(opts[:target].eval('public_instance_methods(false)'))}"
end
end
command ["exit", "quit", "back"] do
describe "End the current Pry session."
action do |opts|
throw(:breakout, opts[:nesting].level)
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,10 +178,8 @@ 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|
@ -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,30 +260,68 @@ describe Pry do
str_output2.string.should =~ /7/ str_output2.string.should =~ /7/
end end
describe "commands" do
it 'should set the commands default, and the default should be overridable' do it 'should set the commands default, and the default should be overridable' do
commands = { class Command0 < Pry::CommandBase
"hello" => proc { |opts| opts[:output].puts "hello world"; opts[:val].clear } command "hello" do
} describe ""
action { |opts| opts[:output].puts "hello world"; opts[:val].clear }
end
end
def commands.commands() self end Pry.commands = Command0
Pry.commands = commands
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 }
def commands.commands() self end end
str_output = StringIO.new
Pry.new(:input => InputTester.new("goodbye"), :output => str_output, :commands => commands).rep
str_output.string.should =~ /goodbye world/
end end
str_output = StringIO.new
Pry.new(:input => InputTester.new("goodbye"), :output => str_output, :commands => Command1).rep
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
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 }

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 }
end
command "command2" do
describe "command 2 test"
pattern /command2\s*(.*)/
action { |opts|
arg = opts[:captures].first arg = opts[:captures].first
opts[:output].puts arg opts[:output].puts arg
opts[:val].clear opts[:val].clear
end
} }
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