2010-12-24 03:30:51 -05:00
|
|
|
class Pry
|
2010-12-30 10:01:11 -05:00
|
|
|
|
2011-01-10 19:01:48 -05:00
|
|
|
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)
|
|
|
|
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
|
|
|
|
description "This menu."
|
|
|
|
action proc { |opts|
|
|
|
|
out = opts[:output]
|
|
|
|
out.puts "Command list:"
|
|
|
|
out.puts "--"
|
|
|
|
opts[:command_info].each do |k, v|
|
|
|
|
puts "#{Array(k).first}".ljust(18) + v
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-30 10:01:11 -05:00
|
|
|
# Default commands used by Pry.
|
2011-01-04 22:23:11 -05:00
|
|
|
# @note
|
2010-12-30 10:01:11 -05:00
|
|
|
# If you plan to replace the default Commands class with a custom
|
|
|
|
# one then it must have a `commands` method that returns a Hash.
|
2010-12-26 08:39:46 -05:00
|
|
|
class Commands
|
|
|
|
|
2011-01-04 22:23:11 -05:00
|
|
|
# This method returns a hash that defines the commands implemented for the REPL session.
|
|
|
|
# The hash has the following form:
|
|
|
|
#
|
|
|
|
# * Each key is a command, it should either be a String or a
|
|
|
|
# Regexp or an Array.
|
|
|
|
# * Where it is an Array, each element should be a String or a
|
|
|
|
# Regexp, and the elements are considered to be aliases.
|
|
|
|
# * Each value is the action to take for the command. The value
|
|
|
|
# should be a `proc`.
|
|
|
|
# * If the proc needs to generate output it should write to the
|
|
|
|
# `opts[:output]` object, as follows: `opts[:output].puts "hello world"`
|
|
|
|
# * When the proc is invoked it is passed parameters in the form
|
|
|
|
# of an options hash, the parameters are as follows:
|
|
|
|
#
|
|
|
|
# * `opts[:val]` The current line of input.
|
|
|
|
# * `opts[:eval_string]` The cumulative lines of input for a multi-line input.
|
|
|
|
# * `opts[:target]` The receiver of the Pry session.
|
|
|
|
# * `opts[:nesting]` The nesting level of the current Pry Session.
|
|
|
|
# * `opts[:output]` The `output` object for the current Pry session.
|
|
|
|
# * `opts[:captures]` The Regexp captures for the command (if
|
|
|
|
# any) - This can be used to implement command parameters.
|
|
|
|
#
|
|
|
|
# @return [Hash] The commands hash.
|
|
|
|
# @example A 'hello' command.
|
|
|
|
# def commands
|
|
|
|
# {
|
|
|
|
# /^hello\s*(.+)/ => proc do |opts|
|
|
|
|
# opts[:output].puts "hello #{opts[:captures].first}"
|
|
|
|
# }
|
|
|
|
# end
|
2010-12-26 08:39:46 -05:00
|
|
|
def commands
|
|
|
|
@commands ||= {
|
|
|
|
"!" => proc do |opts|
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts "Refreshed REPL"
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
opts[:eval_string].clear
|
|
|
|
end,
|
2010-12-30 10:01:11 -05:00
|
|
|
"!pry" => proc do |opts|
|
|
|
|
Pry.start(opts[:target])
|
|
|
|
opts[:val].clear
|
|
|
|
end,
|
2010-12-26 08:39:46 -05:00
|
|
|
["exit_program", "quit_program"] => proc do
|
|
|
|
exit
|
|
|
|
end,
|
2010-12-27 05:56:55 -05:00
|
|
|
/^help\s*(.+)?/ => proc do |opts|
|
|
|
|
param = opts[:captures].first
|
2011-01-04 22:23:11 -05:00
|
|
|
self.show_help(opts[:output], param)
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
"nesting" => proc do |opts|
|
2011-01-04 22:23:11 -05:00
|
|
|
self.show_nesting(opts[:output], opts[:nesting])
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
"status" => proc do |opts|
|
2011-01-04 22:23:11 -05:00
|
|
|
self.show_status(opts[:output], opts[:nesting], opts[:target])
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
"exit_all" => proc do
|
|
|
|
throw(:breakout, 0)
|
|
|
|
end,
|
2011-01-10 08:54:17 -05:00
|
|
|
["exit", "quit", "back", /^cd\s*\.\./] => proc do |opts|
|
2010-12-26 08:39:46 -05:00
|
|
|
throw(:breakout, opts[:nesting].level)
|
|
|
|
end,
|
|
|
|
"ls" => proc do |opts|
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts "#{opts[:target].eval('Pry.view(local_variables + instance_variables)')}"
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^cat\s+(.+)/ => proc do |opts|
|
|
|
|
obj = opts[:captures].first
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts opts[:target].eval("#{obj}.inspect")
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^cd\s+(.+)/ => proc do |opts|
|
|
|
|
obj = opts[:captures].first
|
2011-01-10 08:54:17 -05:00
|
|
|
|
|
|
|
throw(:breakout, opts[:nesting].level) if obj == ".."
|
|
|
|
|
2010-12-26 08:39:46 -05:00
|
|
|
opts[:target].eval("#{obj}.pry")
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^show_doc\s*(.+)/ => proc do |opts|
|
|
|
|
meth_name = opts[:captures].first
|
|
|
|
doc = opts[:target].eval("method(:#{meth_name})").comment
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts doc
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^show_idoc\s*(.+)/ => proc do |opts|
|
|
|
|
meth_name = opts[:captures].first
|
|
|
|
doc = opts[:target].eval("instance_method(:#{meth_name})").comment
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^show_method\s*(.+)/ => proc do |opts|
|
|
|
|
meth_name = opts[:captures].first
|
|
|
|
code = opts[:target].eval("method(:#{meth_name})").source
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts code
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^show_imethod\s*(.+)/ => proc do |opts|
|
|
|
|
meth_name = opts[:captures].first
|
|
|
|
code = opts[:target].eval("instance_method(:#{meth_name})").source
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end,
|
|
|
|
/^jump_to\s*(\d*)/ => proc do |opts|
|
|
|
|
break_level = opts[:captures].first.to_i
|
|
|
|
nesting = opts[:nesting]
|
|
|
|
|
|
|
|
case break_level
|
|
|
|
when nesting.level
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts "Already at nesting level #{nesting.level}"
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
when (0...nesting.level)
|
|
|
|
throw(:breakout, break_level + 1)
|
|
|
|
else
|
|
|
|
max_nest_level = nesting.level - 1
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}."
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end
|
2010-12-27 07:36:29 -05:00
|
|
|
end,
|
|
|
|
"ls_methods" => proc do |opts|
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts "#{Pry.view(opts[:target].eval('public_methods(false)'))}"
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-27 07:36:29 -05:00
|
|
|
end,
|
|
|
|
"ls_imethods" => proc do |opts|
|
2011-01-04 22:23:11 -05:00
|
|
|
opts[:output].puts "#{Pry.view(opts[:target].eval('public_instance_methods(false)'))}"
|
2010-12-30 10:01:11 -05:00
|
|
|
opts[:val].clear
|
2010-12-26 08:39:46 -05:00
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def command_info
|
|
|
|
@command_info ||= {
|
|
|
|
"!" => "Refresh the REPL.",
|
2010-12-30 10:01:11 -05:00
|
|
|
"!pry" => "Start a Pry session on current self; this even works mid-expression.",
|
2010-12-27 05:56:55 -05:00
|
|
|
["exit_program", "quit_program"] => "end the current program.",
|
2010-12-26 08:39:46 -05:00
|
|
|
"help" => "This menu.",
|
|
|
|
"nesting" => "Show nesting information.",
|
|
|
|
"status" => "Show status information.",
|
|
|
|
"exit_all" => "End all nested Pry sessions",
|
2010-12-27 06:45:47 -05:00
|
|
|
["exit", "quit", "back", /cd\s*\.\./] => "End the current Pry session.",
|
2010-12-26 08:39:46 -05:00
|
|
|
"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>",
|
2010-12-27 07:36:29 -05:00
|
|
|
"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."
|
2010-12-26 08:39:46 -05:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2011-01-04 22:23:11 -05:00
|
|
|
def show_help(out, param)
|
2010-12-27 05:56:55 -05:00
|
|
|
if !param
|
|
|
|
out.puts "Command list:"
|
|
|
|
out.puts "--"
|
|
|
|
command_info.each do |k, v|
|
2010-12-27 06:45:47 -05:00
|
|
|
puts "#{Array(k).first}".ljust(18) + v
|
2010-12-27 05:56:55 -05:00
|
|
|
end
|
|
|
|
else
|
2010-12-27 06:45:47 -05:00
|
|
|
key = command_info.keys.find { |v| Array(v).any? { |k| k === param } }
|
2010-12-27 05:56:55 -05:00
|
|
|
if key
|
|
|
|
out.puts command_info[key]
|
|
|
|
else
|
|
|
|
out.puts "No info for command: #{param}"
|
|
|
|
end
|
|
|
|
end
|
2010-12-26 08:39:46 -05:00
|
|
|
end
|
2010-12-24 03:30:51 -05:00
|
|
|
|
2011-01-04 22:23:11 -05:00
|
|
|
def show_nesting(out, nesting)
|
2010-12-26 08:39:46 -05:00
|
|
|
out.puts "Nesting status:"
|
|
|
|
out.puts "--"
|
|
|
|
nesting.each do |level, obj|
|
|
|
|
if level == 0
|
|
|
|
out.puts "#{level}. #{Pry.view(obj)} (Pry top level)"
|
|
|
|
else
|
|
|
|
out.puts "#{level}. #{Pry.view(obj)}"
|
|
|
|
end
|
2010-12-25 08:51:34 -05:00
|
|
|
end
|
2010-12-24 03:30:51 -05:00
|
|
|
end
|
2010-12-25 08:51:34 -05:00
|
|
|
|
2011-01-04 22:23:11 -05:00
|
|
|
def show_status(out, nesting, target)
|
2010-12-26 08:39:46 -05:00
|
|
|
out.puts "Status:"
|
|
|
|
out.puts "--"
|
|
|
|
out.puts "Receiver: #{Pry.view(target.eval('self'))}"
|
|
|
|
out.puts "Nesting level: #{nesting.level}"
|
2010-12-27 07:36:29 -05:00
|
|
|
out.puts "Local variables: #{Pry.view(target.eval('local_variables'))}"
|
2010-12-27 06:45:47 -05:00
|
|
|
out.puts "Pry instance: #{Pry.active_instance}"
|
2010-12-26 08:39:46 -05:00
|
|
|
out.puts "Last result: #{Pry.view(Pry.last_result)}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|