Version 0.5.4. Added Pry.run_command

* Added Pry.run_command for running Pry commands outside of a session.
* Added more graceful error messages when trying to show-method on dynamically defined methods.
* Added tests for new Pry.run_command method
* Updated README with info on Pry.run_command
This commit is contained in:
John Mair 2011-02-19 06:01:21 +13:00
parent adfd39f962
commit 5cda6865d4
5 changed files with 117 additions and 20 deletions

View File

@ -221,6 +221,9 @@ it returns the result of the evaluation or an Exception object in
case of error. It also takes the same parameters as `Pry#repl()`
* Similarly `Pry#r()` only performs the Read section of the REPL, only
returning the Ruby expression (as a string). It takes the same parameters as all the others.
* `Pry.run_command COMMAND` enables you to invoke Pry commands outside
of a session, e.g `Pry.run_command "ls -m", :context => MyObject`. See
docs for more info.
### Session commands

View File

@ -16,6 +16,12 @@ class Pry
meth_name
end
end
check_for_dynamically_defined_method = lambda do |file|
if file =~ /(\(.*\))|<.*>/
raise "Cannot retrieve source for dynamically defined method."
end
end
command "!", "Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop." do
output.puts "Input buffer cleared!"
@ -72,7 +78,7 @@ class Pry
command "ls", "Show the list of vars in the current scope. Type `ls --help` for more info." do |*args|
options = {}
# Set target local to the default -- note that we can set a different target for
# ls if we like: e.g ls my_var
target = target()
@ -172,10 +178,10 @@ Shows local and instance variables by default.
info["instance variables"] = [Array(target.eval("instance_variables")).sort, i += 1] if options[:i] || options[:a]
info["class variables"] = [if target_self.is_a?(Module)
Array(target.eval("class_variables")).sort
else
Array(target.eval("self.class.class_variables")).sort
end, i += 1] if options[:k] || options[:a]
Array(target.eval("class_variables")).sort
else
Array(target.eval("self.class.class_variables")).sort
end, i += 1] if options[:k] || options[:a]
info["global variables"] = [Array(target.eval("global_variables")).sort, i += 1] if options[:g] || options[:a]
@ -196,9 +202,9 @@ Shows local and instance variables by default.
if Module.method(:constants).arity == 0
csuper = nil
end
info["constants"] = [Array(target_self.is_a?(Module) ? target.eval("constants(#{csuper})") :
target.eval("self.class.constants(#{csuper})")).uniq.sort, i += 1] if options[:c] || options[:a]
target.eval("self.class.constants(#{csuper})")).uniq.sort, i += 1] if options[:c] || options[:a]
# verbose output?
if options[:v]
@ -212,9 +218,11 @@ Shows local and instance variables by default.
end
end
# plain
# plain
else
output.puts Pry.view(info.values.sort_by { |v| v.last }.map { |v| v.first }.inject(&:+))
list = info.values.sort_by { |v| v.last }.map { |v| v.first }.inject(&:+)
output.puts Pry.view(list)
list
end
end
@ -264,8 +272,8 @@ e.g: eval-file -c "hello.rb"
TOPLEVEL_BINDING.eval(File.read(file_name))
output.puts "--\nEval'd '#{file_name}' at top-level."
end
new_constants = Object.constants - old_constants
output.puts "Brought in the following top-level constants: #{new_constants.inspect}" if !new_constants.empty?
new_constants = Object.constants - old_constants
output.puts "Brought in the following top-level constants: #{new_constants.inspect}" if !new_constants.empty?
end
command "cat", "Show output of VAR.inspect. Aliases: inspect" do |obj|
@ -329,8 +337,10 @@ e.g show-doc hello_method
next
end
doc = meth.comment
file, line = meth.source_location
check_for_dynamically_defined_method.call(file)
doc = meth.comment
output.puts "--\nFrom #{file} @ line ~#{line}:\n--"
output.puts doc
end
@ -378,26 +388,31 @@ e.g: show-method hello_method
next
end
code = meth.source
file, line = meth.source_location
check_for_dynamically_defined_method.call(file)
code = meth.source
output.puts "--\nFrom #{file} @ line #{line}:\n--"
output.puts code
code
end
command "show-command", "Show sourcecode for a Pry command, e.g: show-command ls" do |command_name|
cmds = Pry.active_instance.commands.commands
command "show-command", "Show sourcecode for a Pry command, e.g: show-command cd" do |command_name|
if !command_name
output.puts "You must provide a command name."
next
end
if cmds[command_name]
meth = cmds[command_name][:action]
code = meth.source
if commands[command_name]
meth = commands[command_name][:action]
file, line = meth.source_location
check_for_dynamically_defined_method.call(file)
code = meth.source
output.puts "--\nFrom #{file} @ line #{line}:\n--"
output.puts code
code
else
output.puts "No such command: #{command_name}."
end

View File

@ -1,4 +1,5 @@
require 'readline'
require 'shellwords'
# @author John Mair (banisterfiend)
class Pry
@ -94,6 +95,47 @@ class Pry
end
end
# Run a Pry command from outside a session. The commands available are
# those referenced by `Pry.commands` (the default command set).
# Command output is suppresed by default, this is because the return
# value (if there is one) is likely to be more useful.
# @param [String] arg_string The Pry command (including arguments,
# if any).
# @param [Hash] options Optional named parameters.
# @option options [Object, Binding] :context The object context to run the
# command under. Defaults to `TOPLEVEL_BINDING` (main).
# @option options [Boolean] :show_output Whether to show command
# output. Defaults to false.
# @example Run at top-level with no output.
# Pry.run_command "ls"
# @example Run under Pry class, returning only public methods.
# Pry.run_command "ls -m", :context => Pry
# @example Display command output.
# Pry.run_command "ls -av", :show_output => true
def self.run_command(arg_string, options={})
name, arg_string = arg_string.split(/\s+/, 2)
arg_string = "" if !arg_string
options = {
:context => TOPLEVEL_BINDING,
:show_output => false
}.merge!(options)
null_output = Object.new.tap { |v| v.instance_eval { def puts(*) end } }
commands = Pry.commands.dup
commands.output = options[:show_output] ? Pry.output : null_output
commands.target = Pry.binding_for(options[:context])
cmd = commands.commands[name]
if cmd
action = cmd[:action]
commands.instance_exec(*Shellwords.shellwords(arg_string), &action)
else
raise "No such command: #{name}"
end
end
# Set all the configurable options back to their default values
def self.reset_defaults
@input = Readline

View File

@ -1,3 +1,3 @@
class Pry
VERSION = "0.5.3"
VERSION = "0.5.4"
end

View File

@ -297,6 +297,43 @@ describe Pry do
str_output2.string.should =~ /7/
end
describe "Pry.run_command" do
before do
class RCTest
def a() end
B = 20
@x = 10
end
end
after do
Object.remove_const(:RCTest)
end
it "should execute command in the appropriate object context" do
result = Pry.run_command "ls", :context => RCTest
result.map(&:to_sym).should == [:@x]
end
it "should execute command with parameters in the appropriate object context" do
result = Pry.run_command "ls -M", :context => RCTest
result.map(&:to_sym).should == [:a]
end
it "should execute command and show output with :show_output => true flag" do
str = StringIO.new
Pry.output = str
result = Pry.run_command "ls -av", :context => RCTest, :show_output => true
str.string.should =~ /global variables/
Pry.output = $stdout
end
it "should execute command with multiple parameters" do
result = Pry.run_command "ls -c -M RCTest"
result.map(&:to_sym).should == [:a, :B]
end
end
describe "commands" do
it 'should set the commands default, and the default should be overridable' do
class Command0 < Pry::CommandBase