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

Merge remote-tracking branch 'upstream/dev'

* upstream/dev: (185 commits)
  removed unnecessary pry_command? method from command_processor.rb. And removed #clear singleton method on val and eval_string, converted to #replace
  removed deprecated has_rdoc= call
  tiny refactor to repl_prologue in pry_instance.rb
  Simplify Pry::CommandSet#each.
  experimental command 'play'
  added CommandSet#list_commands, convenience method for repl exploration of command sets
  CommandSet is Enumerable.
  added tests for help and show-command commands, and by proxy testing :listing functionality and regex commands
  fixed spelling mistakes in comments
  removed all instances of old opts[:arg_string]
  changed formatting for uninstalled command message (added bold)
  changed 'rake pry' to invoke executable instead of starting pry session in Rakefile
  Revert "Made Pry able to cd into procs"
  added tests for updated CommandContext#run method
  Made Pry able to cd into procs
  Pry.config.history.file now has File.expand_path() applied before being saved
  reordered plugin/history loading so that plugins are loaded before history
  removed some unnecessary helper methods
  added reload-method experimental command, removed rue- experimental command, in experimental.rb
  added hist --show option + tests
  ...

Conflicts:
	Rakefile
	lib/pry/commands.rb
This commit is contained in:
David Palm 2011-06-01 08:29:32 +02:00
commit 438fe17fcb
41 changed files with 3680 additions and 2511 deletions

View file

@ -441,11 +441,11 @@ help that can be accessed via typing `command_name --help`. A command
will typically say in its description if the `--help` option is
avaiable.
### Use Pry as your Rails 3 Console
### Use Pry as your Rails Console
This is currently a hack, but follow the gist kindly provided by
MyArtChannel: [https://gist.github.com/941174](https://gist.github.com/941174)
pry -r./config/environment
MyArtChannel has kindly provided a hack to replace the `rails console` command in Rails 3: [https://gist.github.com/941174](https://gist.github.com/941174) This is not recommended for code bases with multiple developers, as they may not all want to use Pry.
### Other Features and limitations
@ -486,13 +486,13 @@ invoke any of these methods directly depending on exactly what aspect of the fun
#### Limitations:
* Some Pry commands (e.g `show-command`) do not work in Ruby 1.8.
* Some Pry commands (e.g `show-command`) do not work in Ruby 1.8
MRI. But many other commands do work in Ruby 1.8 MRI, e.g
`show-method`, due to a functional 1.8 source_location implementation.
* JRuby not officially supported due to currently too many quirks and
strange behaviour. Nonetheless most functionality should still work
OK in JRuby. Full JRuby support coming in a future version.
* `method_source` functionality does not work in JRuby with Ruby 1.8
* Color support does not work in JRuby with Ruby 1.9 (due to a
limitation in JRuby's regex).
* Tab completion is currently a bit broken/limited this will have a
major overhaul in a future version.

View file

@ -5,36 +5,35 @@ $:.unshift 'lib'
require 'pry/version'
CLOBBER.include("**/*~", "**/*#*", "**/*.log")
CLEAN.include("**/*#*", "**/*#*.*", "**/*_flymake*.*", "**/*_flymake", "**/*.rbc")
CLEAN.include("**/*#*", "**/*#*.*", "**/*_flymake*.*", "**/*_flymake",
"**/*.rbc", "**/.#*.*")
def apply_spec_defaults(s)
s.name = "pry"
s.summary = "attach an irb-like session to any object at runtime"
s.summary = "an IRB alternative and runtime developer console"
s.version = Pry::VERSION
s.date = Time.now.strftime '%Y-%m-%d'
s.author = "John Mair (banisterfiend)"
s.email = 'jrmair@gmail.com'
s.description = s.summary
s.homepage = "http://banisterfiend.wordpress.com"
s.has_rdoc = 'yard'
s.executables = ["pry"]
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- test/*`.split("\n")
s.add_dependency("ruby_parser",">=2.0.5")
s.add_dependency("coderay",">=0.9.8")
s.add_dependency("slop",">=1.5.5")
s.add_dependency("slop","~>1.6.0")
s.add_dependency("method_source",">=0.4.0")
s.add_development_dependency("bacon",">=1.1.0")
end
task :test do
sh "bacon -Itest -rubygems test/test.rb"
sh "bacon -Itest -rubygems -a"
end
desc "run pry"
task :pry do
require 'pry'
binding.pry
load 'bin/pry'
end
desc "show pry version"
@ -57,7 +56,7 @@ namespace :ruby do
task :gemspec do
File.open("#{spec.name}-#{spec.version}.gemspec", "w") do |f|
f << spec.to_ruby
end
end
end
end
@ -88,7 +87,6 @@ namespace :jruby do
end
end
desc "build all platform gems at once"
task :gems => [:clean, :rmgems, "ruby:gem", "jruby:gem", "mswin32:gem", "mingw32:gem"]
@ -97,7 +95,7 @@ task :rmgems => ["ruby:clobber_package"]
desc "build and push latest gems"
task :pushgems => :gems do
chdir("#{direc}/pkg") do
chdir("#{File.dirname(__FILE__)}/pkg") do
Dir["*.gem"].each do |gemfile|
sh "gem push #{gemfile}"
end

30
TODO
View file

@ -1,13 +1,39 @@
ROADMAP
FUTURE
* allows pipes (|) for commands
--------
* Pry server and Pry client for SLIME style remote repl connectinos.
* i18n support
0.9.0
Major features
--------------
* Restructure command system and helpers (almost complete)
* Delete unnecessary commands, add a couple of new ones (e.g amend-line)
* Readline history
* Plugin support
* Support Rubinius core methods
* in[] and out[] arrays
* Improve test coverage (test some commands, etc)
Minor changes
-------------
* improve edit-method support for various editors
* ensure all commands have appropriate error handing and informative error messages
* show-doc should include signature of method
Optional
--------
* multi-line readline support
0.8.0
* allow #{} interpolation of all commands
* update documentation! new commands and features and change in behaviour of `run`
* add ; at end of line to suppress return value output
* Remove message spam (before/after hooks)
* stop commands returning a value
* stop commands returning a value
* use `redo` in the r() method when encounter a command
* shell functionality should just use system(), but redirect in and
out to Pry.input and Pry.output by reassining $stdin and $stdout

11
bin/pry
View file

@ -3,6 +3,8 @@
# (C) John Mair (banisterfiend)
# MIT license
$0 = 'pry'
begin
require 'pry'
rescue LoadError
@ -48,15 +50,8 @@ end
# invoked via cli
Pry.cli = true
Pry::Commands.instance_eval do
command "reset", "Reset the REPL to a clean state." do
output.puts "Pry reset."
exec("pry")
end
end
# load ~/.pryrc, if not suppressed with -f option
Pry.should_load_rc = !opts.f?
Pry.config.should_load_rc = !opts.f?
# create the actual context
context = Pry.binding_for(eval(opts[:context]))

View file

@ -1,6 +1,65 @@
# (C) John Mair (banisterfiend) 2011
# MIT License
class Pry
# The default hooks - display messages when beginning and ending Pry sessions.
DEFAULT_HOOKS = {
:before_session => proc do |out, target|
# ensure we're actually in a method
meth_name = target.eval('__method__')
file = target.eval('__FILE__')
# /unknown/ for rbx
if file !~ /(\(.*\))|<.*>/ && file !~ /__unknown__/ && file != "" && file != "-e"
Pry.run_command "whereami 5", :output => out, :show_output => true, :context => target, :commands => Pry::Commands
end
end
}
# The default prints
DEFAULT_PRINT = proc do |output, value|
if Pry.color
output.puts "=> #{CodeRay.scan(Pry.view(value), :ruby).term}"
else
output.puts "=> #{Pry.view(value)}"
end
end
# Will only show the first line of the backtrace
DEFAULT_EXCEPTION_HANDLER = proc do |output, exception|
output.puts "#{exception.class}: #{exception.message}"
output.puts "from #{exception.backtrace.first}"
end
# The default prompt; includes the target and nesting level
DEFAULT_PROMPT = [
proc { |target_self, nest_level|
if nest_level == 0
"pry(#{Pry.view_clip(target_self)})> "
else
"pry(#{Pry.view_clip(target_self)}):#{Pry.view_clip(nest_level)}> "
end
},
proc { |target_self, nest_level|
if nest_level == 0
"pry(#{Pry.view_clip(target_self)})* "
else
"pry(#{Pry.view_clip(target_self)}):#{Pry.view_clip(nest_level)}* "
end
}
]
# A simple prompt - doesn't display target or nesting level
SIMPLE_PROMPT = [proc { ">> " }, proc { ">* " }]
SHELL_PROMPT = [
proc { |target_self, _| "pry #{Pry.view_clip(target_self)}:#{Dir.pwd} $ " },
proc { |target_self, _| "pry #{Pry.view_clip(target_self)}:#{Dir.pwd} * " }
]
end
require "method_source"
require 'shellwords'
require "readline"
@ -20,20 +79,13 @@ if RUBY_PLATFORM =~ /mswin/ || RUBY_PLATFORM =~ /mingw/
end
require "pry/version"
require "pry/hooks"
require "pry/print"
require "pry/helpers"
require "pry/command_set"
require "pry/commands"
require "pry/command_context"
require "pry/prompts"
require "pry/custom_completions"
require "pry/completion"
require "pry/plugins"
require "pry/core_extensions"
require "pry/pry_class"
require "pry/pry_instance"
# TEMPORARY HACK FOR BUG IN JRUBY 1.9 REGEX (which kills CodeRay)
if RUBY_VERSION =~ /1.9/ && RUBY_ENGINE =~ /jruby/
Pry.color = false
end
require "pry/pry_instance"

View file

@ -4,25 +4,26 @@ class Pry
class CommandContext
attr_accessor :output
attr_accessor :target
attr_accessor :captures
attr_accessor :eval_string
attr_accessor :arg_string
attr_accessor :opts
attr_accessor :command_set
attr_accessor :command_processor
def run(name, *args)
if name.start_with? "."
cmd = name[1..-1]
command_processor.
execute_system_command([name, Shellwords.join(args)].join(' '),
target)
else
command_set.run_command(self, name, *args)
end
def run(command_string, *args)
complete_string = "#{command_string} #{args.join(" ")}"
command_processor.process_commands(complete_string, eval_string, target)
end
def commands
command_set.commands
end
def text
Pry::Helpers::Text
end
include Pry::Helpers::BaseHelpers
include Pry::Helpers::CommandHelpers
end

View file

@ -2,9 +2,6 @@ require 'forwardable'
class Pry
class CommandProcessor
SYSTEM_COMMAND_DELIMITER = "."
SYSTEM_COMMAND_REGEX = /^#{Regexp.escape(SYSTEM_COMMAND_DELIMITER)}(.*)/
extend Forwardable
attr_accessor :pry_instance
@ -19,22 +16,16 @@ class Pry
# @param [String] val The string passed in from the Pry prompt.
# @return [Boolean] Whether the string is a valid command.
def valid_command?(val)
system_command?(val) || pry_command?(val)
!!(command_matched(val)[0])
end
# Is the string a valid system command?
# @param [String] val The string passed in from the Pry prompt.
# @return [Boolean] Whether the string is a valid system command.
def system_command?(val)
!!(SYSTEM_COMMAND_REGEX =~ val)
end
# Is the string a valid pry command?
# A Pry command is a command that is not a system command.
# @param [String] val The string passed in from the Pry prompt.
# @return [Boolean] Whether the string is a valid Pry command.
def pry_command?(val)
!!command_matched(val).first
def convert_to_regex(obj)
case obj
when String
Regexp.escape(obj)
else
obj
end
end
# Revaluate the string (str) and perform interpolation.
@ -49,41 +40,6 @@ class Pry
target.eval(dumped_str)
end
# Execute a given system command.
# The commands first have interpolation applied against the
# `target` context.
# All system command are forwarded to a shell. Note that the `cd`
# command is special-cased and is converted internallly to a `Dir.chdir`
# @param [String] val The system command to execute.
# @param [Binding] target The context in which to perform string interpolation.
def execute_system_command(val, target)
SYSTEM_COMMAND_REGEX =~ val
cmd = interpolate_string($1, target)
if cmd =~ /^cd\s+(.+)/i
begin
@@cd_history ||= []
if $1 == "-"
dest = @@cd_history.pop || Dir.pwd
else
dest = File.expand_path($1)
end
@@cd_history << Dir.pwd
Dir.chdir(dest)
rescue Errno::ENOENT
output.puts "No such directory: #{dest}"
end
else
if !system(cmd)
output.puts "Error: there was a problem executing system command: #{cmd}"
end
end
# Tick, tock, im getting rid of this shit soon.
val.replace("")
end
# Determine whether a Pry command was matched and return command data
# and argument string.
# This method should not need to be invoked directly.
@ -91,10 +47,10 @@ class Pry
# @return [Array] The command data and arg string pair
def command_matched(val)
_, cmd_data = commands.commands.find do |name, cmd_data|
/^#{Regexp.escape(name)}(?!\S)(?:\s+(.+))?/ =~ val
/^#{convert_to_regex(name)}(?!\S)/ =~ val
end
[cmd_data, $1]
[cmd_data, (Regexp.last_match ? Regexp.last_match.captures : nil), (Regexp.last_match ? Regexp.last_match.end(0) : nil)]
end
# Process Pry commands. Pry commands are not Ruby methods and are evaluated
@ -107,30 +63,25 @@ class Pry
# multi-line input.
# @param [Binding] target The receiver of the commands.
def process_commands(val, eval_string, target)
def val.clear() replace("") end
def eval_string.clear() replace("") end
if system_command?(val)
execute_system_command(val, target)
return
end
command, captures, pos = command_matched(val)
# no command was matched, so return to caller
return if !pry_command?(val)
return if !command
val.replace interpolate_string(val, target)
command, args_string = command_matched(val)
args = args_string ? Shellwords.shellwords(args_string) : []
val.replace interpolate_string(val, target) if command.options[:interpolate]
arg_string = val[pos..-1].strip
args = arg_string ? Shellwords.shellwords(arg_string) : []
options = {
:val => val,
:arg_string => arg_string,
:eval_string => eval_string,
:nesting => nesting,
:commands => commands.commands
:commands => commands.commands,
:captures => captures
}
execute_command(target, command.name, options, *args)
execute_command(target, command.name, options, *(captures + args))
end
# Execute a Pry command.
@ -146,6 +97,9 @@ class Pry
context.opts = options
context.target = target
context.output = output
context.captures = options[:captures]
context.eval_string = options[:eval_string]
context.arg_string = options[:arg_string]
context.command_set = commands
context.command_processor = self
@ -153,7 +107,7 @@ class Pry
ret = commands.run_command(context, command, *args)
# Tick, tock, im getting rid of this shit soon.
options[:val].clear
options[:val].replace("")
ret
end

View file

@ -5,32 +5,44 @@ class Pry
end
end
# This class used to create sets of commands. Commands can be impoted from
# This class is used to create sets of commands. Commands can be imported from
# different sets, aliased, removed, etc.
class CommandSet
class Command < Struct.new(:name, :description, :options, :block)
def call(context, *args)
if stub_block = options[:stub_info]
context.instance_eval(&stub_block)
else
ret = context.instance_exec(*args, &block)
ret = context.instance_exec(*correct_arg_arity(block.arity, args), &block)
ret if options[:keep_retval]
end
end
private
def correct_arg_arity(arity, args)
case arity <=> 0
when -1
args
when 1, 0
# Keep 1.8 happy
args.values_at 0..(arity - 1)
end
end
end
include Enumerable
include Pry::Helpers::BaseHelpers
attr_reader :commands
attr_reader :name
attr_reader :helper_module
# @param [Symbol] name Name of the command set
# @param [Array<CommandSet>] imported_sets Sets which will be imported
# automatically
# @yield Optional block run to define commands
def initialize(name, *imported_sets, &block)
@name = name
@commands = {}
def initialize(*imported_sets, &block)
@commands = {}
@helper_module = Module.new
define_default_commands
import(*imported_sets)
@ -39,19 +51,29 @@ class Pry
end
# Defines a new Pry command.
# @param [String, Array] names The name of the command (or array of
# command name aliases).
# @param [String, Regexp] name The name of the command. Can be
# Regexp as well as String.
# @param [String] description A description of the command.
# @param [Hash] options The optional configuration parameters.
# @option options [Boolean] :keep_retval Whether or not to use return value
# of the block for return of `command` or just to return `nil`
# (the default).
# @option options [Array<String>] :requires_gem Whether the command has
# any gem dependencies, if it does and dependencies not met then
# command is disabled and a stub proc giving instructions to
# install command is provided.
# @option options [Boolean] :interpolate Whether string #{} based
# interpolation is applied to the command arguments before
# executing the command. Defaults to true.
# @option options [String] :listing The listing name of the
# command. That is the name by which the command is looked up by
# help and by show-command. Necessary for regex based commands.
# @yield The action to perform. The parameters in the block
# determines the parameters the command will receive. All
# parameters passed into the block will be strings. Successive
# command parameters are separated by whitespace at the Pry prompt.
# @example
# MyCommands = Pry::CommandSet.new :mine do
# MyCommands = Pry::CommandSet.new do
# command "greet", "Greet somebody" do |name|
# puts "Good afternoon #{name.capitalize}!"
# end
@ -63,29 +85,49 @@ class Pry
# # Good afternoon John!
# # pry(main)> help greet
# # Greet somebody
def command(names, description="No description.", options={}, &block)
first_name = Array(names).first
# @example Regexp command
# MyCommands = Pry::CommandSet.new do
# command /number-(\d+)/, "number-N regex command", :listing => "number" do |num, name|
# puts "hello #{name}, nice number: #{num}"
# end
# end
#
# # From pry:
# # pry(main)> _pry_.commands = MyCommands
# # pry(main)> number-10 john
# # hello john, nice number: 10
# # pry(main)> help number
# # number-N regex command
def command(name, description="No description.", options={}, &block)
options = {:requires_gem => []}.merge(options)
options = {
:requires_gem => [],
:keep_retval => false,
:argument_required => false,
:interpolate => true,
:listing => name
}.merge!(options)
unless command_dependencies_met? options
gems_needed = Array(options[:requires_gem])
gems_not_installed = gems_needed.select { |g| !gem_installed?(g) }
options[:stub_info] = proc do
output.puts "\n#{first_name} requires the following gems to be installed: #{(gems_needed.join(", "))}"
output.puts "Command not available due to dependency on gems: `#{gems_not_installed.join(", ")}` not being met."
output.puts "Type `install #{first_name}` to install the required gems and activate this command."
output.puts "\nThe command '#{name}' is #{Helpers::Text.bold("unavailable")} because it requires the following gems to be installed: #{(gems_not_installed.join(", "))}"
output.puts "-"
output.puts "Type `install #{name}` to install the required gems and activate this command."
end
end
Array(names).each do |name|
commands[name] = Command.new(name, description, options, block)
end
commands[name] = Command.new(name, description, options, block)
end
def each &block
@commands.each(&block)
end
# Removes some commands from the set
# @param [Arary<String>] names name of the commands to remove
# @param [Array<String>] names name of the commands to remove
def delete(*names)
names.each { |name| commands.delete name }
end
@ -94,20 +136,24 @@ class Pry
# @param [Array<CommandSet>] sets Command sets, all of the commands of which
# will be imported.
def import(*sets)
sets.each { |set| commands.merge! set.commands }
sets.each do |set|
commands.merge! set.commands
helper_module.send :include, set.helper_module
end
end
# Imports some commands from a set
# @param [CommandSet] set Set to import commands from
# @param [Array<String>] names Commands to import
def import_from(set, *names)
helper_module.send :include, set.helper_module
names.each { |name| commands[name] = set.commands[name] }
end
# Aliases a command
# @param [String] new_name New name of the command.
# @param [String] old_name Old name of the command.
# @pasam [String, nil] desc New description of the command.
# @param [String, nil] desc New description of the command.
def alias_command(new_name, old_name, desc = nil)
commands[new_name] = commands[old_name].dup
commands[new_name].name = new_name
@ -121,11 +167,18 @@ class Pry
# @param [Array<Object>] args Arguments passed to the command
# @raise [NoCommandError] If the command is not defined in this set
def run_command(context, name, *args)
if command = commands[name]
command.call(context, *args)
else
context.extend helper_module
command = commands[name]
if command.nil?
raise NoCommandError.new(name, self)
end
if command.options[:argument_required] && args.empty?
puts "The command '#{command.name}' requires an argument."
else
command.call context, *args
end
end
# Sets the description for a command (replacing the old
@ -133,15 +186,38 @@ class Pry
# @param [String] name The command name.
# @param [String] description The command description.
# @example
# MyCommands = Pry::CommandSet.new :test do
# MyCommands = Pry::CommandSet.new do
# desc "help", "help description"
# end
def desc(name, description)
commands[name].description = description
end
# Defines helpers methods for this command sets.
# Those helpers are only defined in this command set.
#
# @yield A block defining helper methods
# @example
# helpers do
# def hello
# puts "Hello!"
# end
#
# include OtherModule
# end
def helpers(&block)
helper_module.class_eval(&block)
end
# @return [Array] The list of commands provided by the command set.
def list_commands
commands.keys
end
private
def define_default_commands
command "help", "This menu." do |cmd|
if !cmd
output.puts
@ -149,13 +225,13 @@ class Pry
commands.each do |key, command|
if command.description && !command.description.empty?
help_text << "#{key}".ljust(18) + command.description + "\n"
help_text << "#{command.options[:listing]}".ljust(18) + command.description + "\n"
end
end
stagger_output(help_text)
else
if command = commands[cmd]
if command = find_command(cmd)
output.puts command.description
else
output.puts "No info for command: #{cmd}"
@ -164,7 +240,8 @@ class Pry
end
command "install", "Install a disabled command." do |name|
stub_info = commands[name].options[:stub_info]
command = find_command(name)
stub_info = command.options[:stub_info]
if !stub_info
output.puts "Not a command stub. Nothing to do."
@ -172,7 +249,7 @@ class Pry
end
output.puts "Attempting to install `#{name}` command..."
gems_to_install = Array(commands[name].options[:requires_gem])
gems_to_install = Array(command.options[:requires_gem])
gem_install_failed = false
gems_to_install.each do |g|
@ -190,7 +267,17 @@ class Pry
next if gem_install_failed
Gem.refresh
commands[name].options.delete :stub_info
gems_to_install.each do |g|
begin
require g
rescue LoadError
output.puts "Required Gem: `#{g}` installed but not found?!. Aborting command installation."
gem_install_failed = true
end
end
next if gem_install_failed
command.options.delete :stub_info
output.puts "Installation of `#{name}` successful! Type `help #{name}` for information"
end
end

File diff suppressed because it is too large Load diff

View file

@ -14,28 +14,29 @@ class Pry
Readline.completion_append_character = nil
ReservedWords = [
"BEGIN", "END",
"alias", "and",
"begin", "break",
"case", "class",
"def", "defined", "do",
"else", "elsif", "end", "ensure",
"false", "for",
"if", "in",
"module",
"next", "nil", "not",
"or",
"redo", "rescue", "retry", "return",
"self", "super",
"then", "true",
"undef", "unless", "until",
"when", "while",
"yield",
]
"BEGIN", "END",
"alias", "and",
"begin", "break",
"case", "class",
"def", "defined", "do",
"else", "elsif", "end", "ensure",
"false", "for",
"if", "in",
"module",
"next", "nil", "not",
"or",
"redo", "rescue", "retry", "return",
"self", "super",
"then", "true",
"undef", "unless", "until",
"when", "while",
"yield" ]
Operators = ["%", "&", "*", "**", "+", "-", "/",
"<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
"[]", "[]=", "^", "!", "!=", "!~"]
Operators = [
"%", "&", "*", "**", "+", "-", "/",
"<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
"[]", "[]=", "^", "!", "!=", "!~"
]
# Return a new completion proc for use by Readline.
# @param [Binding] target The current binding context.
@ -188,13 +189,13 @@ class Pry
def self.select_message(receiver, message, candidates)
candidates.grep(/^#{message}/).collect do |e|
case e
when /^[a-zA-Z_]/
receiver + "." + e
when /^[0-9]/
when *Operators
#receiver + " " + e
end
case e
when /^[a-zA-Z_]/
receiver + "." + e
when /^[0-9]/
when *Operators
#receiver + " " + e
end
end
end
end

86
lib/pry/config.rb Normal file
View file

@ -0,0 +1,86 @@
require 'ostruct'
class Pry
class Config < OpenStruct
# Get/Set the object to use for input by default by all Pry instances.
# @return [#readline] The object to use for input by default by all
# Pry instances.
attr_accessor :input
# Get/Set the object to use for output by default by all Pry instances.
# @return [#puts] The object to use for output by default by all
# Pry instances.
attr_accessor :output
# Get/Set the object to use for commands by default by all Pry instances.
# @return [Pry::CommandBase] The object to use for commands by default by all
# Pry instances.
attr_accessor :commands
# Get/Set the Proc to use for printing by default by all Pry
# instances.
# This is the 'print' component of the REPL.
# @return [Proc] The Proc to use for printing by default by all
# Pry instances.
attr_accessor :print
# @return [Proc] The Proc to use for printing exceptions by default by all
# Pry instances.
attr_accessor :exception_handler
# Get/Set the Hash that defines Pry hooks used by default by all Pry
# instances.
# @return [Hash] The hooks used by default by all Pry instances.
# @example
# Pry.hooks :before_session => proc { puts "hello" },
# :after_session => proc { puts "goodbye" }
attr_accessor :hooks
# Get the array of Procs to be used for the prompts by default by
# all Pry instances.
# @return [Array<Proc>] The array of Procs to be used for the
# prompts by default by all Pry instances.
attr_accessor :prompt
# The default editor to use. Defaults to $EDITOR or nano if
# $EDITOR is not defined.
# If `editor` is a String then that string is used as the shell
# command to invoke the editor. If `editor` is callable (e.g a
# Proc) then `file` and `line` are passed in as parameters and the
# return value of that callable invocation is used as the exact
# shell command to invoke the editor.
# @example String
# Pry.editor = "emacsclient"
# @example Callable
# Pry.editor = proc { |file, line| "emacsclient #{file} +#{line}" }
# @return [String, #call]
attr_accessor :editor
# @return [Boolean] Toggle Pry color on and off.
attr_accessor :color
# @return [Boolean] Toggle paging on and off.
attr_accessor :pager
# Determines whether the rc file (~/.pryrc) should be loaded.
# @return [Boolean]
attr_accessor :should_load_rc
# Determines whether plugins should be loaded.
# @return [Boolean]
attr_accessor :should_load_plugins
# Config option for history.
# sub-options include hist.file, hist.load, and hist.save
# hist.file is the file to save/load history too, e.g
# Pry.config.history.file = "~/.pry_history".
# hist.load is a boolean that determines whether history will be
# loaded from hist.file at session start.
# hist.save is a boolean that determines whether history will be
# saved to hist.file at session end.
# @return [OpenStruct]
attr_accessor :history
end
end

View file

@ -0,0 +1,37 @@
class Pry
module DefaultCommands
Basic = Pry::CommandSet.new do
command "toggle-color", "Toggle syntax highlighting." do
Pry.color = !Pry.color
output.puts "Syntax highlighting #{Pry.color ? "on" : "off"}"
end
command "simple-prompt", "Toggle the simple prompt." do
case Pry.active_instance.prompt
when Pry::SIMPLE_PROMPT
Pry.active_instance.pop_prompt
else
Pry.active_instance.push_prompt Pry::SIMPLE_PROMPT
end
end
command "version", "Show Pry version." do
output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}."
end
command "import", "Import a command set" do |command_set_name|
next output.puts "Provide a command set name" if command_set.nil?
set = target.eval(arg_string)
Pry.active_instance.commands.import set
end
command "reset", "Reset the REPL to a clean state." do
output.puts "Pry reset."
exec "pry"
end
end
end
end

View file

@ -0,0 +1,127 @@
require "pry/default_commands/ls"
class Pry
module DefaultCommands
Context = Pry::CommandSet.new do
import Ls
command "cd", "Start a Pry session on VAR (use `cd ..` to go back and `cd /` to return to Pry top-level)", :keep_retval => true do |obj|
case obj
when nil
output.puts "Must provide an object."
next
when ".."
throw(:breakout, opts[:nesting].level)
when "/"
throw(:breakout, 1) if opts[:nesting].level > 0
next
when "::"
TOPLEVEL_BINDING.pry
next
else
Pry.start target.eval(arg_string)
end
end
command "nesting", "Show nesting information." do
nesting = opts[:nesting]
output.puts "Nesting status:"
output.puts "--"
nesting.each do |level, obj|
if level == 0
output.puts "#{level}. #{Pry.view_clip(obj)} (Pry top level)"
else
output.puts "#{level}. #{Pry.view_clip(obj)}"
end
end
end
command "jump-to", "Jump to a Pry session further up the stack, exiting all sessions below." do |break_level|
break_level = break_level.to_i
nesting = opts[:nesting]
case break_level
when 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
output.puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}."
end
end
command "exit", "End the current Pry session. Accepts optional return value. Aliases: quit, back" do
throw(:breakout, [opts[:nesting].level, target.eval(arg_string)])
end
alias_command "quit", "exit", ""
alias_command "back", "exit", ""
command "exit-all", "End all nested Pry sessions. Accepts optional return value. Aliases: !!@" do
throw(:breakout, [0, target.eval(arg_string)])
end
alias_command "!!@", "exit-all", ""
command "exit-program", "End the current program. Aliases: quit-program, !!!" do
exit
end
alias_command "quit-program", "exit-program", ""
alias_command "!!!", "exit-program", ""
command "!pry", "Start a Pry session on current self; this even works mid-expression." do
target.pry
end
command "whereami", "Show the code context for the session. (whereami <n> shows <n> extra lines of code around the invocation line. Default: 5)" do |num|
file = target.eval('__FILE__')
line_num = target.eval('__LINE__')
klass = target.eval('self.class')
if num
i_num = num.to_i
else
i_num = 5
end
meth_name = meth_name_from_binding(target)
meth_name = "N/A" if !meth_name
if file =~ /(\(.*\))|<.*>/ || file == "" || file == "-e"
output.puts "Cannot find local context. Did you use `binding.pry` ?"
next
end
set_file_and_dir_locals(file)
output.puts "\n#{text.bold('From:')} #{file} @ line #{line_num} in #{klass}##{meth_name}:\n\n"
# This method inspired by http://rubygems.org/gems/ir_b
File.open(file).each_with_index do |line, index|
line_n = index + 1
next unless line_n > (line_num - i_num - 1)
break if line_n > (line_num + i_num)
if line_n == line_num
code =" =>#{line_n.to_s.rjust(3)}: #{line.chomp}"
if Pry.color
code = CodeRay.scan(code, :ruby).term
end
output.puts code
code
else
code = "#{line_n.to_s.rjust(6)}: #{line.chomp}"
if Pry.color
code = CodeRay.scan(code, :ruby).term
end
output.puts code
code
end
end
end
end
end
end

View file

@ -0,0 +1,146 @@
class Pry
module DefaultCommands
Documentation = Pry::CommandSet.new do
command "ri", "View ri documentation. e.g `ri Array#each`" do |*args|
run ".ri", *args
end
command "show-doc", "Show the comments above METH. Type `show-doc --help` for more info. Aliases: \?" do |*args|
target = target()
opts = Slop.parse!(args) do |opt|
opt.banner = "Usage: show-doc [OPTIONS] [METH]\n" \
"Show the comments above method METH. Tries instance methods first and then methods by default.\n" \
"e.g show-doc hello_method"
opt.on :M, "instance-methods", "Operate on instance methods."
opt.on :m, :methods, "Operate on methods."
opt.on :c, :context, "Select object context to run under.", true do |context|
target = Pry.binding_for(target.eval(context))
end
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
opt.on :h, :help, "This message." do
output.puts opt
end
end
next if opts.help?
meth_name = args.shift
if (meth = get_method_object(meth_name, target, opts.to_hash(true))).nil?
output.puts "Invalid method name: #{meth_name}. Type `show-doc --help` for help"
next
end
doc, code_type = doc_and_code_type_for(meth)
next if !doc
next output.puts("No documentation found.") if doc.empty?
doc = process_comment_markup(doc, code_type)
output.puts make_header(meth, code_type, doc)
render_output(opts.flood?, false, doc)
doc
end
alias_command "?", "show-doc", ""
command "stat", "View method information and set _file_ and _dir_ locals. Type `stat --help` for more info." do |*args|
target = target()
opts = Slop.parse!(args) do |opt|
opt.banner "Usage: stat [OPTIONS] [METH]\n" \
"Show method information for method METH and set _file_ and _dir_ locals." \
"e.g: stat hello_method"
opt.on :M, "instance-methods", "Operate on instance methods."
opt.on :m, :methods, "Operate on methods."
opt.on :c, :context, "Select object context to run under.", true do |context|
target = Pry.binding_for(target.eval(context))
end
opt.on :h, :help, "This message" do
output.puts opt
end
end
next if opts.help?
meth_name = args.shift
if (meth = get_method_object(meth_name, target, opts.to_hash(true))).nil?
output.puts "Invalid method name: #{meth_name}. Type `stat --help` for help"
next
end
code, code_type = code_and_code_type_for(meth)
next if !code
doc, code_type = doc_and_code_type_for(meth)
output.puts make_header(meth, code_type, code)
output.puts text.bold("Method Name: ") + meth_name
output.puts text.bold("Method Owner: ") + (meth.owner.to_s ? meth.owner.to_s : "Unknown")
output.puts text.bold("Method Language: ") + code_type.to_s.capitalize
output.puts text.bold("Method Type: ") + (meth.is_a?(Method) ? "Bound" : "Unbound")
output.puts text.bold("Method Arity: ") + meth.arity.to_s
name_map = { :req => "Required:", :opt => "Optional:", :rest => "Rest:" }
if meth.respond_to?(:parameters)
output.puts text.bold("Method Parameters: ") + meth.parameters.group_by(&:first).
map { |k, v| "#{name_map[k]} #{v.map { |kk, vv| vv ? vv.to_s : "noname" }.join(", ")}" }.join(". ")
end
output.puts text.bold("Comment length: ") + (doc.empty? ? 'No comment.' : (doc.lines.count.to_s + ' lines.'))
end
command "gist-method", "Gist a method to github. Type `gist-method --help` for more info.", :requires_gem => "gist" do |*args|
require 'gist'
target = target()
opts = Slop.parse!(args) do |opt|
opt.banner "Usage: gist-method [OPTIONS] [METH]\n" \
"Gist the method (doc or source) to github.\n" \
"Ensure the `gist` gem is properly working before use. http://github.com/defunkt/gist for instructions.\n" \
"e.g: gist -m my_method\n" \
"e.g: gist -d my_method\n"
opt.on :m, :method, "Gist a method's source."
opt.on :d, :doc, "Gist a method's documentation."
opt.on :p, :private, "Create a private gist (default: true)", :default => true
opt.on :h, :help, "This message" do
output.puts opt
end
end
next if opts.help?
# This needs to be extracted into its own method as it's shared
# by show-method and show-doc and stat commands
meth_name = args.shift
if (meth = get_method_object(meth_name, target, opts.to_hash(true))).nil?
output.puts "Invalid method name: #{meth_name}. Type `gist-method --help` for help"
next
end
type_map = { :ruby => "rb", :c => "c", :plain => "plain" }
if !opts.doc?
content, code_type = code_and_code_type_for(meth)
else
content, code_type = doc_and_code_type_for(meth)
text.no_color do
content = process_comment_markup(content, code_type)
end
code_type = :plain
end
link = Gist.write([:extension => ".#{type_map[code_type]}",
:input => content],
opts.p?)
output.puts "Gist created at #{link}"
end
end
end
end

View file

@ -0,0 +1,52 @@
class Pry
module DefaultCommands
EasterEggs = Pry::CommandSet.new do
command "east-coker", "" do
text = %{
--
Now the light falls
Across the open field, leaving the deep lane
Shuttered with branches, dark in the afternoon,
Where you lean against a bank while a van passes,
And the deep lane insists on the direction
Into the village, in the electric heat
Hypnotised. In a warm haze the sultry light
Is absorbed, not refracted, by grey stone.
The dahlias sleep in the empty silence.
Wait for the early owl.
-- T.S Eliot
}
output.puts text
text
end
command "cohen-poem", "" do
text = %{
--
When this American woman,
whose thighs are bound in casual red cloth,
comes thundering past my sitting place
like a forest-burning Mongol tribe,
the city is ravished
and brittle buildings of a hundred years
splash into the street;
and my eyes are burnt
for the embroidered Chinese girls,
already old,
and so small between the thin pines
on these enormous landscapes,
that if you turn your head
they are lost for hours.
-- Leonard Cohen
}
output.puts text
text
end
end
end
end

View file

@ -0,0 +1,46 @@
class Pry
module DefaultCommands
Gems = Pry::CommandSet.new do
command "gem-install", "Install a gem and refresh the gem cache.", :argument_required => true do |gem|
begin
destination = File.writable?(Gem.dir) ? Gem.dir : Gem.user_dir
installer = Gem::DependencyInstaller.new :install_dir => destination
installer.install gem
rescue Errno::EACCES
output.puts "Insufficient permissions to install `#{text.green gem}`"
rescue Gem::GemNotFoundException
output.puts "Gem `#{text.green gem}` not found."
else
Gem.refresh
output.puts "Gem `#{text.green gem}` installed."
end
end
command "gem-cd", "Change working directory to specified gem's directory.", :argument_required => true do |gem|
spec = Gem.source_index.find_name(gem).sort { |a,b| Gem::Version.new(b.version) <=> Gem::Version.new(a.version) }.first
spec ? Dir.chdir(spec.full_gem_path) : output.puts("Gem `#{gem}` not found.")
end
command "gem-list", "List/search installed gems. (Optional parameter: a regexp to limit the search)" do |pattern|
pattern = Regexp.new pattern.to_s, Regexp::IGNORECASE
gems = Gem.source_index.find_name(pattern).group_by(&:name)
gems.each do |gem, specs|
specs.sort! do |a,b|
Gem::Version.new(b.version) <=> Gem::Version.new(a.version)
end
versions = specs.map.with_index do |spec, index|
index == 0 ? text.bright_green(spec.version.to_s) : text.green(spec.version.to_s)
end
output.puts "#{text.white gem} (#{versions.join ', '})"
end
end
end
end
end

View file

@ -0,0 +1,114 @@
class Pry
module DefaultCommands
Input = Pry::CommandSet.new do
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!"
eval_string.replace("")
end
command "show-input", "Show the current eval_string" do
render_output(false, 0, Pry.color ? CodeRay.scan(eval_string, :ruby).term : eval_string)
end
command /amend-line-?(\d+)?/, "Experimental amend-line, where the N in amend-line-N represents line to replace. Aliases: %N",
:interpolate => false, :listing => "amend-line-N" do |line_number, replacement_line|
replacement_line = "" if !replacement_line
input_array = eval_string.each_line.to_a
line_num = line_number ? line_number.to_i : input_array.size - 1
input_array[line_num] = arg_string + "\n"
eval_string.replace input_array.join
end
alias_command /%(\d+)?/, /amend-line-?(\d+)?/, ""
command "hist", "Show and replay Readline history. Type `hist --help` for more info." do |*args|
Slop.parse(args) do |opt|
history = Readline::HISTORY.to_a
opt.banner "Usage: hist [--replay START..END] [--clear] [--grep PATTERN] [--head N] [--tail N] [--help]\n"
opt.on :g, :grep, 'A pattern to match against the history.', true do |pattern|
pattern = Regexp.new arg_string.split(/ /)[1]
history.pop
history.map!.with_index do |element, index|
if element =~ pattern
"#{text.blue index}: #{element}"
end
end
stagger_output history.compact.join "\n"
end
opt.on :head, 'Display the first N items of history', :optional => true, :as => Integer do |limit|
unless opt.grep?
limit ||= 10
list = history.first limit
lines = text.with_line_numbers list.join("\n"), 0
stagger_output lines
end
end
opt.on :t, :tail, 'Display the last N items of history', :optional => true, :as => Integer do |limit|
unless opt.grep?
limit ||= 10
offset = history.size-limit
offset = offset < 0 ? 0 : offset
list = history.last limit
lines = text.with_line_numbers list.join("\n"), offset
stagger_output lines
end
end
opt.on :s, :show, 'Show the history corresponding to the history line (or range of lines).', true, :as => Range do |range|
unless opt.grep?
start_line = range.is_a?(Range) ? range.first : range
lines = text.with_line_numbers Array(history[range]).join("\n"), start_line
stagger_output lines
end
end
opt.on :e, :exclude, 'Exclude pry commands from the history.' do
unless opt.grep?
history.map!.with_index do |element, index|
unless command_processor.valid_command? element
"#{text.blue index}: #{element}"
end
end
stagger_output history.compact.join "\n"
end
end
opt.on :r, :replay, 'The line (or range of lines) to replay.', true, :as => Range do |range|
unless opt.grep?
actions = Array(history[range]).join("\n") + "\n"
Pry.active_instance.input = StringIO.new(actions)
end
end
opt.on :c, :clear, 'Clear the history' do
unless opt.grep?
Readline::HISTORY.shift until Readline::HISTORY.empty?
output.puts 'History cleared.'
end
end
opt.on :h, :help, 'Show this message.', :tail => true do
unless opt.grep?
output.puts opt.help
end
end
opt.on_empty do
lines = text.with_line_numbers history.join("\n"), 0
stagger_output lines
end
end
end
end
end
end

View file

@ -0,0 +1,180 @@
class Pry
module DefaultCommands
Introspection = Pry::CommandSet.new do
command "show-method", "Show the source for METH. Type `show-method --help` for more info. Aliases: $, show-source" do |*args|
target = target()
opts = Slop.parse!(args) do |opt|
opt.banner "Usage: show-method [OPTIONS] [METH]\n" \
"Show the source for method METH. Tries instance methods first and then methods by default.\n" \
"e.g: show-method hello_method"
opt.on :l, "line-numbers", "Show line numbers."
opt.on :M, "instance-methods", "Operate on instance methods."
opt.on :m, :methods, "Operate on methods."
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
opt.on :c, :context, "Select object context to run under.", true do |context|
target = Pry.binding_for(target.eval(context))
end
opt.on :h, :help, "This message." do
output.puts opt
end
end
next if opts.help?
meth_name = args.shift
if (meth = get_method_object(meth_name, target, opts.to_hash(true))).nil?
output.puts "Invalid method name: #{meth_name}. Type `show-method --help` for help"
next
end
code, code_type = code_and_code_type_for(meth)
next if !code
output.puts make_header(meth, code_type, code)
if Pry.color
code = CodeRay.scan(code, code_type).term
end
start_line = false
if opts.l?
start_line = meth.source_location ? meth.source_location.last : 1
end
render_output(opts.flood?, start_line, code)
code
end
alias_command "show-source", "show-method", ""
alias_command "$", "show-method", ""
command "show-command", "Show the source for CMD. Type `show-command --help` for more info." do |*args|
target = target()
opts = Slop.parse!(args) do |opt|
opt.banner = "Usage: show-command [OPTIONS] [CMD]\n" \
"Show the source for command CMD.\n" \
"e.g: show-command show-method"
opt.on :l, "line-numbers", "Show line numbers."
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
opt.on :h, :help, "This message." do
output.puts opt
end
end
next if opts.help?
command_name = args.shift
if !command_name
output.puts "You must provide a command name."
next
end
if find_command(command_name)
block = find_command(command_name).block
code, _ = code_and_code_type_for(block)
next if !code
output.puts make_header(block, :ruby, code)
if Pry.color
code = CodeRay.scan(code, :ruby).term
end
start_line = false
if opts.l?
start_line = block.source_location ? block.source_location.last : 1
end
render_output(opts.flood?, opts.l? ? block.source_location.last : false, code)
code
else
output.puts "No such command: #{command_name}."
end
end
command "edit-method", "Edit a method. Type `edit-method --help` for more info." do |*args|
target = target()
opts = Slop.parse!(args) do |opt|
opt.banner "Usage: edit-method [OPTIONS] [METH]\n" \
"Edit the method METH in an editor.\n" \
"Ensure #{text.bold("Pry.editor")} is set to your editor of choice.\n" \
"e.g: edit-method hello_method"
opt.on :M, "instance-methods", "Operate on instance methods."
opt.on :m, :methods, "Operate on methods."
opt.on "no-reload", "Do not automatically reload the method's file after editting."
opt.on :n, "no-jump", "Do not fast forward editor to first line of method."
opt.on :c, :context, "Select object context to run under.", true do |context|
target = Pry.binding_for(target.eval(context))
end
opt.on :h, :help, "This message." do
output.puts opt
end
end
next if opts.help?
meth_name = args.shift
if (meth = get_method_object(meth_name, target, opts.to_hash(true))).nil?
output.puts "Invalid method name: #{meth_name}."
next
end
next output.puts "Error: No editor set!\nEnsure that #{text.bold("Pry.editor")} is set to your editor of choice." if !Pry.editor
if is_a_c_method?(meth)
output.puts "Error: Can't edit a C method."
elsif is_a_dynamically_defined_method?(meth)
output.puts "Error: Can't edit an eval method."
# editor is invoked here
else
file, line = meth.source_location
set_file_and_dir_locals(file)
if Pry.editor.respond_to?(:call)
editor_invocation = Pry.editor.call(file, line)
else
# only use start line if -n option is not used
start_line_syntax = opts.n? ? "" : start_line_for_editor(line)
editor_invocation = "#{Pry.editor} #{start_line_syntax} #{file}"
end
run ".#{editor_invocation}"
silence_warnings do
load file if !opts["no-reload"]
end
end
end
helpers do
def start_line_for_editor(line_number)
case Pry.editor
when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/
"+#{line_number}"
when /^mate/, /^geany/
"-l #{line_number}"
else
if RUBY_PLATFORM =~ /mswin|mingw/
""
else
"+#{line_number}"
end
end
end
end
end
end
end

View file

@ -0,0 +1,199 @@
class Pry
module DefaultCommands
Ls = Pry::CommandSet.new do
command "ls", "Show the list of vars and methods 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()
OptionParser.new do |opts|
opts.banner = %{Usage: ls [OPTIONS] [VAR]\n\
List information about VAR (the current context by default).
Shows local and instance variables by default.
--
}
opts.on("-g", "--globals", "Display global variables.") do
options[:g] = true
end
opts.on("-c", "--constants", "Display constants.") do
options[:c] = true
end
opts.on("-l", "--locals", "Display locals.") do
options[:l] = true
end
opts.on("-i", "--ivars", "Display instance variables.") do
options[:i] = true
end
opts.on("-k", "--class-vars", "Display class variables.") do
options[:k] = true
end
opts.on("-m", "--methods", "Display methods (public methods by default).") do
options[:m] = true
end
opts.on("-M", "--instance-methods", "Display instance methods (only relevant to classes and modules).") do
options[:M] = true
end
opts.on("-P", "--public", "Display public methods (with -m).") do
options[:P] = true
end
opts.on("-r", "--protected", "Display protected methods (with -m).") do
options[:r] = true
end
opts.on("-p", "--private", "Display private methods (with -m).") do
options[:p] = true
end
opts.on("-j", "--just-singletons", "Display just the singleton methods (with -m).") do
options[:j] = true
end
opts.on("-s", "--super", "Include superclass entries (relevant to constant and methods options).") do
options[:s] = true
end
opts.on("-a", "--all", "Display all types of entries.") do
options[:a] = true
end
opts.on("-v", "--verbose", "Verbose ouput.") do
options[:v] = true
end
opts.on("-f", "--flood", "Do not use a pager to view text longer than one screen.") do
options[:f] = true
end
opts.on("--grep REG", "Regular expression to be used.") do |reg|
options[:grep] = Regexp.new(reg)
end
opts.on_tail("-h", "--help", "Show this message.") do
output.puts opts
options[:h] = true
end
end.order(args) do |new_target|
target = Pry.binding_for(target.eval("#{new_target}")) if !options[:h]
end
# exit if we've displayed help
next if options[:h]
# default is locals/ivars/class vars.
# Only occurs when no options or when only option is verbose
options.merge!({
:l => true,
:i => true,
:k => true
}) if options.empty? || (options.size == 1 && options[:v]) || (options.size == 1 && options[:grep])
options[:grep] = // if !options[:grep]
# Display public methods by default if -m or -M switch is used.
options[:P] = true if (options[:m] || options[:M]) && !(options[:p] || options[:r] || options[:j])
info = {}
target_self = target.eval('self')
# ensure we have a real boolean and not a `nil` (important when
# interpolating in the string)
options[:s] = !!options[:s]
# Numbers (e.g 0, 1, 2) are for ordering the hash values in Ruby 1.8
i = -1
# Start collecting the entries selected by the user
info["local variables"] = [Array(target.eval("local_variables")).sort, i += 1] if options[:l] || options[:a]
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]
info["global variables"] = [Array(target.eval("global_variables")).sort, i += 1] if options[:g] || options[:a]
info["public methods"] = [Array(target.eval("public_methods(#{options[:s]})")).uniq.sort, i += 1] if (options[:m] && options[:P]) || options[:a]
info["protected methods"] = [Array(target.eval("protected_methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:r]) || options[:a]
info["private methods"] = [Array(target.eval("private_methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:p]) || options[:a]
info["just singleton methods"] = [Array(target.eval("methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:j]) || options[:a]
info["public instance methods"] = [Array(target.eval("public_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:P]) || options[:a])
info["protected instance methods"] = [Array(target.eval("protected_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:r]) || options[:a])
info["private instance methods"] = [Array(target.eval("private_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:p]) || options[:a])
# dealing with 1.8/1.9 compatibility issues :/
csuper = options[:s]
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]
text = ""
# verbose output?
if options[:v]
# verbose
info.sort_by { |k, v| v.last }.each do |k, v|
if !v.first.empty?
text << "#{k}:\n--\n"
filtered_list = v.first.grep options[:grep]
if Pry.color
text << CodeRay.scan(Pry.view(filtered_list), :ruby).term + "\n"
else
text << Pry.view(filtered_list) + "\n"
end
text << "\n\n"
end
end
if !options[:f]
stagger_output(text)
else
output.puts text
end
# plain
else
list = info.values.sort_by(&:last).map(&:first).inject(&:+)
list = list.grep(options[:grep]) if list
list.uniq! if list
if Pry.color
text << CodeRay.scan(Pry.view(list), :ruby).term + "\n"
else
text << Pry.view(list) + "\n"
end
if !options[:f]
stagger_output(text)
else
output.puts text
end
list
end
end
end
end
end

View file

@ -0,0 +1,81 @@
class Pry
module DefaultCommands
Shell = Pry::CommandSet.new do
command /\.(.*)/, "All text following a '.' is forwarded to the shell.", :listing => ".<shell command>" do |cmd|
if cmd =~ /^cd\s+(.+)/i
begin
Dir.chdir File.expand_path($1)
rescue Errno::ENOENT
output.puts "No such directory: #{dest}"
end
else
if !system(cmd)
output.puts "Error: there was a problem executing system command: #{cmd}"
end
end
end
command "shell-mode", "Toggle shell mode. Bring in pwd prompt and file completion." do
case Pry.active_instance.prompt
when Pry::SHELL_PROMPT
Pry.active_instance.pop_prompt
Pry.active_instance.custom_completions = Pry::DEFAULT_CUSTOM_COMPLETIONS
else
Pry.active_instance.push_prompt Pry::SHELL_PROMPT
Pry.active_instance.custom_completions = Pry::FILE_COMPLETIONS
Readline.completion_proc = Pry::InputCompleter.build_completion_proc target,
Pry.active_instance.instance_eval(&Pry::FILE_COMPLETIONS)
end
end
alias_command "file-mode", "shell-mode", ""
command "cat", "Show output of file FILE. Type `cat --help` for more information." do |*args|
start_line = 0
end_line = -1
opts = Slop.parse!(args) do |opt|
opt.on :s, :start, "Start line (defaults to start of file)Line 1 is the first line.", true, :as => Integer do |line|
start_line = line - 1
end
opt.on :e, :end, "End line (defaults to end of file). Line -1 is the last line", true, :as => Integer do |line|
end_line = line - 1
end
opt.on :l, "line-numbers", "Show line numbers."
opt.on :t, :type, "The specific file type for syntax higlighting (e.g ruby, python)", true, :as => Symbol
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
opt.on :h, :help, "This message." do
output.puts opt
end
end
next if opts.help?
file_name = args.shift
if !file_name
output.puts "Must provide a file name."
next
end
contents, normalized_start_line, _ = read_between_the_lines(file_name, start_line, end_line)
if Pry.color
contents = syntax_highlight_by_file_type_or_specified(contents, file_name, opts[:type])
end
set_file_and_dir_locals(file_name)
render_output(opts.flood?, opts.l? ? normalized_start_line + 1 : false, contents)
contents
end
end
end
end

View file

@ -0,0 +1,48 @@
class Pry
module ExtendedCommands
Experimental = Pry::CommandSet.new do
command "reload-method", "Reload the source specifically for a method", :requires_gem => "method_reload" do |meth_name|
if (meth = get_method_object(meth_name, target, {})).nil?
output.puts "Invalid method name: #{meth_name}."
next
end
meth.reload
end
command "play", "Play a string as input" do |*args|
Slop.parse!(args) do |opt|
opt.banner "Usage: play-method [--replay START..END] [--clear] [--grep PATTERN] [--help]\n"
opt.on :l, :lines, 'The line (or range of lines) to replay.', true, :as => Range
opt.on :m, :method, 'Play a method.', true do |meth_name|
if (meth = get_method_object(meth_name, target, {})).nil?
output.puts "Invalid method name: #{meth_name}."
next
end
code, code_type = code_and_code_type_for(meth)
next if !code
range = opt.l? ? opt[:l] : (0..-1)
Pry.active_instance.input = StringIO.new(code[range])
end
opt.on :f, "file", 'The line (or range of lines) to replay.', true do |file_name|
text = File.read File.expand_path(file_name)
range = opt.l? ? opt[:l] : (0..-1)
Pry.active_instance.input = StringIO.new(text[range])
end
opt.on :h, :help, "This message." do
output.puts opt
end
end
end
end
end
end

View file

@ -0,0 +1,22 @@
class Pry
module ExtendedCommands
UserCommandAPI = Pry::CommandSet.new do
command "define-command", "To honor Mon-Ouie" do |arg|
next output.puts("Provide an arg!") if arg.nil?
prime_string = "command #{arg_string}\n"
command_string = Pry.active_instance.r(target, prime_string)
eval_string.replace <<-HERE
_pry_.commands.instance_eval do
#{command_string}
end
HERE
end
end
end
end

View file

@ -1,2 +1,3 @@
require "pry/helpers/base_helpers"
require "pry/helpers/command_helpers"
require "pry/helpers/text"

View file

@ -2,9 +2,26 @@ class Pry
module Helpers
module BaseHelpers
module_function
module_function
def gem_installed?(gem_name)
def silence_warnings
old_verbose = $VERBOSE
$VERBOSE = nil
begin
yield
ensure
$VERBOSE = old_verbose
end
end
def find_command(name)
command_match = commands.find { |_, command| command.options[:listing] == name }
return command_match.last if command_match
nil
end
def gem_installed?(gem_name)
require 'rubygems'
Gem::Specification.respond_to?(:find_all_by_name) ? !Gem::Specification.find_all_by_name(gem_name).empty? : Gem.source_index.find_name(gem_name).first
end
@ -16,12 +33,22 @@ class Pry
end
end
def set_file_and_dir_locals(file_name)
return if !target
$_file_temp = File.expand_path(file_name)
$_dir_temp = File.dirname($_file_temp)
target.eval("_file_ = $_file_temp")
target.eval("_dir_ = $_dir_temp")
end
def stub_proc(name, options)
gems_needed = Array(options[:requires_gem])
gems_not_installed = gems_needed.select { |g| !gem_installed?(g) }
proc do
output.puts "\n#{name} requires the following gems to be installed: #{(gems_needed.join(", "))}"
output.puts "\nThe command '#{name}' requires the following gems to be installed: #{(gems_needed.join(", "))}"
output.puts "-"
output.puts "Command not available due to dependency on gems: `#{gems_not_installed.join(", ")}` not being met."
output.puts "-"
output.puts "Type `install #{name}` to install the required gems and activate this command."
end
end
@ -36,103 +63,6 @@ class Pry
end
end
#
# Color helpers:
# gray, red, green, yellow, blue, purple, cyan, white,
# and bright_red, bright_green, etc...
#
# ANSI color codes:
# \033 => escape
# 30 => color base
# 1 => bright
# 0 => normal
#
COLORS = {
"black" => 0,
"red" => 1,
"green" => 2,
"yellow" => 3,
"blue" => 4,
"purple" => 5,
"magenta" => 5,
"cyan" => 6,
"white" => 7
}
COLORS.each do |color, i|
define_method color do |str|
Pry.color ? "\033[0;#{30+i}m#{str}\033[0m" : str
end
define_method "bright_#{color}" do |str|
Pry.color ? "\033[1;#{30+i}m#{str}\033[0m" : str
end
end
alias_method :grey, :bright_black
alias_method :gray, :bright_black
require 'set'
VALID_COLORS = Set.new(
COLORS.keys +
COLORS.keys.map{|k| "bright_#{k}" } +
["grey", "gray"]
)
def bold(text)
Pry.color ? "\e[1m#{text}\e[0m" : text
end
#
# Colorize a string that has "color tags".
#
# Examples:
# puts colorize("<light_green><magenta>*</magenta> Hey mom! I am <light_blue>SO</light_blue> colored right now.</light_green>")
#
def colorize(string)
stack = []
# split the string into tags and literal strings
tokens = string.split(/(<\/?[\w\d_]+>)/)
tokens.delete_if { |token| token.size == 0 }
result = ""
tokens.each do |token|
# token is an opening tag!
if /<([\w\d_]+)>/ =~ token and VALID_COLORS.include?($1) #valid_tag?($1)
stack.push $1
# token is a closing tag!
elsif /<\/([\w\d_]+)>/ =~ token and VALID_COLORS.include?($1) # valid_tag?($1)
# if this color is on the stack somwehere...
if pos = stack.rindex($1)
# close the tag by removing it from the stack
stack.delete_at pos
else
raise "Error: tried to close an unopened color tag -- #{token}"
end
# token is a literal string!
else
color = (stack.last || "white")
#color = BBS_COLOR_TABLE[color.to_i] if color =~ /^\d+$/
result << send(color, token) # colorize the result
end
end
result
end
def highlight(string, regexp, highlight_color=:bright_yellow)
highlighted = string.gsub(regexp) { |match| "<#{highlight_color}>#{match}</#{highlight_color}>" }
end

View file

@ -23,31 +23,10 @@ class Pry
end
end
def set_file_and_dir_locals(file_name)
return if !target
$_file_temp = File.expand_path(file_name)
$_dir_temp = File.dirname($_file_temp)
target.eval("_file_ = $_file_temp")
target.eval("_dir_ = $_dir_temp")
end
def add_line_numbers(lines, start_line)
line_array = lines.each_line.to_a
line_array.each_with_index.map do |line, idx|
adjusted_index = idx + start_line
if Pry.color
cindex = CodeRay.scan("#{adjusted_index}", :ruby).term
"#{cindex}: #{line}"
else
"#{idx}: #{line}"
end
end.join
end
# if start_line is not false then add line numbers starting with start_line
def render_output(should_flood, start_line, doc)
if start_line
doc = add_line_numbers(doc, start_line)
doc = Pry::Helpers::Text.with_line_numbers doc, start_line
end
if should_flood
@ -57,52 +36,18 @@ class Pry
end
end
def editor_with_start_line(line_number)
case Pry.editor
when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/
"#{Pry.editor} +#{line_number}"
when /^mate/
"#{Pry.editor} -l#{line_number}"
else
if RUBY_PLATFORM =~ /mswin|mingw/
Pry.editor
else
"#{Pry.editor} +#{line_number}"
end
end
end
def is_a_dynamically_defined_method?(meth)
file, _ = meth.source_location
!!(file =~ /(\(.*\))|<.*>/)
end
def check_for_dynamically_defined_method(meth)
if is_a_dynamically_defined_method?(meth)
raise "Cannot retrieve source for dynamically defined method."
end
end
def check_for_dynamically_defined_method(meth)
file, _ = meth.source_location
if file =~ /(\(.*\))|<.*>/
if file =~ /(\(.*\))|<.*>/ && file != Pry.eval_path
raise "Cannot retrieve source for dynamically defined method."
end
end
def remove_first_word(text)
text.split.drop(1).join(' ')
end
# turn off color for duration of block
def no_color(&block)
old_color_state = Pry.color
Pry.color = false
yield
ensure
Pry.color = old_color_state
end
def code_and_code_type_for(meth)
case code_type = code_type_for(meth)
when nil
@ -111,7 +56,14 @@ class Pry
code = Pry::MethodInfo.info_for(meth).source
code = strip_comments_from_c_code(code)
when :ruby
code = strip_leading_whitespace(meth.source)
if meth.source_location.first == Pry.eval_path
start_line = meth.source_location.last
p = Pry.new(:input => StringIO.new(Pry.line_buffer[start_line..-1].join)).r(target)
code = strip_leading_whitespace(p)
else
code = strip_leading_whitespace(meth.source)
end
set_file_and_dir_locals(meth.source_location.first)
end
@ -134,36 +86,48 @@ class Pry
end
def get_method_object(meth_name, target, options)
if meth_name
if meth_name =~ /(\S+)\#(\S+)\Z/
context, meth_name = $1, $2
target = Pry.binding_for(target.eval(context))
options["instance-methods"] = true
options[:methods] = false
elsif meth_name =~ /(\S+)\.(\S+)\Z/
context, meth_name = $1, $2
target = Pry.binding_for(target.eval(context))
options["instance-methods"] = false
options[:methods] = true
end
else
meth_name = meth_name_from_binding(target)
end
if !meth_name
return nil
end
if options[:M]
target.eval("instance_method(:#{meth_name})")
elsif options[:m]
target.eval("method(:#{meth_name})")
if options["instance-methods"]
target.eval("instance_method(:#{meth_name})") rescue nil
elsif options[:methods]
target.eval("method(:#{meth_name})") rescue nil
else
begin
target.eval("instance_method(:#{meth_name})")
rescue
begin
target.eval("method(:#{meth_name})")
rescue
return nil
end
target.eval("method(:#{meth_name})") rescue nil
end
end
end
def make_header(meth, code_type, content)
num_lines = "Number of lines: #{bold(content.each_line.count.to_s)}"
num_lines = "Number of lines: #{Pry::Helpers::Text.bold(content.each_line.count.to_s)}"
case code_type
when :ruby
file, line = meth.source_location
"\n#{bold('From:')} #{file} @ line #{line}:\n#{num_lines}\n\n"
"\n#{Pry::Helpers::Text.bold('From:')} #{file} @ line #{line}:\n#{num_lines}\n\n"
else
file = Pry::MethodInfo.info_for(meth).file
"\n#{bold('From:')} #{file} in Ruby Core (C Method):\n#{num_lines}\n\n"
"\n#{Pry::Helpers::Text.bold('From:')} #{file} in Ruby Core (C Method):\n#{num_lines}\n\n"
end
end
@ -172,7 +136,7 @@ class Pry
end
def should_use_pry_doc?(meth)
Pry.has_pry_doc && is_a_c_method?(meth)
Pry.config.has_pry_doc && is_a_c_method?(meth)
end
def code_type_for(meth)
@ -243,16 +207,11 @@ class Pry
normalized_line_number(end_line, lines_array.size)]
end
# documentation related helpers
def strip_color_codes(str)
str.gsub(/\e\[.*?(\d)+m/, '')
end
def process_rdoc(comment, code_type)
comment = comment.dup
comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { Pry.color ? "\e[34m#{$1}\e[0m" : $1 }.
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { Pry.color ? "\e[1m#{$1}\e[0m": $1 }.
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { Pry.color ? "\e[1m#{$1}\e[0m" : $1 }.
gsub(/\B\+(\w*?)\+\B/) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
gsub(/`(?:\s*\n)?(.*?)\s*`/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }
@ -262,7 +221,7 @@ class Pry
in_tag_block = nil
output = comment.lines.map do |v|
if in_tag_block && v !~ /^\S/
strip_color_codes(strip_color_codes(v))
Pry::Helpers::Text.strip_color Pry::Helpers::Text.strip_color(v)
elsif in_tag_block
in_tag_block = false
v
@ -299,7 +258,7 @@ class Pry
end
def strip_comments_from_c_code(code)
code.sub /\A\s*\/\*.*?\*\/\s*/m, ''
code.sub(/\A\s*\/\*.*?\*\/\s*/m, '')
end
def prompt(message, options="Yn")

83
lib/pry/helpers/text.rb Normal file
View file

@ -0,0 +1,83 @@
class Pry
module Helpers
# The methods defined on {Text} are available to custom commands via {Pry::CommandContext#text}.
module Text
COLORS =
{
"black" => 0,
"red" => 1,
"green" => 2,
"yellow" => 3,
"blue" => 4,
"purple" => 5,
"magenta" => 5,
"cyan" => 6,
"white" => 7
}
class << self
COLORS.each_pair do |color, value|
define_method color do |text|
Pry.color ? "\033[0;#{30+value}m#{text}\033[0m" : text.to_s
end
define_method "bright_#{color}" do |text|
Pry.color ? "\033[1;#{30+value}m#{text}\033[0m" : text.to_s
end
end
alias_method :grey, :bright_black
alias_method :gray, :bright_black
# Remove any color codes from _text_.
#
# @param [String, #to_s] text
# @return [String] _text_ stripped of any color codes.
def strip_color text
text.to_s.gsub(/\e\[.*?(\d)+m/ , '')
end
# Returns _text_ as bold text for use on a terminal.
# _Pry.color_ must be true for this method to perform any transformations.
#
# @param [String, #to_s] text
# @return [String] _text_
def bold text
Pry.color ? "\e[1m#{text}\e[0m" : text.to_s
end
# Executes _block_ with _Pry.color_ set to false.
#
# @param [Proc]
# @return [void]
def no_color &block
boolean = Pry.color
Pry.color = false
yield
ensure
Pry.color = boolean
end
# Returns _text_ in a numbered list, beginning at _offset_.
#
# @param [#each_line] text
# @param [Fixnum] offset
# @return [String]
def with_line_numbers text, offset
lines = text.each_line.to_a
lines.each_with_index.map do |line, index|
adjusted_index = index + offset
"#{self.blue adjusted_index}: #{line}"
end.join
end
end
end
end
end

View file

@ -1,17 +0,0 @@
class Pry
# The default hooks - display messages when beginning and ending Pry sessions.
DEFAULT_HOOKS = {
:before_session => proc do |out, target|
# ensure we're actually in a method
meth_name = target.eval('__method__')
file = target.eval('__FILE__')
# /unknown/ for rbx
if file !~ /(\(.*\))|<.*>/ && file !~ /__unknown__/ && file != "" && file != "-e"
Pry.run_command "whereami 5", :output => out, :show_output => true, :context => target, :commands => Pry::Commands
end
end,
}
end

79
lib/pry/plugins.rb Normal file
View file

@ -0,0 +1,79 @@
class Pry
class PluginManager
PRY_PLUGIN_PREFIX = /^pry-/
PluginNotFound = Class.new(LoadError)
class Plugin
attr_accessor :name, :gem_name, :enabled, :active
def initialize(name, gem_name, enabled)
@name, @gem_name, @enabled = name, gem_name, enabled
end
# Disable a plugin.
def disable!
self.enabled = false
end
# Enable a plugin.
def enable!
self.enabled = true
end
# Activate the plugin (require the gem).
def activate!
begin
require gem_name
rescue LoadError
raise PluginNotFound, "The plugin '#{gem_name}' was not found!"
end
self.active = true
self.enabled = true
end
alias active? active
alias enabled? enabled
end
def initialize
@plugins = []
end
# Find all installed Pry plugins and store them in an internal array.
def locate_plugins
Gem.refresh
Gem.source_index.find_name('').each do |gem|
next if gem.name !~ PRY_PLUGIN_PREFIX
plugin_name = gem.name.split('-', 2).last
@plugins << Plugin.new(plugin_name, gem.name, true) if !gem_located?(gem.name)
end
@plugins
end
# @return [Hash] A hash with all plugin names (minus the 'pry-') as
# keys and Plugin objects as values.
def plugins
h = {}
@plugins.each do |plugin|
h[plugin.name] = plugin
end
h
end
# Require all enabled plugins, disabled plugins are skipped.
def load_plugins
@plugins.each do |plugin|
plugin.activate! if plugin.enabled?
end
end
private
def gem_located?(gem_name)
@plugins.any? { |plugin| plugin.gem_name == gem_name }
end
end
end

View file

@ -1,16 +0,0 @@
class Pry
DEFAULT_PRINT = proc do |output, value|
if Pry.color
output.puts "=> #{CodeRay.scan(Pry.view(value), :ruby).term}"
else
output.puts "=> #{Pry.view(value)}"
end
end
# Will only show the first line of the backtrace
DEFAULT_EXCEPTION_HANDLER = proc do |output, exception|
output.puts "#{exception.class}: #{exception.message}"
output.puts "from #{exception.backtrace.first}"
end
end

View file

@ -1,31 +0,0 @@
class Pry
# The default prompt; includes the target and nesting level
DEFAULT_PROMPT = [
proc do |target_self, nest_level|
if nest_level == 0
"pry(#{Pry.view_clip(target_self)})> "
else
"pry(#{Pry.view_clip(target_self)}):#{Pry.view_clip(nest_level)}> "
end
end,
proc do |target_self, nest_level|
if nest_level == 0
"pry(#{Pry.view_clip(target_self)})* "
else
"pry(#{Pry.view_clip(target_self)}):#{Pry.view_clip(nest_level)}* "
end
end
]
# A simple prompt - doesn't display target or nesting level
SIMPLE_PROMPT = [proc { ">> " }, proc { ">* " }]
SHELL_PROMPT = [
proc { |target_self, _| "pry #{Pry.view_clip(target_self)}:#{Dir.pwd} $ " },
proc { |target_self, _| "pry #{Pry.view_clip(target_self)}:#{Dir.pwd} * " }
]
end

View file

@ -1,3 +1,7 @@
require 'ostruct'
require 'forwardable'
require 'pry/config'
# @author John Mair (banisterfiend)
class Pry
@ -6,6 +10,13 @@ class Pry
# class accessors
class << self
extend Forwardable
# convenience method
def self.delegate_accessors(delagatee, *names)
def_delegators delagatee, *names
def_delegators delagatee, *names.map { |v| "#{v}=" }
end
# Get nesting data.
# This method should not need to be accessed directly.
@ -27,41 +38,6 @@ class Pry
# @return [Pry] The active Pry instance.
attr_accessor :active_instance
# Get/Set the object to use for input by default by all Pry instances.
# @return [#readline] The object to use for input by default by all
# Pry instances.
attr_accessor :input
# Get/Set the object to use for output by default by all Pry instances.
# @return [#puts] The object to use for output by default by all
# Pry instances.
attr_accessor :output
# Get/Set the object to use for commands by default by all Pry instances.
# @return [Pry::CommandBase] The object to use for commands by default by all
# Pry instances.
attr_accessor :commands
# Get/Set the Proc to use for printing by default by all Pry
# instances.
# This is the 'print' component of the REPL.
# @return [Proc] The Proc to use for printing by default by all
# Pry instances.
attr_accessor :print
# @return [Proc] The Proc to use for printing exceptions by default by all
# Pry instances.
attr_accessor :exception_handler
# Get/Set the Hash that defines Pry hooks used by default by all Pry
# instances.
# @return [Hash] The hooks used by default by all Pry instances.
# @example
# Pry.hooks :before_session => proc { puts "hello" },
# :after_session => proc { puts "goodbye" }
attr_accessor :hooks
# Get/Set the Proc that defines extra Readline completions (on top
# of the ones defined for IRB).
# @return [Proc] The Proc that defines extra Readline completions (on top
@ -69,40 +45,31 @@ class Pry
# Pry.custom_completions = proc { Dir.entries('.') }
attr_accessor :custom_completions
# Get the array of Procs to be used for the prompts by default by
# all Pry instances.
# @return [Array<Proc>] The array of Procs to be used for the
# prompts by default by all Pry instances.
attr_accessor :prompt
# Value returned by last executed Pry command.
# @return [Object] The command value
attr_accessor :cmd_ret_value
# Determines whether colored output is enabled.
# @return [Boolean]
attr_accessor :color
# @return [Fixnum] The current input line.
attr_accessor :current_line
# Determines whether paging (of long blocks of text) is enabled.
# @return [Boolean]
attr_accessor :pager
# @return [Array] The Array of evaluated expressions.
attr_accessor :line_buffer
# Determines whether the rc file (~/.pryrc) should be loaded.
# @return [Boolean]
attr_accessor :should_load_rc
# @return [String] The __FILE__ for the `eval()`. Should be "(pry)"
# by default.
attr_accessor :eval_path
# Set to true if Pry is invoked from command line using `pry` executable
# @return [Boolean]
# @return [OpenStruct] Return Pry's config object.
attr_accessor :config
# @return [Boolean] Whether Pry was activated from the command line.
attr_accessor :cli
# Set to true if the pry-doc extension is loaded.
# @return [Boolean]
attr_accessor :has_pry_doc
# plugin forwardables
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
# The default editor to use. Defaults to $EDITOR or nano if
# $EDITOR is not defined.
# @return [String]
attr_accessor :editor
delegate_accessors :@config, :input, :output, :commands, :prompt, :print, :exception_handler,
:hooks, :color, :pager, :editor
end
# Load the rc files given in the `Pry::RC_FILES` array.
@ -124,9 +91,15 @@ class Pry
# @example
# Pry.start(Object.new, :input => MyInput.new)
def self.start(target=TOPLEVEL_BINDING, options={})
if should_load_rc && !@rc_loaded
load_rc
@rc_loaded = true
if initial_session?
# note these have to be loaded here rather than in pry_instance as
# we only want them loaded once per entire Pry lifetime, not
# multiple times per each new session (i.e in debugging)
load_rc if Pry.config.should_load_rc
load_plugins if Pry.config.should_load_plugins
load_history if Pry.config.history.load
@initial_session = false
end
new(options).repl(target)
@ -137,12 +110,7 @@ class Pry
# @param obj The object to view.
# @return [String] The string representation of `obj`.
def self.view(obj)
case obj
when String, Hash, Array, Symbol, Exception, nil
obj.inspect
else
obj.to_s
end
obj.inspect
rescue NoMethodError
"unknown"
@ -161,10 +129,20 @@ class Pry
end
end
# Load Readline history if required.
def self.load_history
history_file = File.expand_path(Pry.config.history.file)
Readline::HISTORY.push(*File.readlines(history_file).map(&:chomp)) if File.exists?(history_file)
end
# @return [Boolean] Whether this is the first time a Pry session has
# been started since loading the Pry class.
def self.initial_session?
@initial_session
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.
@ -172,35 +150,24 @@ class Pry
# @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.
# output. Defaults to true.
# @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
def self.run_command(command_string, options={})
options = {
:context => TOPLEVEL_BINDING,
:show_output => false,
:show_output => true,
:output => Pry.output,
:commands => Pry.commands
}.merge!(options)
null_output = StringIO.new
output = options[:show_output] ? options[:output] : StringIO.new
context = CommandContext.new
commands = options[:commands]
context.opts = {}
context.output = options[:show_output] ? options[:output] : null_output
context.target = Pry.binding_for(options[:context])
context.command_set = commands
commands.run_command(context, name, *Shellwords.shellwords(arg_string))
Pry.new(:output => output, :input => StringIO.new(command_string), :commands => options[:commands]).rep(options[:context])
end
def self.default_editor_for_platform
@ -211,25 +178,47 @@ class Pry
end
end
# Set all the configurable options back to their default values
def self.reset_defaults
@input = Readline
@output = $stdout
@commands = Pry::Commands
@prompt = DEFAULT_PROMPT
@print = DEFAULT_PRINT
@exception_handler = DEFAULT_EXCEPTION_HANDLER
@hooks = DEFAULT_HOOKS
@custom_completions = DEFAULT_CUSTOM_COMPLETIONS
@color = true
@pager = true
@should_load_rc = true
@rc_loaded = false
@cli = false
@editor = default_editor_for_platform
def self.set_config_defaults
config.input = Readline
config.output = $stdout
config.commands = Pry::Commands
config.prompt = DEFAULT_PROMPT
config.print = DEFAULT_PRINT
config.exception_handler = DEFAULT_EXCEPTION_HANDLER
config.hooks = DEFAULT_HOOKS
config.color = true
config.pager = true
config.editor = default_editor_for_platform
config.should_load_rc = true
config.should_load_plugins = true
config.history ||= OpenStruct.new
config.history.save = true
config.history.load = true
config.history.file = File.expand_path("~/.pry_history")
end
self.reset_defaults
# Set all the configurable options back to their default values
def self.reset_defaults
set_config_defaults
@initial_session = true
self.custom_completions = DEFAULT_CUSTOM_COMPLETIONS
self.cli = false
self.current_line = 0
self.line_buffer = []
self.eval_path = "(pry)"
end
# Basic initialization.
def self.init
@plugin_manager ||= PluginManager.new
self.config ||= Config.new
reset_defaults
locate_plugins
end
@nesting = []
def @nesting.level
@ -260,3 +249,5 @@ class Pry
end
end
end
Pry.init

View file

@ -2,12 +2,13 @@ require "pry/command_processor.rb"
class Pry
# The list of configuration options.
CONFIG_OPTIONS = [:input, :output, :commands, :print,
:exception_handler, :prompt, :hooks,
:custom_completions]
attr_accessor *CONFIG_OPTIONS
attr_accessor :input
attr_accessor :output
attr_accessor :commands
attr_accessor :print
attr_accessor :exception_handler
attr_accessor :hooks
attr_accessor :custom_completions
# Returns the target binding for the session. Note that altering this
# attribute will not change the target binding.
@ -24,18 +25,44 @@ class Pry
# @option options [Proc] :print The Proc to use for the 'print'
# component of the REPL. (see print.rb)
def initialize(options={})
defaults = {}
attributes = [
:input, :output, :commands, :print,
:exception_handler, :hooks, :custom_completions,
:prompt
]
default_options = {}
CONFIG_OPTIONS.each { |v| default_options[v] = Pry.send(v) }
default_options.merge!(options)
attributes.each do |attribute|
defaults[attribute] = Pry.send attribute
end
CONFIG_OPTIONS.each do |key|
instance_variable_set("@#{key}", default_options[key])
defaults.merge!(options).each_key do |key|
send "#{key}=", defaults[key]
end
@command_processor = CommandProcessor.new(self)
end
# The current prompt.
# This is the prompt at the top of the prompt stack.
#
# @example
# self.prompt = Pry::SIMPLE_PROMPT
# self.prompt # => Pry::SIMPLE_PROMPT
#
# @return [Array<Proc>] Current prompt.
def prompt
prompt_stack.last
end
def prompt=(new_prompt)
if prompt_stack.empty?
push_prompt new_prompt
else
prompt_stack[-1] = new_prompt
end
end
# Get nesting data.
# This method should not need to be accessed directly.
# @return [Array] The unparsed nesting information.
@ -50,6 +77,11 @@ class Pry
self.class.nesting = v
end
# @return [Boolean] Whether top-level session has ended.
def finished_top_level_session?
nesting.empty?
end
# Return parent of current Pry session.
# @return [Pry] The parent of the current Pry session.
def parent
@ -71,8 +103,8 @@ class Pry
Pry.active_instance = self
# Make sure special locals exist
target.eval("_pry_ = ::Pry.active_instance")
target.eval("_ = ::Pry.last_result")
set_active_instance(target)
set_last_result(Pry.last_result, target)
self.session_target = target
end
@ -91,6 +123,8 @@ class Pry
throw :breakout, break_data
end
save_history if Pry.config.history.save && finished_top_level_session?
return_value
end
@ -151,22 +185,19 @@ class Pry
Readline.completion_proc = Pry::InputCompleter.build_completion_proc target, instance_eval(&custom_completions)
end
# save the pry instance to active_instance
Pry.active_instance = self
target.eval("_pry_ = ::Pry.active_instance")
@last_result_is_exception = false
set_active_instance(target)
expr = r(target)
# eval the expression and save to last_result
# Do not want __FILE__, __LINE__ here because we need to distinguish
# (eval) methods for show-method and friends.
# This also sets the `_` local for the session.
set_last_result(target.eval(r(target)), target)
Pry.line_buffer.push(*expr.each_line)
set_last_result(target.eval(expr, Pry.eval_path, Pry.current_line), target)
rescue SystemExit => e
exit
rescue Exception => e
@last_result_is_exception = true
set_last_exception(e, target)
ensure
Pry.current_line += expr.each_line.count if expr
end
# Perform a read.
@ -175,18 +206,19 @@ class Pry
# Ruby expression is received.
# Pry commands are also accepted here and operate on the target.
# @param [Object, Binding] target The receiver of the read.
# @param [String] eval_string Optionally Prime `eval_string` with a start value.
# @return [String] The Ruby expression.
# @example
# Pry.new.r(Object.new)
def r(target=TOPLEVEL_BINDING)
def r(target=TOPLEVEL_BINDING, eval_string="")
target = Pry.binding_for(target)
@suppress_output = false
eval_string = ""
val = ""
loop do
val = retrieve_line(eval_string, target)
process_line(val, eval_string, target)
break if valid_expression?(eval_string)
end
@ -195,7 +227,7 @@ class Pry
eval_string
end
# FIXME should delete this method? it's exposing an implementation detail!
# Output the result or pass to an exception handler (if result is an exception).
def show_result(result)
if last_result_is_exception?
exception_handler.call output, result
@ -264,6 +296,14 @@ class Pry
target.eval("_ex_ = ::Pry.last_exception")
end
# Set the active instance for a session.
# This method should not need to be invoked directly.
# @param [Binding] target The binding to set `_ex_` on.
def set_active_instance(target)
Pry.active_instance = self
target.eval("_pry_ = ::Pry.active_instance")
end
# @return [Boolean] True if the last result is an exception that was raised,
# as opposed to simply an instance of Exception (like the result of
# Exception.new)
@ -291,7 +331,7 @@ class Pry
end
rescue EOFError
self.input = Readline
self.input = Pry.input
""
end
end
@ -305,6 +345,14 @@ class Pry
!@suppress_output || last_result_is_exception?
end
# Save readline history to a file.
def save_history
history_file = File.expand_path(Pry.config.history.file)
File.open(history_file, 'w') do |f|
f.write Readline::HISTORY.to_a.join("\n")
end
end
# Returns the appropriate prompt to use.
# This method should not need to be invoked directly.
# @param [Boolean] first_line Whether this is the first line of input
@ -320,6 +368,39 @@ class Pry
end
end
# the array that the prompt stack is stored in
def prompt_stack
@prompt_stack ||= Array.new
end
private :prompt_stack
# Pushes the current prompt onto a stack that it can be restored from later.
# Use this if you wish to temporarily change the prompt.
# @param [Array<Proc>] new_prompt
# @return [Array<Proc>] new_prompt
# @example
# new_prompt = [ proc { '>' }, proc { '>>' } ]
# push_prompt(new_prompt) # => new_prompt
def push_prompt(new_prompt)
prompt_stack.push new_prompt
end
# Pops the current prompt off of the prompt stack.
# If the prompt you are popping is the last prompt, it will not be popped.
# Use this to restore the previous prompt.
# @return [Array<Proc>] Prompt being popped.
# @example
# prompt1 = [ proc { '>' }, proc { '>>' } ]
# prompt2 = [ proc { '$' }, proc { '>' } ]
# pry = Pry.new :prompt => prompt1
# pry.push_prompt(prompt2)
# pry.pop_prompt # => prompt2
# pry.pop_prompt # => prompt1
# pry.pop_prompt # => prompt1
def pop_prompt
prompt_stack.size > 1 ? prompt_stack.pop : prompt
end
if RUBY_VERSION =~ /1.9/ && RUBY_ENGINE == "ruby"
require 'ripper'

View file

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

106
test/helper.rb Normal file
View file

@ -0,0 +1,106 @@
unless Object.const_defined? 'Pry'
$:.unshift File.expand_path '../../lib', __FILE__
require 'pry'
end
require 'bacon'
# Ensure we do not execute any rc files
Pry::RC_FILES.clear
# in case the tests call reset_defaults, ensure we reset them to
# amended (test friendly) values
class << Pry
alias_method :orig_reset_defaults, :reset_defaults
def reset_defaults
orig_reset_defaults
Pry.color = false
Pry.pager = false
Pry.config.should_load_rc = false
Pry.config.history.load = false
Pry.config.history.save = false
end
end
Pry.reset_defaults
# sample doc
def sample_method
:sample
end
def redirect_pry_io(new_in, new_out)
old_in = Pry.input
old_out = Pry.output
Pry.input = new_in
Pry.output = new_out
begin
yield
ensure
Pry.input = old_in
Pry.output = old_out
end
end
def redirect_global_pry_input(new_io)
old_io = Pry.input
Pry.input = new_io
begin
yield
ensure
Pry.input = old_io
end
end
def redirect_global_pry_output(new_io)
old_io = Pry.output
Pry.output = new_io
begin
yield
ensure
Pry.output = old_io
end
end
class Module
public :remove_const
public :remove_method
end
class InputTester
def initialize(*actions)
@orig_actions = actions.dup
@actions = actions
end
def readline(*)
@actions.shift
end
def rewind
@actions = @orig_actions.dup
end
end
class Pry
# null output class - doesn't write anywwhere.
class NullOutput
def self.puts(*) end
def self.string(*) end
end
end
CommandTester = Pry::CommandSet.new do
command "command1", "command 1 test" do
output.puts "command1"
end
command "command2", "command 2 test" do |arg|
output.puts arg
end
end

View file

@ -1,928 +0,0 @@
require 'test_helper'
puts "Ruby Version #{RUBY_VERSION}"
puts "Testing Pry #{Pry::VERSION}"
puts "With method_source version #{MethodSource::VERSION}"
puts "--"
describe Pry do
describe "open a Pry session on an object" do
describe "rep" do
before do
class Hello
end
end
after do
Object.send(:remove_const, :Hello)
end
# bug fix for https://github.com/banister/pry/issues/93
it 'should not leak pry constants into Object namespace' do
input_string = "VERSION == ::Pry::VERSION"
str_output = StringIO.new
o = Object.new
pry_tester = Pry.new(:input => StringIO.new(input_string),
:output => str_output,
:exception_handler => proc { |_, exception| @excep = exception },
:print => proc {}
).rep(o)
@excep.is_a?(NameError).should == true
end
it 'should set an ivar on an object' do
input_string = "@x = 10"
input = InputTester.new(input_string)
o = Object.new
pry_tester = Pry.new(:input => input, :output => Pry::NullOutput)
pry_tester.rep(o)
o.instance_variable_get(:@x).should == 10
end
it 'should make self evaluate to the receiver of the rep session' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("self"), :output => str_output)
pry_tester.rep(o)
str_output.string.should =~ /#{o.to_s}/
end
it 'should work with multi-line input' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("x = ", "1 + 4"), :output => str_output)
pry_tester.rep(o)
str_output.string.should =~ /5/
end
it 'should define a nested class under Hello and not on top-level or Pry' do
pry_tester = Pry.new(:input => InputTester.new("class Nested", "end"), :output => Pry::NullOutput)
pry_tester.rep(Hello)
Hello.const_defined?(:Nested).should == true
end
it 'should suppress output if input ends in a ";" and is an Exception object (single line)' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("Exception.new;"), :output => str_output)
pry_tester.rep(o)
str_output.string.should == ""
end
it 'should suppress output if input ends in a ";" (single line)' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("x = 5;"), :output => str_output)
pry_tester.rep(o)
str_output.string.should == ""
end
it 'should suppress output if input ends in a ";" (multi-line)' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("def self.blah", ":test", "end;"), :output => str_output)
pry_tester.rep(o)
str_output.string.should == ""
end
it 'should be able to evaluate exceptions normally' do
o = Exception.new
str_output = StringIO.new
was_called = false
pry_tester = Pry.new(:input => InputTester.new("self"),
:output => str_output,
:exception_handler => proc { was_called = true })
pry_tester.rep(o)
was_called.should == false
end
it 'should notice when exceptions are raised' do
o = Exception.new
str_output = StringIO.new
was_called = false
pry_tester = Pry.new(:input => InputTester.new("raise self"),
:output => str_output,
:exception_handler => proc { was_called = true })
pry_tester.rep(o)
was_called.should == true
end
end
describe "repl" do
describe "basic functionality" do
it 'should set an ivar on an object and exit the repl' do
input_strings = ["@x = 10", "exit"]
input = InputTester.new(*input_strings)
o = Object.new
pry_tester = Pry.start(o, :input => input, :output => Pry::NullOutput)
o.instance_variable_get(:@x).should == 10
end
end
describe "test loading rc files" do
after do
Pry::RC_FILES.clear
Pry.should_load_rc = false
end
it "should run the rc file only once" do
Pry.should_load_rc = true
Pry::RC_FILES << File.expand_path("../testrc", __FILE__)
Pry.start(self, :input => StringIO.new("exit\n"), :output => Pry::NullOutput)
TEST_RC.should == [0]
Pry.start(self, :input => StringIO.new("exit\n"), :output => Pry::NullOutput)
TEST_RC.should == [0]
Object.remove_const(:TEST_RC)
end
it "should not run the rc file at all if Pry.should_load_rc is false" do
Pry.should_load_rc = false
Pry.start(self, :input => StringIO.new("exit\n"), :output => Pry::NullOutput)
Object.const_defined?(:TEST_RC).should == false
end
it "should not load the rc file if #repl method invoked" do
Pry.should_load_rc = true
Pry.new(:input => StringIO.new("exit\n"), :output => Pry::NullOutput).repl(self)
Object.const_defined?(:TEST_RC).should == false
Pry.should_load_rc = false
end
end
describe "nesting" do
after do
Pry.reset_defaults
Pry.color = false
end
it 'should nest properly' do
Pry.input = InputTester.new("pry", "pry", "pry", "\"nest:\#\{Pry.nesting.level\}\"", "exit_all")
str_output = StringIO.new
Pry.output = str_output
o = Object.new
pry_tester = o.pry
str_output.string.should =~ /nest:3/
end
end
describe "defining methods" do
it 'should define a method on the singleton class of an object when performing "def meth;end" inside the object' do
[Object.new, {}, []].each do |val|
str_input = StringIO.new("def hello;end")
Pry.new(:input => str_input, :output => StringIO.new).rep(val)
val.methods(false).map(&:to_sym).include?(:hello).should == true
end
end
it 'should define an instance method on the module when performing "def meth;end" inside the module' do
str_input = StringIO.new("def hello;end")
hello = Module.new
Pry.new(:input => str_input, :output => StringIO.new).rep(hello)
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
it 'should define an instance method on the class when performing "def meth;end" inside the class' do
str_input = StringIO.new("def hello;end")
hello = Class.new
Pry.new(:input => str_input, :output => StringIO.new).rep(hello)
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
it 'should define a method on the class of an object when performing "def meth;end" inside an immediate value or Numeric' do
# should include float in here, but test fails for some reason
# on 1.8.7, no idea why!
[:test, 0, true, false, nil].each do |val|
str_input = StringIO.new("def hello;end")
Pry.new(:input => str_input, :output => StringIO.new).rep(val)
val.class.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
end
end
describe "commands" do
it 'should run a command with no parameter' do
pry_tester = Pry.new
pry_tester.commands = CommandTester
pry_tester.input = InputTester.new("command1", "exit_all")
pry_tester.commands = CommandTester
str_output = StringIO.new
pry_tester.output = str_output
pry_tester.rep
str_output.string.should =~ /command1/
end
it 'should run a command with one parameter' do
pry_tester = Pry.new
pry_tester.commands = CommandTester
pry_tester.input = InputTester.new("command2 horsey", "exit_all")
pry_tester.commands = CommandTester
str_output = StringIO.new
pry_tester.output = str_output
pry_tester.rep
str_output.string.should =~ /horsey/
end
end
describe "Object#pry" do
after do
Pry.reset_defaults
Pry.color = false
end
it "should start a pry session on the receiver (first form)" do
Pry.input = InputTester.new("self", "exit")
str_output = StringIO.new
Pry.output = str_output
20.pry
str_output.string.should =~ /20/
end
it "should start a pry session on the receiver (second form)" do
Pry.input = InputTester.new("self", "exit")
str_output = StringIO.new
Pry.output = str_output
pry 20
str_output.string.should =~ /20/
end
it "should error if more than one argument is passed to Object#pry" do
lambda { pry(20, :input => Readline) }.should.raise ArgumentError
end
end
describe "Pry.binding_for" do
it 'should return TOPLEVEL_BINDING if parameter self is main' do
_main_ = lambda { TOPLEVEL_BINDING.eval('self') }
Pry.binding_for(_main_.call).is_a?(Binding).should == true
Pry.binding_for(_main_.call).should == TOPLEVEL_BINDING
Pry.binding_for(_main_.call).should == Pry.binding_for(_main_.call)
end
end
describe "test Pry defaults" do
after do
Pry.reset_defaults
Pry.color = false
end
describe "input" do
after do
Pry.reset_defaults
Pry.color = false
end
it 'should set the input default, and the default should be overridable' do
Pry.input = InputTester.new("5")
str_output = StringIO.new
Pry.output = str_output
Pry.new.rep
str_output.string.should =~ /5/
Pry.new(:input => InputTester.new("6")).rep
str_output.string.should =~ /6/
end
it 'should pass in the prompt if readline arity is 1' do
Pry.prompt = proc { "A" }
arity_one_input = Class.new do
attr_accessor :prompt
def readline(prompt)
@prompt = prompt
"exit"
end
end.new
Pry.start(self, :input => arity_one_input, :output => Pry::NullOutput)
arity_one_input.prompt.should == Pry.prompt.call
end
it 'should not pass in the prompt if the arity is 0' do
Pry.prompt = proc { "A" }
arity_zero_input = Class.new do
def readline
"exit"
end
end.new
lambda { Pry.start(self, :input => arity_zero_input, :output => Pry::NullOutput) }.should.not.raise Exception
end
it 'should not pass in the prompt if the arity is -1' do
Pry.prompt = proc { "A" }
arity_multi_input = Class.new do
attr_accessor :prompt
def readline(*args)
@prompt = args.first
"exit"
end
end.new
Pry.start(self, :input => arity_multi_input, :output => Pry::NullOutput)
arity_multi_input.prompt.should == nil
end
end
it 'should set the output default, and the default should be overridable' do
Pry.input = InputTester.new("5", "6", "7")
str_output = StringIO.new
Pry.output = str_output
Pry.new.rep
str_output.string.should =~ /5/
Pry.new.rep
str_output.string.should =~ /5\n.*6/
str_output2 = StringIO.new
Pry.new(:output => str_output2).rep
str_output2.string.should.not =~ /5\n.*6/
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 -afv", :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 interpolate ruby code into commands' do
klass = Class.new(Pry::CommandBase) do
command "hello", "", :keep_retval => true do |arg|
arg
end
end
$test_interpolation = "bing"
str_output = StringIO.new
Pry.new(:input => StringIO.new("hello #{$test_interpolation}"), :output => str_output, :commands => klass).rep
str_output.string.should =~ /bing/
$test_interpolation = nil
end
it 'should create a comand in a nested context and that command should be accessible from the parent' do
str_input = StringIO.new("@x=nil\ncd 7\n_pry_.commands.instance_eval {\ncommand('bing') { |arg| run arg }\n}\ncd ..\nbing ls\nexit")
str_output = StringIO.new
Pry.input = str_input
obj = Object.new
Pry.new(:output => str_output).repl(obj)
Pry.input = Readline
str_output.string.should =~ /@x/
end
it 'should define a command that keeps its return value' do
class Command68 < Pry::CommandBase
command "hello", "", :keep_retval => true do
:kept_hello
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("hello\n"), :output => str_output, :commands => Command68).rep
str_output.string.should =~ /:kept_hello/
str_output.string.should =~ /=>/
Object.remove_const(:Command68)
end
it 'should define a command that does NOT keep its return value' do
class Command68 < Pry::CommandBase
command "hello", "", :keep_retval => false do
:kept_hello
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("hello\n"), :output => str_output, :commands => Command68).rep
(str_output.string =~ /:kept_hello/).should == nil
str_output.string !~ /=>/
Object.remove_const(:Command68)
end
it 'should set the commands default, and the default should be overridable' do
class Command0 < Pry::CommandBase
command "hello" do
output.puts "hello world"
end
end
Pry.commands = Command0
str_output = StringIO.new
Pry.new(:input => InputTester.new("hello"), :output => str_output).rep
str_output.string.should =~ /hello world/
class Command1 < Pry::CommandBase
command "goodbye", "" do
output.puts "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", "h command" do
end
end
Command2.commands.keys.size.should == 3
Command2.commands.keys.include?("help").should == true
Command2.commands.keys.include?("install").should == true
Command2.commands.keys.include?("h").should == true
Object.remove_const(:Command2)
end
it 'should inherit commands from Pry::Commands' do
class Command3 < Pry::Commands
command "v" do
end
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
Object.remove_const(:Command3)
end
it 'should alias a command with another command' do
class Command6 < Pry::CommandBase
alias_command "help2", "help"
end
Command6.commands["help2"].should == Command6.commands["help"]
# 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(:Command6)
end
it 'should change description of a command using desc' do
class Command7 < Pry::Commands
end
orig = Command7.commands["help"][:description]
class Command7
desc "help", "blah"
end
Command7.commands["help"][:description].should.not == orig
Command7.commands["help"][:description].should == "blah"
Object.remove_const(:Command7)
end
it 'should run a command from within a command' do
class Command01 < 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 => Command01).rep
str_output.string.should =~ /v command/
Object.remove_const(:Command01)
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.rstrip.should == "john"
Object.remove_const(:Command3)
Object.remove_const(:Command4)
end
it 'should import commands from another command object' do
Object.remove_const(:Command77) if Object.const_defined?(:Command77)
class Command77 < 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 => Command77).rep("john")
str_output.string.should =~ /Status:/
Object.remove_const(:Command77)
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.rstrip.should == "jump-to the music"
str_output = StringIO.new
Pry.new(:input => InputTester.new("help"), :output => str_output, :commands => Command3).rep
str_output.string.rstrip.should == "help to the music"
Object.remove_const(:Command3)
Pry.reset_defaults
Pry.color = false
end
end
it "should set the print default, and the default should be overridable" do
new_print = proc { |out, value| out.puts value }
Pry.print = new_print
Pry.new.print.should == Pry.print
str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => str_output).rep
str_output.string.should == "test\n"
str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => str_output,
:print => proc { |out, value| out.puts value.reverse }).rep
str_output.string.should == "tset\n"
Pry.new.print.should == Pry.print
str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => str_output).rep
str_output.string.should == "test\n"
end
describe "pry return values" do
it 'should return the target object' do
Pry.start(self, :input => StringIO.new("exit"), :output => Pry::NullOutput).should == self
end
it 'should return the parameter given to exit' do
Pry.start(self, :input => StringIO.new("exit 10"), :output => Pry::NullOutput).should == 10
end
it 'should return the parameter (multi word string) given to exit' do
Pry.start(self, :input => StringIO.new("exit \"john mair\""), :output => Pry::NullOutput).should == "john mair"
end
it 'should return the parameter (function call) given to exit' do
Pry.start(self, :input => StringIO.new("exit 'abc'.reverse"), :output => Pry::NullOutput).should == 'cba'
end
it 'should return the parameter (self) given to exit' do
Pry.start("carl", :input => StringIO.new("exit self"), :output => Pry::NullOutput).should == "carl"
end
end
describe "prompts" do
it 'should set the prompt default, and the default should be overridable (single prompt)' do
new_prompt = proc { "test prompt> " }
Pry.prompt = new_prompt
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt> "
new_prompt = proc { "A" }
pry_tester = Pry.new(:prompt => new_prompt)
pry_tester.prompt.should == new_prompt
pry_tester.select_prompt(true, 0).should == "A"
pry_tester.select_prompt(false, 0).should == "A"
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt> "
end
it 'should set the prompt default, and the default should be overridable (multi prompt)' do
new_prompt = [proc { "test prompt> " }, proc { "test prompt* " }]
Pry.prompt = new_prompt
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt* "
new_prompt = [proc { "A" }, proc { "B" }]
pry_tester = Pry.new(:prompt => new_prompt)
pry_tester.prompt.should == new_prompt
pry_tester.select_prompt(true, 0).should == "A"
pry_tester.select_prompt(false, 0).should == "B"
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt* "
end
end
it 'should set the hooks default, and the default should be overridable' do
Pry.input = InputTester.new("exit")
Pry.hooks = {
:before_session => proc { |out,_| out.puts "HELLO" },
:after_session => proc { |out,_| out.puts "BYE" }
}
str_output = StringIO.new
Pry.new(:output => str_output).repl
str_output.string.should =~ /HELLO/
str_output.string.should =~ /BYE/
Pry.input.rewind
str_output = StringIO.new
Pry.new(:output => str_output,
:hooks => {
:before_session => proc { |out,_| out.puts "MORNING" },
:after_session => proc { |out,_| out.puts "EVENING" }
}
).repl
str_output.string.should =~ /MORNING/
str_output.string.should =~ /EVENING/
# try below with just defining one hook
Pry.input.rewind
str_output = StringIO.new
Pry.new(:output => str_output,
:hooks => {
:before_session => proc { |out,_| out.puts "OPEN" }
}
).repl
str_output.string.should =~ /OPEN/
Pry.input.rewind
str_output = StringIO.new
Pry.new(:output => str_output,
:hooks => {
:after_session => proc { |out,_| out.puts "CLOSE" }
}
).repl
str_output.string.should =~ /CLOSE/
Pry.reset_defaults
Pry.color = false
end
end
end
end
end
describe Pry::CommandSet do
before do
@set = Pry::CommandSet.new(:some_name)
end
it 'should use the name specified at creation' do
@set.name.should == :some_name
end
it 'should call the block used for the command when it is called' do
run = false
@set.command 'foo' do
run = true
end
@set.run_command nil, 'foo'
run.should == true
end
it 'should pass arguments of the command to the block' do
@set.command 'foo' do |*args|
args.should == [1, 2, 3]
end
@set.run_command nil, 'foo', 1, 2, 3
end
it 'should use the first argument as self' do
@set.command 'foo' do
self.should == true
end
@set.run_command true, 'foo'
end
it 'should raise an error when calling an undefined comand' do
@set.command('foo') {}
lambda {
@set.run_command nil, 'bar'
}.should.raise(Pry::NoCommandError)
end
it 'should be able to remove its own commands' do
@set.command('foo') {}
@set.delete 'foo'
lambda {
@set.run_command nil, 'foo'
}.should.raise(Pry::NoCommandError)
end
it 'should be able to import some commands from other sets' do
run = false
other_set = Pry::CommandSet.new :foo do
command('foo') { run = true }
command('bar') {}
end
@set.import_from(other_set, 'foo')
@set.run_command nil, 'foo'
run.should == true
lambda {
@set.run_command nil, 'bar'
}.should.raise(Pry::NoCommandError)
end
it 'should be able to import a whole set' do
run = []
other_set = Pry::CommandSet.new :foo do
command('foo') { run << true }
command('bar') { run << true }
end
@set.import other_set
@set.run_command nil, 'foo'
@set.run_command nil, 'bar'
run.should == [true, true]
end
it 'should be able to import sets at creation' do
run = false
@set.command('foo') { run = true }
Pry::CommandSet.new(:other, @set).run_command nil, 'foo'
run.should == true
end
it 'should set the descriptions of commands' do
@set.command('foo', 'some stuff') {}
@set.commands['foo'].description.should == 'some stuff'
end
it 'should be able to alias method' do
run = false
@set.command('foo', 'stuff') { run = true }
@set.alias_command 'bar', 'foo'
@set.commands['bar'].name.should == 'bar'
@set.commands['bar'].description.should == 'stuff'
@set.run_command nil, 'bar'
run.should == true
end
it 'should be able to change the descritpions of methods' do
@set.command('foo', 'bar') {}
@set.desc 'foo', 'baz'
@set.commands['foo'].description.should == 'baz'
end
it 'should return nil for commands by default' do
@set.command('foo') { 3 }
@set.run_command(nil, 'foo').should == nil
end
it 'should be able to keep return values' do
@set.command('foo', '', :keep_retval => true) { 3 }
@set.run_command(nil, 'foo').should == 3
end
end

View file

@ -0,0 +1,77 @@
require 'helper'
describe Pry::Helpers::CommandHelpers do
before do
@helper = Pry::Helpers::CommandHelpers
end
describe "get_method_object" do
it 'should look up instance methods if no methods available and no options provided' do
klass = Class.new { def hello; end }
meth = @helper.get_method_object(:hello, Pry.binding_for(klass), {})
meth.should == klass.instance_method(:hello)
end
it 'should look up methods if no instance methods available and no options provided' do
klass = Class.new { def self.hello; end }
meth = @helper.get_method_object(:hello, Pry.binding_for(klass), {})
meth.should == klass.method(:hello)
end
it 'should look up instance methods first even if methods available and no options provided' do
klass = Class.new { def hello; end; def self.hello; end }
meth = @helper.get_method_object(:hello, Pry.binding_for(klass), {})
meth.should == klass.instance_method(:hello)
end
it 'should look up instance methods if "instance-methods" option provided' do
klass = Class.new { def hello; end; def self.hello; end }
meth = @helper.get_method_object(:hello, Pry.binding_for(klass), {"instance-methods" => true})
meth.should == klass.instance_method(:hello)
end
it 'should look up methods if :methods option provided' do
klass = Class.new { def hello; end; def self.hello; end }
meth = @helper.get_method_object(:hello, Pry.binding_for(klass), {:methods => true})
meth.should == klass.method(:hello)
end
it 'should look up instance methods using the Class#method syntax' do
klass = Class.new { def hello; end; def self.hello; end }
meth = @helper.get_method_object("klass#hello", Pry.binding_for(binding), {})
meth.should == klass.instance_method(:hello)
end
it 'should look up methods using the object.method syntax' do
klass = Class.new { def hello; end; def self.hello; end }
meth = @helper.get_method_object("klass.hello", Pry.binding_for(binding), {})
meth.should == klass.method(:hello)
end
it 'should NOT look up instance methods using the Class#method syntax if no instance methods defined' do
klass = Class.new { def self.hello; end }
meth = @helper.get_method_object("klass#hello", Pry.binding_for(binding), {})
meth.should == nil
end
it 'should NOT look up methods using the object.method syntax if no methods defined' do
klass = Class.new { def hello; end }
meth = @helper.get_method_object("klass.hello", Pry.binding_for(binding), {})
meth.should == nil
end
it 'should look up methods using klass.new.method syntax' do
klass = Class.new { def hello; :hello; end }
meth = @helper.get_method_object("klass.new.hello", Pry.binding_for(binding), {})
meth.name.to_sym.should == :hello
end
it 'should look up instance methods using klass.meth#method syntax' do
klass = Class.new { def self.meth; Class.new; end }
meth = @helper.get_method_object("klass.meth#initialize", Pry.binding_for(binding), {})
meth.name.to_sym.should == :initialize
end
end
end

View file

@ -0,0 +1,99 @@
require 'helper'
describe "Pry::CommandProcessor" do
before do
@pry = Pry.new
@pry.commands = Pry::CommandSet.new
@command_processor = Pry::CommandProcessor.new(@pry)
end
after do
@pry.commands = Pry::CommandSet.new
end
it 'should correctly match a simple string command' do
@pry.commands.command("test-command") {}
command, captures, pos = @command_processor.command_matched "test-command"
command.name.should == "test-command"
captures.should == []
pos.should == "test-command".length
end
it 'should correctly match a simple string command with parameters' do
@pry.commands.command("test-command") { |arg|}
command, captures, pos = @command_processor.command_matched "test-command hello"
command.name.should == "test-command"
captures.should == []
pos.should == "test-command".length
end
it 'should not match when the relevant command does not exist' do
command, captures, pos = @command_processor.command_matched "test-command"
command.should == nil
captures.should == nil
end
it 'should correctly match a regex command' do
@pry.commands.command(/rue(.?)/) { }
command, captures, pos = @command_processor.command_matched "rue hello"
command.name.should == /rue(.?)/
captures.should == [""]
pos.should == 3
end
it 'should correctly match a regex command and extract the capture groups' do
@pry.commands.command(/rue(.?)/) { }
command, captures, pos = @command_processor.command_matched "rue5 hello"
command.name.should == /rue(.?)/
captures.should == ["5"]
pos.should == 4
end
it 'should correctly match a string command with spaces in its name' do
@pry.commands.command("test command") {}
command, captures, pos = @command_processor.command_matched "test command"
command.name.should == "test command"
captures.should == []
pos.should == command.name.length
end
it 'should correctly match a string command with spaces in its name with parameters' do
@pry.commands.command("test command") {}
command, captures, pos = @command_processor.command_matched "test command param1 param2"
command.name.should == "test command"
captures.should == []
pos.should == command.name.length
end
it 'should correctly match a regex command with spaces in its name' do
regex_command_name = /test\s+(.+)\s+command/
@pry.commands.command(regex_command_name) {}
sample_text = "test friendship command"
command, captures, pos = @command_processor.command_matched sample_text
command.name.should == regex_command_name
captures.should == ["friendship"]
pos.should == sample_text.size
end
it 'should correctly match a complex regex command' do
regex_command_name = /\.(.*)/
@pry.commands.command(regex_command_name) {}
sample_text = ".cd ~/pry"
command, captures, pos = @command_processor.command_matched sample_text
command.name.should == regex_command_name
captures.should == ["cd ~/pry"]
pos.should == sample_text.size
end
end

190
test/test_command_set.rb Normal file
View file

@ -0,0 +1,190 @@
require 'helper'
describe Pry::CommandSet do
before do
@set = Pry::CommandSet.new
end
it 'should call the block used for the command when it is called' do
run = false
@set.command 'foo' do
run = true
end
@set.run_command nil, 'foo'
run.should == true
end
it 'should pass arguments of the command to the block' do
@set.command 'foo' do |*args|
args.should == [1, 2, 3]
end
@set.run_command nil, 'foo', 1, 2, 3
end
it 'should use the first argument as self' do
@set.command 'foo' do
self.should == true
end
@set.run_command true, 'foo'
end
it 'should raise an error when calling an undefined command' do
@set.command('foo') {}
lambda {
@set.run_command nil, 'bar'
}.should.raise(Pry::NoCommandError)
end
it 'should be able to remove its own commands' do
@set.command('foo') {}
@set.delete 'foo'
lambda {
@set.run_command nil, 'foo'
}.should.raise(Pry::NoCommandError)
end
it 'should be able to import some commands from other sets' do
run = false
other_set = Pry::CommandSet.new do
command('foo') { run = true }
command('bar') {}
end
@set.import_from(other_set, 'foo')
@set.run_command nil, 'foo'
run.should == true
lambda {
@set.run_command nil, 'bar'
}.should.raise(Pry::NoCommandError)
end
it 'should be able to import a whole set' do
run = []
other_set = Pry::CommandSet.new do
command('foo') { run << true }
command('bar') { run << true }
end
@set.import other_set
@set.run_command nil, 'foo'
@set.run_command nil, 'bar'
run.should == [true, true]
end
it 'should be able to import sets at creation' do
run = false
@set.command('foo') { run = true }
Pry::CommandSet.new(@set).run_command nil, 'foo'
run.should == true
end
it 'should set the descriptions of commands' do
@set.command('foo', 'some stuff') {}
@set.commands['foo'].description.should == 'some stuff'
end
it 'should be able to alias method' do
run = false
@set.command('foo', 'stuff') { run = true }
@set.alias_command 'bar', 'foo'
@set.commands['bar'].name.should == 'bar'
@set.commands['bar'].description.should == 'stuff'
@set.run_command nil, 'bar'
run.should == true
end
it 'should be able to change the descritpions of methods' do
@set.command('foo', 'bar') {}
@set.desc 'foo', 'baz'
@set.commands['foo'].description.should == 'baz'
end
it 'should return nil for commands by default' do
@set.command('foo') { 3 }
@set.run_command(nil, 'foo').should == nil
end
it 'should be able to keep return values' do
@set.command('foo', '', :keep_retval => true) { 3 }
@set.run_command(nil, 'foo').should == 3
end
it 'should be able to have its own helpers' do
@set.command('foo') do
should.respond_to :my_helper
end
@set.helpers do
def my_helper; end
end
@set.run_command(Pry::CommandContext.new, 'foo')
Pry::CommandContext.new.should.not.respond_to :my_helper
end
it 'should not recreate a new helper module when helpers is called' do
@set.command('foo') do
should.respond_to :my_helper
should.respond_to :my_other_helper
end
@set.helpers do
def my_helper; end
end
@set.helpers do
def my_other_helper; end
end
@set.run_command(Pry::CommandContext.new, 'foo')
end
it 'should import helpers from imported sets' do
imported_set = Pry::CommandSet.new do
helpers do
def imported_helper_method; end
end
end
@set.import imported_set
@set.command('foo') { should.respond_to :imported_helper_method }
@set.run_command(Pry::CommandContext.new, 'foo')
end
it 'should import helpers even if only some commands are imported' do
imported_set = Pry::CommandSet.new do
helpers do
def imported_helper_method; end
end
command('bar') {}
end
@set.import_from imported_set, 'bar'
@set.command('foo') { should.respond_to :imported_helper_method }
@set.run_command(Pry::CommandContext.new, 'foo')
end
it 'should provide a :listing for a command that defaults to its name' do
@set.command 'foo', "" do;end
@set.commands['foo'].options[:listing].should == 'foo'
end
it 'should provide a :listing for a command that differs from its name' do
@set.command 'foo', "", :listing => 'bar' do;end
@set.commands['foo'].options[:listing].should == 'bar'
end
end

View file

@ -0,0 +1,400 @@
require 'helper'
describe "Pry::Commands" do
after do
$obj = nil
end
describe "hist" do
push_first_hist_line = lambda do |hist, line|
hist.push line
end
before do
Readline::HISTORY.shift until Readline::HISTORY.empty?
@hist = Readline::HISTORY
end
it 'should display the correct history' do
push_first_hist_line.call(@hist, "'bug in 1.8 means this line is ignored'")
@hist.push "hello"
@hist.push "world"
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /hello\n.*world/
end
it 'should replay history correctly (single item)' do
push_first_hist_line.call(@hist, ":hello")
@hist.push ":blah"
@hist.push ":bucket"
@hist.push ":ostrich"
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist --replay -1", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /ostrich/
end
it 'should replay a range of history correctly (range of items)' do
push_first_hist_line.call(@hist, "'bug in 1.8 means this line is ignored'")
@hist.push ":hello"
@hist.push ":carl"
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist --replay 0..2", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /:hello\n.*:carl/
end
it 'should grep for correct lines in history' do
push_first_hist_line.call(@hist, "apple")
@hist.push "abby"
@hist.push "box"
@hist.push "button"
@hist.push "pepper"
@hist.push "orange"
@hist.push "grape"
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist --grep o", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /\d:.*?box\n\d:.*?button\n\d:.*?orange/
end
it 'should return last N lines in history with --tail switch' do
push_first_hist_line.call(@hist, "0")
("a".."z").each do |v|
@hist.push v
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist --tail 3", "exit-all"), str_output) do
pry
end
str_output.string.each_line.count.should == 3
str_output.string.should =~ /x\n\d+:.*y\n\d+:.*z/
end
# strangeness in this test is due to bug in Readline::HISTORY not
# always registering first line of input
it 'should return first N lines in history with --head switch' do
push_first_hist_line.call(@hist, "0")
("a".."z").each do |v|
@hist.push v
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist --head 4", "exit-all"), str_output) do
pry
end
str_output.string.each_line.count.should == 4
str_output.string.should =~ /a\n\d+:.*b\n\d+:.*c/
end
# strangeness in this test is due to bug in Readline::HISTORY not
# always registering first line of input
it 'should show lines between lines A and B with the --show switch' do
push_first_hist_line.call(@hist, "0")
("a".."z").each do |v|
@hist.push v
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hist --show 1..4", "exit-all"), str_output) do
pry
end
str_output.string.each_line.count.should == 4
str_output.string.should =~ /b\n\d+:.*c\n\d+:.*d/
end
end
describe "show-method" do
it 'should output a method\'s source' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("show-method sample_method", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /def sample/
end
it 'should output a method\'s source with line numbers' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("show-method -l sample_method", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /\d+: def sample/
end
it 'should output a method\'s source if inside method without needing to use method name' do
$str_output = StringIO.new
o = Object.new
def o.sample
redirect_pry_io(InputTester.new("show-method", "exit-all"), $str_output) do
binding.pry
end
end
o.sample
$str_output.string.should =~ /def o.sample/
$str_output = nil
end
it 'should output a method\'s source if inside method without needing to use method name, and using the -l switch' do
$str_output = StringIO.new
o = Object.new
def o.sample
redirect_pry_io(InputTester.new("show-method -l", "exit-all"), $str_output) do
binding.pry
end
end
o.sample
$str_output.string.should =~ /\d+: def o.sample/
$str_output = nil
end
# dynamically defined method source retrieval is only supported in
# 1.9 - where Method#source_location is native
if RUBY_VERSION =~ /1.9/
it 'should output a method\'s source for a method defined inside pry' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("def dyna_method", ":testing", "end", "show-method dyna_method"), str_output) do
TOPLEVEL_BINDING.pry
end
str_output.string.should =~ /def dyna_method/
Object.remove_method :dyna_method
end
it 'should output a method\'s source for a method defined inside pry, even if exceptions raised before hand' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("bad code", "123", "bad code 2", "1 + 2", "def dyna_method", ":testing", "end", "show-method dyna_method"), str_output) do
TOPLEVEL_BINDING.pry
end
str_output.string.should =~ /def dyna_method/
Object.remove_method :dyna_method
end
it 'should output an instance method\'s source for a method defined inside pry' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("class A", "def yo", "end", "end", "show-method A#yo"), str_output) do
TOPLEVEL_BINDING.pry
end
str_output.string.should =~ /def yo/
Object.remove_const :A
end
it 'should output an instance method\'s source for a method defined inside pry using define_method' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("class A", "define_method(:yup) {}", "end", "end", "show-method A#yup"), str_output) do
TOPLEVEL_BINDING.pry
end
str_output.string.should =~ /define_method\(:yup\)/
Object.remove_const :A
end
end
end
describe "show-doc" do
it 'should output a method\'s documentation' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("show-doc sample_method", "exit-all"), str_output) do
pry
end
str_output.string.should =~ /sample doc/
end
it 'should output a method\'s documentation if inside method without needing to use method name' do
$str_output = StringIO.new
o = Object.new
def o.sample
redirect_pry_io(InputTester.new("show-doc", "exit-all"), $str_output) do
binding.pry
end
end
o.sample
$str_output.string.should =~ /sample doc/
$str_output = nil
end
end
describe "cd" do
it 'should cd into simple input' do
b = Pry.binding_for(Object.new)
b.eval("x = :mon_ouie")
redirect_pry_io(InputTester.new("cd x", "$obj = self", "exit-all"), StringIO.new) do
b.pry
end
$obj.should == :mon_ouie
end
it 'should break out of session with cd ..' do
b = Pry.binding_for(:outer)
b.eval("x = :inner")
redirect_pry_io(InputTester.new("cd x", "$inner = self;", "cd ..", "$outer = self", "exit-all"), StringIO.new) do
b.pry
end
$inner.should == :inner
$outer.should == :outer
end
it 'should break out to outer-most session with cd /' do
b = Pry.binding_for(:outer)
b.eval("x = :inner")
redirect_pry_io(InputTester.new("cd x", "$inner = self;", "cd 5", "$five = self", "cd /", "$outer = self", "exit-all"), StringIO.new) do
b.pry
end
$inner.should == :inner
$five.should == 5
$outer.should == :outer
end
it 'should start a session on TOPLEVEL_BINDING with cd ::' do
b = Pry.binding_for(:outer)
redirect_pry_io(InputTester.new("cd ::", "$obj = self", "exit-all"), StringIO.new) do
5.pry
end
$obj.should == TOPLEVEL_BINDING.eval('self')
end
it 'should cd into complex input (with spaces)' do
o = Object.new
def o.hello(x, y, z)
:mon_ouie
end
redirect_pry_io(InputTester.new("cd hello 1, 2, 3", "$obj = self", "exit-all"), StringIO.new) do
o.pry
end
$obj.should == :mon_ouie
end
end
# show-command only works in implementations that support Proc#source_location
if Proc.method_defined?(:source_location)
describe "show-command" do
it 'should show source for an ordinary command' do
set = Pry::CommandSet.new do
import_from Pry::Commands, "show-command"
command "foo" do
:body_of_foo
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("show-command foo"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /:body_of_foo/
end
it 'should show source for a command with spaces in its name' do
set = Pry::CommandSet.new do
import_from Pry::Commands, "show-command"
command "foo bar" do
:body_of_foo_bar
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("show-command \"foo bar\""), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /:body_of_foo_bar/
end
it 'should show source for a command by listing name' do
set = Pry::CommandSet.new do
import_from Pry::Commands, "show-command"
command /foo(.*)/, "", :listing => "bar" do
:body_of_foo_regex
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("show-command bar"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /:body_of_foo_regex/
end
end
end
describe "help" do
it 'should display help for a specific command' do
str_output = StringIO.new
redirect_pry_io(InputTester.new("help ls", "exit-all"), str_output) do
pry
end
str_output.string.each_line.count.should == 1
str_output.string.should =~ /ls --help/
end
it 'should display help for a regex command with a "listing"' do
set = Pry::CommandSet.new do
command /bar(.*)/, "Test listing", :listing => "foo" do
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("help foo"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.each_line.count.should == 1
str_output.string.should =~ /Test listing/
end
it 'should display help for a command with a spaces in its name' do
set = Pry::CommandSet.new do
command "command with spaces", "description of a command with spaces" do
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("help \"command with spaces\""), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.each_line.count.should == 1
str_output.string.should =~ /description of a command with spaces/
end
it 'should display help for all commands with a description' do
set = Pry::CommandSet.new do
command /bar(.*)/, "Test listing", :listing => "foo" do; end
command "b", "description for b", :listing => "foo" do; end
command "c" do;end
command "d", "" do;end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("help"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /Test listing/
str_output.string.should =~ /description for b/
str_output.string.should =~ /No description/
end
end
end

View file

@ -1,58 +0,0 @@
unless Object.const_defined? 'Pry'
$:.unshift File.expand_path '../../lib', __FILE__
require 'pry'
end
require 'bacon'
# Ensure we do not execute any rc files
Pry::RC_FILES.clear
Pry.color = false
Pry.should_load_rc = false
class Module
public :remove_const
end
class << Pry
alias_method :orig_reset_defaults, :reset_defaults
def reset_defaults
orig_reset_defaults
Pry.color = false
end
end
class InputTester
def initialize(*actions)
@orig_actions = actions.dup
@actions = actions
end
def readline(*)
@actions.shift
end
def rewind
@actions = @orig_actions.dup
end
end
class Pry
# null output class - doesn't write anywwhere.
class NullOutput
def self.puts(*) end
def self.string(*) end
end
end
CommandTester = Pry::CommandSet.new :test do
command "command1", "command 1 test" do
output.puts "command1"
end
command "command2", "command 2 test" do |arg|
output.puts arg
end
end

925
test/test_pry.rb Normal file
View file

@ -0,0 +1,925 @@
require 'helper'
puts "Ruby Version #{RUBY_VERSION}"
puts "Testing Pry #{Pry::VERSION}"
puts "With method_source version #{MethodSource::VERSION}"
puts "--"
describe Pry do
describe "open a Pry session on an object" do
describe "rep" do
before do
class Hello
end
end
after do
Object.send(:remove_const, :Hello)
end
# bug fix for https://github.com/banister/pry/issues/93
it 'should not leak pry constants into Object namespace' do
input_string = "CommandContext"
str_output = StringIO.new
o = Object.new
pry_tester = Pry.new(:input => StringIO.new(input_string),
:output => str_output,
:exception_handler => proc { |_, exception| @excep = exception },
:print => proc {}
).rep(o)
@excep.is_a?(NameError).should == true
end
it 'should set an ivar on an object' do
input_string = "@x = 10"
input = InputTester.new(input_string)
o = Object.new
pry_tester = Pry.new(:input => input, :output => Pry::NullOutput)
pry_tester.rep(o)
o.instance_variable_get(:@x).should == 10
end
it 'should make self evaluate to the receiver of the rep session' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("self"), :output => str_output)
pry_tester.rep(o)
str_output.string.should =~ /#{o.to_s}/
end
it 'should work with multi-line input' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("x = ", "1 + 4"), :output => str_output)
pry_tester.rep(o)
str_output.string.should =~ /5/
end
it 'should define a nested class under Hello and not on top-level or Pry' do
pry_tester = Pry.new(:input => InputTester.new("class Nested", "end"), :output => Pry::NullOutput)
pry_tester.rep(Hello)
Hello.const_defined?(:Nested).should == true
end
it 'should suppress output if input ends in a ";" and is an Exception object (single line)' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("Exception.new;"), :output => str_output)
pry_tester.rep(o)
str_output.string.should == ""
end
it 'should suppress output if input ends in a ";" (single line)' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("x = 5;"), :output => str_output)
pry_tester.rep(o)
str_output.string.should == ""
end
it 'should suppress output if input ends in a ";" (multi-line)' do
o = Object.new
str_output = StringIO.new
pry_tester = Pry.new(:input => InputTester.new("def self.blah", ":test", "end;"), :output => str_output)
pry_tester.rep(o)
str_output.string.should == ""
end
it 'should be able to evaluate exceptions normally' do
o = Exception.new
str_output = StringIO.new
was_called = false
pry_tester = Pry.new(:input => InputTester.new("self"),
:output => str_output,
:exception_handler => proc { was_called = true })
pry_tester.rep(o)
was_called.should == false
end
it 'should notice when exceptions are raised' do
o = Exception.new
str_output = StringIO.new
was_called = false
pry_tester = Pry.new(:input => InputTester.new("raise self"),
:output => str_output,
:exception_handler => proc { was_called = true })
pry_tester.rep(o)
was_called.should == true
end
end
describe "repl" do
describe "basic functionality" do
it 'should set an ivar on an object and exit the repl' do
input_strings = ["@x = 10", "exit"]
input = InputTester.new(*input_strings)
o = Object.new
pry_tester = Pry.start(o, :input => input, :output => Pry::NullOutput)
o.instance_variable_get(:@x).should == 10
end
end
describe "test loading rc files" do
before do
Pry.instance_variable_set(:@initial_session, true)
end
after do
Pry::RC_FILES.clear
Pry.config.should_load_rc = false
end
it "should run the rc file only once" do
Pry.config.should_load_rc = true
Pry::RC_FILES << File.expand_path("../testrc", __FILE__)
Pry.start(self, :input => StringIO.new("exit\n"), :output => Pry::NullOutput)
TEST_RC.should == [0]
Pry.start(self, :input => StringIO.new("exit\n"), :output => Pry::NullOutput)
TEST_RC.should == [0]
Object.remove_const(:TEST_RC)
end
it "should not run the rc file at all if Pry.config.should_load_rc is false" do
Pry.config.should_load_rc = false
Pry.start(self, :input => StringIO.new("exit\n"), :output => Pry::NullOutput)
Object.const_defined?(:TEST_RC).should == false
end
it "should not load the rc file if #repl method invoked" do
Pry.config.should_load_rc = true
Pry.new(:input => StringIO.new("exit\n"), :output => Pry::NullOutput).repl(self)
Object.const_defined?(:TEST_RC).should == false
Pry.config.should_load_rc = false
end
end
describe "nesting" do
after do
Pry.reset_defaults
Pry.color = false
end
it 'should nest properly' do
Pry.input = InputTester.new("pry", "pry", "pry", "\"nest:\#\{Pry.nesting.level\}\"", "exit_all")
str_output = StringIO.new
Pry.output = str_output
o = Object.new
pry_tester = o.pry
str_output.string.should =~ /nest:3/
end
end
describe "defining methods" do
it 'should define a method on the singleton class of an object when performing "def meth;end" inside the object' do
[Object.new, {}, []].each do |val|
str_input = StringIO.new("def hello;end")
Pry.new(:input => str_input, :output => StringIO.new).rep(val)
val.methods(false).map(&:to_sym).include?(:hello).should == true
end
end
it 'should define an instance method on the module when performing "def meth;end" inside the module' do
str_input = StringIO.new("def hello;end")
hello = Module.new
Pry.new(:input => str_input, :output => StringIO.new).rep(hello)
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
it 'should define an instance method on the class when performing "def meth;end" inside the class' do
str_input = StringIO.new("def hello;end")
hello = Class.new
Pry.new(:input => str_input, :output => StringIO.new).rep(hello)
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
it 'should define a method on the class of an object when performing "def meth;end" inside an immediate value or Numeric' do
# should include float in here, but test fails for some reason
# on 1.8.7, no idea why!
[:test, 0, true, false, nil].each do |val|
str_input = StringIO.new("def hello;end")
Pry.new(:input => str_input, :output => StringIO.new).rep(val)
val.class.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
end
end
describe "commands" do
it 'should run a command with no parameter' do
pry_tester = Pry.new
pry_tester.commands = CommandTester
pry_tester.input = InputTester.new("command1", "exit_all")
pry_tester.commands = CommandTester
str_output = StringIO.new
pry_tester.output = str_output
pry_tester.rep
str_output.string.should =~ /command1/
end
it 'should run a command with one parameter' do
pry_tester = Pry.new
pry_tester.commands = CommandTester
pry_tester.input = InputTester.new("command2 horsey", "exit_all")
pry_tester.commands = CommandTester
str_output = StringIO.new
pry_tester.output = str_output
pry_tester.rep
str_output.string.should =~ /horsey/
end
end
describe "Object#pry" do
after do
Pry.reset_defaults
Pry.color = false
end
it "should start a pry session on the receiver (first form)" do
Pry.input = InputTester.new("self", "exit")
str_output = StringIO.new
Pry.output = str_output
20.pry
str_output.string.should =~ /20/
end
it "should start a pry session on the receiver (second form)" do
Pry.input = InputTester.new("self", "exit")
str_output = StringIO.new
Pry.output = str_output
pry 20
str_output.string.should =~ /20/
end
it "should error if more than one argument is passed to Object#pry" do
lambda { pry(20, :input => Readline) }.should.raise ArgumentError
end
end
describe "Pry.binding_for" do
it 'should return TOPLEVEL_BINDING if parameter self is main' do
_main_ = lambda { TOPLEVEL_BINDING.eval('self') }
Pry.binding_for(_main_.call).is_a?(Binding).should == true
Pry.binding_for(_main_.call).should == TOPLEVEL_BINDING
Pry.binding_for(_main_.call).should == Pry.binding_for(_main_.call)
end
end
describe "test Pry defaults" do
after do
Pry.reset_defaults
Pry.color = false
end
describe "input" do
after do
Pry.reset_defaults
Pry.color = false
end
it 'should set the input default, and the default should be overridable' do
Pry.input = InputTester.new("5")
str_output = StringIO.new
Pry.output = str_output
Pry.new.rep
str_output.string.should =~ /5/
Pry.new(:input => InputTester.new("6")).rep
str_output.string.should =~ /6/
end
it 'should pass in the prompt if readline arity is 1' do
Pry.prompt = proc { "A" }
arity_one_input = Class.new do
attr_accessor :prompt
def readline(prompt)
@prompt = prompt
"exit"
end
end.new
Pry.start(self, :input => arity_one_input, :output => Pry::NullOutput)
arity_one_input.prompt.should == Pry.prompt.call
end
it 'should not pass in the prompt if the arity is 0' do
Pry.prompt = proc { "A" }
arity_zero_input = Class.new do
def readline
"exit"
end
end.new
lambda { Pry.start(self, :input => arity_zero_input, :output => Pry::NullOutput) }.should.not.raise Exception
end
it 'should not pass in the prompt if the arity is -1' do
Pry.prompt = proc { "A" }
arity_multi_input = Class.new do
attr_accessor :prompt
def readline(*args)
@prompt = args.first
"exit"
end
end.new
Pry.start(self, :input => arity_multi_input, :output => Pry::NullOutput)
arity_multi_input.prompt.should == nil
end
end
it 'should set the output default, and the default should be overridable' do
Pry.input = InputTester.new("5", "6", "7")
str_output = StringIO.new
Pry.output = str_output
Pry.new.rep
str_output.string.should =~ /5/
Pry.new.rep
str_output.string.should =~ /5\n.*6/
str_output2 = StringIO.new
Pry.new(:output => str_output2).rep
str_output2.string.should.not =~ /5\n.*6/
str_output2.string.should =~ /7/
end
describe "commands" do
it 'should interpolate ruby code into commands' do
klass = Pry::CommandSet.new do
command "hello", "", :keep_retval => true do |arg|
arg
end
end
$test_interpolation = "bing"
str_output = StringIO.new
Pry.new(:input => StringIO.new('hello #{$test_interpolation}'), :output => str_output, :commands => klass).rep
str_output.string.should =~ /bing/
$test_interpolation = nil
end
it 'should NOT interpolate ruby code into commands if :interpolate => false' do
klass = Pry::CommandSet.new do
command "hello", "", :keep_retval => true, :interpolate => false do |arg|
arg
end
end
$test_interpolation = "bing"
str_output = StringIO.new
Pry.new(:input => StringIO.new('hello #{$test_interpolation}'), :output => str_output, :commands => klass).rep
str_output.string.should =~ /test_interpolation/
$test_interpolation = nil
end
it 'should create a command with a space in its name' do
set = Pry::CommandSet.new do
command "hello baby", "" do
output.puts "hello baby command"
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hello baby", "exit-all"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /hello baby command/
end
it 'should create a command with a space in its name and pass an argument' do
set = Pry::CommandSet.new do
command "hello baby", "" do |arg|
output.puts "hello baby command #{arg}"
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hello baby john"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /hello baby command john/
end
it 'should create a regex command and be able to invoke it' do
set = Pry::CommandSet.new do
command /hello(.)/, "" do
c = captures.first
output.puts "hello#{c}"
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hello1"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /hello1/
end
it 'should create a regex command and pass captures into the args list before regular arguments' do
set = Pry::CommandSet.new do
command /hello(.)/, "" do |c1, a1|
output.puts "hello #{c1} #{a1}"
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hello1 baby"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /hello 1 baby/
end
it 'if a regex capture is missing it should be nil' do
set = Pry::CommandSet.new do
command /hello(.)?/, "" do |c1, a1|
output.puts "hello #{c1.inspect} #{a1}"
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("hello baby"), str_output) do
Pry.new(:commands => set).rep
end
str_output.string.should =~ /hello nil baby/
end
it 'should create a command in a nested context and that command should be accessible from the parent' do
redirect_pry_io(StringIO.new, StringIO.new) do
str_input = StringIO.new("@x=nil\ncd 7\n_pry_.commands.instance_eval {\ncommand('bing') { |arg| run arg }\n}\ncd ..\nbing ls\nexit")
str_output = StringIO.new
Pry.input = str_input
obj = Object.new
Pry.new(:output => str_output).repl(obj)
Pry.input = Readline
str_output.string.should =~ /@x/
end
end
it 'should define a command that keeps its return value' do
klass = Pry::CommandSet.new do
command "hello", "", :keep_retval => true do
:kept_hello
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("hello\n"), :output => str_output, :commands => klass).rep
str_output.string.should =~ /:kept_hello/
str_output.string.should =~ /=>/
end
it 'should define a command that does NOT keep its return value' do
klass = Pry::CommandSet.new do
command "hello", "", :keep_retval => false do
:kept_hello
end
end
str_output = StringIO.new
Pry.new(:input => StringIO.new("hello\n"), :output => str_output, :commands => klass).rep
(str_output.string =~ /:kept_hello/).should == nil
str_output.string !~ /=>/
end
it 'should set the commands default, and the default should be overridable' do
klass = Pry::CommandSet.new do
command "hello" do
output.puts "hello world"
end
end
Pry.commands = klass
str_output = StringIO.new
Pry.new(:input => InputTester.new("hello"), :output => str_output).rep
str_output.string.should =~ /hello world/
other_klass = Pry::CommandSet.new do
command "goodbye", "" do
output.puts "goodbye world"
end
end
str_output = StringIO.new
Pry.new(:input => InputTester.new("goodbye"), :output => str_output, :commands => other_klass).rep
str_output.string.should =~ /goodbye world/
end
it 'should inherit "help" command from Pry::CommandBase' do
klass = Pry::CommandSet.new do
command "h", "h command" do
end
end
klass.commands.keys.size.should == 3
klass.commands.keys.include?("help").should == true
klass.commands.keys.include?("install").should == true
klass.commands.keys.include?("h").should == true
end
it 'should inherit commands from Pry::Commands' do
klass = Pry::CommandSet.new Pry::Commands do
command "v" do
end
end
klass.commands.include?("nesting").should == true
klass.commands.include?("jump-to").should == true
klass.commands.include?("cd").should == true
klass.commands.include?("v").should == true
end
it 'should alias a command with another command' do
klass = Pry::CommandSet.new do
alias_command "help2", "help"
end
klass.commands["help2"].block.should == klass.commands["help"].block
end
it 'should change description of a command using desc' do
klass = Pry::CommandSet.new do; end
orig = klass.commands["help"].description
klass.instance_eval do
desc "help", "blah"
end
klass.commands["help"].description.should.not == orig
klass.commands["help"].description.should == "blah"
end
it 'should run a command from within a command' do
klass = Pry::CommandSet.new do
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 => klass).rep
str_output.string.should =~ /v command/
end
it 'should run a regex command from within a command' do
klass = Pry::CommandSet.new do
command /v(.*)?/ do |arg|
output.puts "v #{arg}"
end
command "run_v" do
run "vbaby"
end
end
str_output = StringIO.new
redirect_pry_io(InputTester.new("run_v"), str_output) do
Pry.new(:commands => klass).rep
end
str_output.string.should =~ /v baby/
end
it 'should run a command from within a command with arguments' do
klass = Pry::CommandSet.new do
command /v(\w+)/ do |arg1, arg2|
output.puts "v #{arg1} #{arg2}"
end
command "run_v_explicit_parameter" do
run "vbaby", "param"
end
command "run_v_embedded_parameter" do
run "vbaby param"
end
end
["run_v_explicit_parameter", "run_v_embedded_parameter"].each do |cmd|
str_output = StringIO.new
redirect_pry_io(InputTester.new(cmd), str_output) do
Pry.new(:commands => klass).rep
end
str_output.string.should =~ /v baby param/
end
end
it 'should enable an inherited method to access opts and output and target, due to instance_exec' do
klass = Pry::CommandSet.new do
command "v" do
output.puts "#{target.eval('self')}"
end
end
child_klass = Pry::CommandSet.new klass do
end
str_output = StringIO.new
Pry.new(:print => proc {}, :input => InputTester.new("v"),
:output => str_output, :commands => child_klass).rep("john")
str_output.string.rstrip.should == "john"
end
it 'should import commands from another command object' do
klass = Pry::CommandSet.new do
import_from Pry::Commands, "ls", "jump-to"
end
klass.commands.include?("ls").should == true
klass.commands.include?("jump-to").should == true
end
it 'should delete some inherited commands when using delete method' do
klass = Pry::CommandSet.new Pry::Commands do
command "v" do
end
delete "show-doc", "show-method"
delete "ls"
end
klass.commands.include?("nesting").should == true
klass.commands.include?("jump-to").should == true
klass.commands.include?("cd").should == true
klass.commands.include?("v").should == true
klass.commands.include?("show-doc").should == false
klass.commands.include?("show-method").should == false
klass.commands.include?("ls").should == false
end
it 'should override some inherited commands' do
klass = Pry::CommandSet.new Pry::Commands do
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 => klass).rep
str_output.string.rstrip.should == "jump-to the music"
str_output = StringIO.new
Pry.new(:input => InputTester.new("help"), :output => str_output, :commands => klass).rep
str_output.string.rstrip.should == "help to the music"
Pry.reset_defaults
Pry.color = false
end
end
it "should set the print default, and the default should be overridable" do
new_print = proc { |out, value| out.puts value }
Pry.print = new_print
Pry.new.print.should == Pry.print
str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => str_output).rep
str_output.string.should == "test\n"
str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => str_output,
:print => proc { |out, value| out.puts value.reverse }).rep
str_output.string.should == "tset\n"
Pry.new.print.should == Pry.print
str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => str_output).rep
str_output.string.should == "test\n"
end
describe "pry return values" do
it 'should return the target object' do
Pry.start(self, :input => StringIO.new("exit"), :output => Pry::NullOutput).should == self
end
it 'should return the parameter given to exit' do
Pry.start(self, :input => StringIO.new("exit 10"), :output => Pry::NullOutput).should == 10
end
it 'should return the parameter (multi word string) given to exit' do
Pry.start(self, :input => StringIO.new("exit \"john mair\""), :output => Pry::NullOutput).should == "john mair"
end
it 'should return the parameter (function call) given to exit' do
Pry.start(self, :input => StringIO.new("exit 'abc'.reverse"), :output => Pry::NullOutput).should == 'cba'
end
it 'should return the parameter (self) given to exit' do
Pry.start("carl", :input => StringIO.new("exit self"), :output => Pry::NullOutput).should == "carl"
end
end
describe "prompts" do
it 'should set the prompt default, and the default should be overridable (single prompt)' do
new_prompt = proc { "test prompt> " }
Pry.prompt = new_prompt
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt> "
new_prompt = proc { "A" }
pry_tester = Pry.new(:prompt => new_prompt)
pry_tester.prompt.should == new_prompt
pry_tester.select_prompt(true, 0).should == "A"
pry_tester.select_prompt(false, 0).should == "A"
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt> "
end
it 'should set the prompt default, and the default should be overridable (multi prompt)' do
new_prompt = [proc { "test prompt> " }, proc { "test prompt* " }]
Pry.prompt = new_prompt
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt* "
new_prompt = [proc { "A" }, proc { "B" }]
pry_tester = Pry.new(:prompt => new_prompt)
pry_tester.prompt.should == new_prompt
pry_tester.select_prompt(true, 0).should == "A"
pry_tester.select_prompt(false, 0).should == "B"
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(true, 0).should == "test prompt> "
Pry.new.select_prompt(false, 0).should == "test prompt* "
end
describe 'storing and restoring the prompt' do
before do
make = lambda do |name,i|
prompt = [ proc { "#{i}>" } , proc { "#{i+1}>" } ]
(class << prompt; self; end).send(:define_method, :inspect) { "<Prompt-#{name}>" }
prompt
end
@a , @b , @c = make[:a,0] , make[:b,1] , make[:c,2]
@pry = Pry.new :prompt => @a
end
it 'should have a prompt stack' do
@pry.push_prompt @b
@pry.push_prompt @c
@pry.prompt.should == @c
@pry.pop_prompt
@pry.prompt.should == @b
@pry.pop_prompt
@pry.prompt.should == @a
end
it 'should restore overridden prompts when returning from file-mode' do
pry = Pry.new :input => InputTester.new('shell-mode', 'shell-mode'),
:prompt => [ proc { 'P>' } ] * 2
pry.select_prompt(true, 0).should == "P>"
pry.re
pry.select_prompt(true, 0).should =~ /\Apry .* \$ \z/
pry.re
pry.select_prompt(true, 0).should == "P>"
end
it '#pop_prompt should return the popped prompt' do
@pry.push_prompt @b
@pry.push_prompt @c
@pry.pop_prompt.should == @c
@pry.pop_prompt.should == @b
end
it 'should not pop the last prompt' do
@pry.push_prompt @b
@pry.pop_prompt.should == @b
@pry.pop_prompt.should == @a
@pry.pop_prompt.should == @a
@pry.prompt.should == @a
end
describe '#prompt= should replace the current prompt with the new prompt' do
it 'when only one prompt on the stack' do
@pry.prompt = @b
@pry.prompt.should == @b
@pry.pop_prompt.should == @b
@pry.pop_prompt.should == @b
end
it 'when several prompts on the stack' do
@pry.push_prompt @b
@pry.prompt = @c
@pry.pop_prompt.should == @c
@pry.pop_prompt.should == @a
end
end
end
end
it 'should set the hooks default, and the default should be overridable' do
Pry.input = InputTester.new("exit")
Pry.hooks = {
:before_session => proc { |out,_| out.puts "HELLO" },
:after_session => proc { |out,_| out.puts "BYE" }
}
str_output = StringIO.new
Pry.new(:output => str_output).repl
str_output.string.should =~ /HELLO/
str_output.string.should =~ /BYE/
Pry.input.rewind
str_output = StringIO.new
Pry.new(:output => str_output,
:hooks => {
:before_session => proc { |out,_| out.puts "MORNING" },
:after_session => proc { |out,_| out.puts "EVENING" }
}
).repl
str_output.string.should =~ /MORNING/
str_output.string.should =~ /EVENING/
# try below with just defining one hook
Pry.input.rewind
str_output = StringIO.new
Pry.new(:output => str_output,
:hooks => {
:before_session => proc { |out,_| out.puts "OPEN" }
}
).repl
str_output.string.should =~ /OPEN/
Pry.input.rewind
str_output = StringIO.new
Pry.new(:output => str_output,
:hooks => {
:after_session => proc { |out,_| out.puts "CLOSE" }
}
).repl
str_output.string.should =~ /CLOSE/
Pry.reset_defaults
Pry.color = false
end
end
end
end
end