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

Merge branch 'wip.refactor'

Conflicts:
	lib/pry/completion.rb
	lib/pry/pry_instance.rb
	spec/commands/play_spec.rb
	spec/pry_defaults_spec.rb
This commit is contained in:
Conrad Irwin 2013-01-17 23:56:07 -08:00
commit 2c60f93b2d
37 changed files with 1317 additions and 1367 deletions

View file

@ -1 +1,3 @@
--markup markdown --private
--markup markdown
--private
--hide-void-return

View file

@ -10,7 +10,7 @@ class Pry
# The default hooks - display messages when beginning and ending Pry sessions.
DEFAULT_HOOKS = Pry::Hooks.new.add_hook(:before_session, :default) do |out, target, _pry_|
next if _pry_.quiet?
_pry_.run_command("whereami --quiet", "", target)
_pry_.run_command("whereami --quiet")
end
# The default print
@ -120,10 +120,9 @@ class Pry
_pry_.binding_stack.clear
throw(:breakout)
else
# Store the entire binding stack before popping. Useful for `cd -`.
if _pry_.command_state['cd'].nil?
_pry_.command_state['cd'] = OpenStruct.new
end
# Otherwise, saves current binding stack as old stack and pops last
# binding out of binding stack (the old stack still has that binding).
_pry_.command_state["cd"] ||= OpenStruct.new # FIXME
_pry_.command_state['cd'].old_stack = _pry_.binding_stack.dup
_pry_.binding_stack.pop
end
@ -246,6 +245,7 @@ rescue LoadError
end
require 'pry/version'
require 'pry/repl'
require 'pry/rbx_method'
require 'pry/rbx_path'
require 'pry/code'

View file

@ -13,7 +13,6 @@ class Pry
BANNER
def process
Pry.save_history if Pry.config.history.should_save
Kernel.exit target.eval(arg_string).to_i
end
end

View file

@ -32,7 +32,8 @@ class Pry
end
def process
@history = Pry::Code(Pry.history.to_a)
# The last value in history will be the 'hist' command itself
@history = Pry::Code(Pry.history.to_a[0..-2])
if opts.present?(:show)
@history = @history.between(opts[:show])
@ -113,10 +114,9 @@ class Pry
# further.
check_for_juxtaposed_replay(replay_sequence)
_pry_.input_stack.push _pry_.input
_pry_.input = StringIO.new(replay_sequence)
# eval_string << "#{@history.raw}\n"
# run "show-input" unless _pry_.complete_expression?(eval_string)
replay_sequence.lines.each do |line|
_pry_.eval line, :generated => true
end
end
# Checks +replay_sequence+ for the presence of neighboring replay calls.

View file

@ -16,8 +16,6 @@ class Pry
else
_pry_.push_prompt Pry::SHELL_PROMPT
_pry_.custom_completions = Pry::FILE_COMPLETIONS
Readline.completion_proc = Pry::InputCompleter.build_completion_proc target,
_pry_.instance_eval(&Pry::FILE_COMPLETIONS)
end
end
end

View file

@ -3,37 +3,38 @@
class Pry
module BondCompleter
def self.build_completion_proc(target, pry=nil, commands=[""])
if !@started
@started = true
start
end
Pry.current[:pry] = pry
proc{ |*a| Bond.agent.call(*a) }
def self.call(input, options)
Pry.current[:pry] = options[:pry]
Bond.agent.call(input)
end
def self.start
Bond.start(:eval_binding => lambda{ Pry.current[:pry].current_context })
Bond.start(:eval_binding => lambda{ Pry.current[:pry] && Pry.current[:pry].current_context })
Bond.complete(:on => /\A/) do |input|
Pry.commands.complete(input.line,
:pry_instance => Pry.current[:pry],
:target => Pry.current[:pry].current_context,
:command_set => Pry.current[:pry].commands)
end
self
end
end
# Implements tab completion for Readline in Pry
module InputCompleter
if Readline.respond_to?("basic_word_break_characters=")
Readline.basic_word_break_characters = " \t\n\"\\'`><=;|&{("
def self.call(input, options)
build_completion_proc(options[:target], options[:pry], options[:custom_completions]).call input
end
Readline.completion_append_character = nil
def self.start
if Readline.respond_to?("basic_word_break_characters=")
Readline.basic_word_break_characters = " \t\n\"\\'`><=;|&{("
end
Readline.completion_append_character = nil
self
end
ReservedWords = [
"BEGIN", "END",
@ -63,199 +64,200 @@ class Pry
# Return a new completion proc for use by Readline.
# @param [Binding] target The current binding context.
# @param [Array<String>] commands The array of Pry commands.
def self.build_completion_proc(target, pry=nil, commands=[""])
def self.call(input, options)
proc do |input|
custom_completions = options[:custom_completions] || []
# if there are multiple contexts e.g. cd 1/2/3
# get new target for 1/2 and find candidates for 3
path, input = build_path(input)
# if there are multiple contexts e.g. cd 1/2/3
# get new target for 1/2 and find candidates for 3
path, input = build_path(input)
unless path.call.empty?
target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, pry)
target = target.last
end
if path.call.empty?
target = options[:target]
else
target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, options[:pry])
target = target.last
end
begin
bind = target
begin
bind = target
case input
case input
# Complete stdlib symbols
# Complete stdlib symbols
when /^(\/[^\/]*\/)\.([^.]*)$/
# Regexp
receiver = $1
message = Regexp.quote($2)
when /^(\/[^\/]*\/)\.([^.]*)$/
# Regexp
receiver = $1
message = Regexp.quote($2)
candidates = Regexp.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
candidates = Regexp.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
when /^([^\]]*\])\.([^.]*)$/
# Array
receiver = $1
message = Regexp.quote($2)
when /^([^\]]*\])\.([^.]*)$/
# Array
receiver = $1
message = Regexp.quote($2)
candidates = Array.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
candidates = Array.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
when /^([^\}]*\})\.([^.]*)$/
# Proc or Hash
receiver = $1
message = Regexp.quote($2)
when /^([^\}]*\})\.([^.]*)$/
# Proc or Hash
receiver = $1
message = Regexp.quote($2)
candidates = Proc.instance_methods.collect(&:to_s)
candidates |= Hash.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
candidates = Proc.instance_methods.collect(&:to_s)
candidates |= Hash.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
when /^(:[^:.]*)$/
# Symbol
if Symbol.respond_to?(:all_symbols)
sym = Regexp.quote($1)
candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
candidates.grep(/^#{sym}/)
else
[]
end
when /^::([A-Z][^:\.\(]*)$/
# Absolute Constant or class methods
receiver = $1
candidates = Object.constants.collect(&:to_s)
candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
# Complete target symbols
when /^([A-Z][A-Za-z0-9]*)$/
# Constant
message = $1
begin
context = target.eval("self")
context = context.class unless context.respond_to? :constants
candidates = context.constants.collect(&:to_s)
rescue
candidates = []
end
candidates = candidates.grep(/^#{message}/).collect(&path)
when /^([A-Z].*)::([^:.]*)$/
# Constant or class methods
receiver = $1
message = Regexp.quote($2)
begin
candidates = eval("#{receiver}.constants.collect(&:to_s)", bind)
candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind)
rescue RescuableException
candidates = []
end
candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
when /^(:[^:.]+)\.([^.]*)$/
# Symbol
receiver = $1
message = Regexp.quote($2)
candidates = Symbol.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
# Numeric
receiver = $1
message = Regexp.quote($5)
begin
candidates = eval(receiver, bind).methods.collect(&:to_s)
rescue RescuableException
candidates = []
end
select_message(path, receiver, message, candidates)
when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/#
# Numeric(0xFFFF)
receiver = $1
message = Regexp.quote($2)
begin
candidates = eval(receiver, bind).methods.collect(&:to_s)
rescue RescuableException
candidates = []
end
select_message(path, receiver, message, candidates)
when /^(\$[^.]*)$/
# Global variables
regmessage = Regexp.new(Regexp.quote($1))
candidates = global_variables.collect(&:to_s).grep(regmessage)
when /^([^."].*)\.([^.]*)$/
# Variable
receiver = $1
message = Regexp.quote($2)
gv = eval("global_variables", bind).collect(&:to_s)
lv = eval("local_variables", bind).collect(&:to_s)
cv = eval("self.class.constants", bind).collect(&:to_s)
if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
# foo.func and foo is local var. OR
# Foo::Bar.func
begin
candidates = eval("#{receiver}.methods", bind).collect(&:to_s)
rescue RescuableException
candidates = []
end
else
# func1.func2
candidates = []
ObjectSpace.each_object(Module){|m|
begin
name = m.name.to_s
rescue RescuableException
name = ""
end
next if name != "IRB::Context" and
/^(IRB|SLex|RubyLex|RubyToken)/ =~ name
# jruby doesn't always provide #instance_methods() on each
# object.
if m.respond_to?(:instance_methods)
candidates.concat m.instance_methods(false).collect(&:to_s)
end
}
candidates.sort!
candidates.uniq!
end
select_message(path, receiver, message, candidates)
when /^\.([^.]*)$/
# Unknown(maybe String)
receiver = ""
message = Regexp.quote($1)
candidates = String.instance_methods(true).collect(&:to_s)
select_message(path, receiver, message, candidates)
when /^(:[^:.]*)$/
# Symbol
if Symbol.respond_to?(:all_symbols)
sym = Regexp.quote($1)
candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
candidates.grep(/^#{sym}/)
else
candidates = eval(
"methods | private_methods | local_variables | " \
"self.class.constants | instance_variables",
bind
).collect(&:to_s)
if eval("respond_to?(:class_variables)", bind)
candidates += eval("class_variables", bind).collect(&:to_s)
end
candidates = (candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/)
candidates.collect(&path)
[]
end
rescue RescuableException
[]
when /^::([A-Z][^:\.\(]*)$/
# Absolute Constant or class methods
receiver = $1
candidates = Object.constants.collect(&:to_s)
candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
# Complete target symbols
when /^([A-Z][A-Za-z0-9]*)$/
# Constant
message = $1
begin
context = target.eval("self")
context = context.class unless context.respond_to? :constants
candidates = context.constants.collect(&:to_s)
rescue
candidates = []
end
candidates = candidates.grep(/^#{message}/).collect(&path)
when /^([A-Z].*)::([^:.]*)$/
# Constant or class methods
receiver = $1
message = Regexp.quote($2)
begin
candidates = eval("#{receiver}.constants.collect(&:to_s)", bind)
candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind)
rescue RescuableException
candidates = []
end
candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
when /^(:[^:.]+)\.([^.]*)$/
# Symbol
receiver = $1
message = Regexp.quote($2)
candidates = Symbol.instance_methods.collect(&:to_s)
select_message(path, receiver, message, candidates)
when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
# Numeric
receiver = $1
message = Regexp.quote($5)
begin
candidates = eval(receiver, bind).methods.collect(&:to_s)
rescue RescuableException
candidates = []
end
select_message(path, receiver, message, candidates)
when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/#
# Numeric(0xFFFF)
receiver = $1
message = Regexp.quote($2)
begin
candidates = eval(receiver, bind).methods.collect(&:to_s)
rescue RescuableException
candidates = []
end
select_message(path, receiver, message, candidates)
when /^(\$[^.]*)$/
# Global variables
regmessage = Regexp.new(Regexp.quote($1))
candidates = global_variables.collect(&:to_s).grep(regmessage)
when /^([^."].*)\.([^.]*)$/
# Variable
receiver = $1
message = Regexp.quote($2)
gv = eval("global_variables", bind).collect(&:to_s)
lv = eval("local_variables", bind).collect(&:to_s)
cv = eval("self.class.constants", bind).collect(&:to_s)
if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
# foo.func and foo is local var. OR
# Foo::Bar.func
begin
candidates = eval("#{receiver}.methods", bind).collect(&:to_s)
rescue RescuableException
candidates = []
end
else
# func1.func2
candidates = []
ObjectSpace.each_object(Module){|m|
begin
name = m.name.to_s
rescue RescuableException
name = ""
end
next if name != "IRB::Context" and
/^(IRB|SLex|RubyLex|RubyToken)/ =~ name
# jruby doesn't always provide #instance_methods() on each
# object.
if m.respond_to?(:instance_methods)
candidates.concat m.instance_methods(false).collect(&:to_s)
end
}
candidates.sort!
candidates.uniq!
end
select_message(path, receiver, message, candidates)
when /^\.([^.]*)$/
# Unknown(maybe String)
receiver = ""
message = Regexp.quote($1)
candidates = String.instance_methods(true).collect(&:to_s)
select_message(path, receiver, message, candidates)
else
candidates = eval(
"methods | private_methods | local_variables | " \
"self.class.constants | instance_variables",
bind
).collect(&:to_s)
if eval("respond_to?(:class_variables)", bind)
candidates += eval("class_variables", bind).collect(&:to_s)
end
candidates = (candidates|ReservedWords|custom_completions).grep(/^#{Regexp.quote(input)}/)
candidates.collect(&path)
end
rescue RescuableException
[]
end
end

View file

@ -94,13 +94,6 @@ class Pry
end
end
# Get/Set the stack of input objects that a Pry instance switches
# to when its current input object encounters EOF.
# @return [Array] The array of input objects.
# @example
# Pry.config.input_stack = [StringIO.new("puts 'hello world'\nexit")]
attr_accessor :input_stack
# Get the array of Procs (or single Proc) to be used for the prompts by default by
# all Pry instances.
# Three parameters are passed into the prompt procs, (1) the

View file

@ -1,23 +1,23 @@
class Pry
# The History class is responsible for maintaining the user's input history, both
# internally and within Readline.
# The History class is responsible for maintaining the user's input history,
# both internally and within Readline.
class History
attr_accessor :loader, :saver, :pusher, :clearer
# @return [Fixnum] Number of lines in history when Pry first loaded.
attr_reader :original_lines
def initialize
def initialize(options={})
@history = []
@saved_lines = 0
@original_lines = 0
@file_path = options[:file_path]
restore_default_behavior
end
# Assign the default methods for loading, saving, pushing, and clearing.
def restore_default_behavior
@loader = method(:read_from_file)
@saver = method(:write_to_file)
@saver = method(:save_to_file)
@pusher = method(:push_to_readline)
@clearer = method(:clear_readline)
end
@ -29,16 +29,6 @@ class Pry
@pusher.call(line.chomp)
@history << line.chomp
end
@saved_lines = @original_lines = @history.length
end
# Write this session's history using `History.saver`.
# @return [Integer] The number of lines saved
def save
history_to_save = @history[@saved_lines..-1]
@saver.call(history_to_save)
@saved_lines = @history.length
history_to_save.length
end
# Add a line to the input history, ignoring blank and duplicate lines.
@ -48,18 +38,17 @@ class Pry
unless line.empty? || (@history.last && line == @history.last)
@pusher.call(line)
@history << line
@saver.call(line) if Pry.config.history.should_save
end
line
end
alias << push
# Clear all history. Anything the user entered before this point won't be
# saved, but anything they put in afterwards will still be appended to the
# history file on exit.
# Clear this session's history. This won't affect the contents of the
# history file.
def clear
@clearer.call
@history = []
@saved_lines = 0
end
# @return [Fixnum] The number of lines in history.
@ -67,6 +56,7 @@ class Pry
@history.count
end
# @return [Fixnum] The number of lines in history from just this session.
def session_line_count
@history.count - @original_lines
end
@ -79,33 +69,16 @@ class Pry
end
private
# The default loader. Yields lines from `Pry.history.config.file`.
def read_from_file
begin
history_file = File.expand_path(Pry.config.history.file)
if File.exists?(history_file)
File.foreach(history_file) { |line| yield(line) }
end
rescue => error
unless error.message.empty?
warn "History file not loaded, received an error: #{error.message}"
end
end
end
filename = File.expand_path(Pry.config.history.file)
# The default saver. Appends the given lines to `Pry.history.config.file`.
# @param [Array<String>] lines
def write_to_file(lines)
history_file = File.expand_path(Pry.config.history.file)
begin
File.open(history_file, 'a') do |f|
lines.each { |ln| f.puts ln }
end
rescue Errno::EACCES
# We should probably create an option Pry.show_warnings?!?!?!
warn 'Unable to write to your history file, history not saved'
if File.exists?(filename)
File.foreach(filename) { |line| yield(line) }
end
rescue => error
warn "History file not loaded: #{error.message}"
end
# The default pusher. Appends the given line to Readline::HISTORY.
@ -118,5 +91,26 @@ class Pry
def clear_readline
Readline::HISTORY.shift until Readline::HISTORY.empty?
end
# The default saver. Appends the given line to `Pry.history.config.file`.
def save_to_file(line)
history_file.puts line if history_file
end
# The history file, opened for appending.
def history_file
if defined?(@history_file)
@history_file
else
@history_file = File.open(file_path, 'a').tap { |f| f.sync = true }
end
rescue Errno::EACCES
warn 'History not saved; unable to open your history file for writing.'
@history_file = false
end
def file_path
@file_path || Pry.config.history.file
end
end
end

View file

@ -407,5 +407,10 @@ class Pry
"#{move_up}#{prompt}#{colorize_code(code)}#{whitespace}#{move_down}"
end
# Given the current Pry environment, should we try to correct indentation?
def should_correct_indentation?
Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
end
end
end

View file

@ -59,7 +59,7 @@ class Pry
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
delegate_accessors :@config, :input, :output, :commands, :prompt, :print, :exception_handler,
:hooks, :color, :pager, :editor, :memory_size, :input_stack, :extra_sticky_locals
:hooks, :color, :pager, :editor, :memory_size, :extra_sticky_locals
end
# Load the given file in the context of `Pry.toplevel_binding`
@ -119,7 +119,7 @@ class Pry
end
# Start a Pry REPL.
# This method also loads the ~/.pryrc and ./.pryrc as necessary
# This method also loads `~/.pryrc` and `./.pryrc` as necessary the
# first time it is invoked.
# @param [Object, Binding] target The receiver of the Pry session
# @param [Hash] options
@ -135,35 +135,24 @@ class Pry
return
end
target = Pry.binding_for(target || toplevel_binding)
options[:target] = Pry.binding_for(target || toplevel_binding)
initial_session_setup
# create the Pry instance to manage the session
pry_instance = new(options)
# Unless we were given a backtrace, save the current one
if options[:backtrace].nil?
options[:backtrace] = caller
# save backtrace
pry_instance.backtrace = caller
# if Pry was started via binding.pry, elide that from the backtrace.
pry_instance.backtrace.shift if pry_instance.backtrace.first =~ /pry.*core_extensions.*pry/
# yield the binding_stack to the hook for modification
pry_instance.exec_hook(:when_started, target, options, pry_instance)
if !pry_instance.binding_stack.empty?
head = pry_instance.binding_stack.pop
else
head = target
# If Pry was started via `binding.pry`, elide that from the backtrace
if options[:backtrace].first =~ /pry.*core_extensions.*pry/
options[:backtrace].shift
end
end
# Clear the line before starting Pry. This fixes the issue discussed here:
# https://github.com/pry/pry/issues/566
if Pry.config.auto_indent
Kernel.print Pry::Helpers::BaseHelpers.windows_ansi? ? "\e[0F" : "\e[0G"
end
driver = options[:driver] || Pry::REPL
# Enter the matrix
pry_instance.repl(head)
driver.start(options)
rescue Pry::TooSafeException
puts "ERROR: Pry cannot work with $SAFE > 0"
raise
@ -202,11 +191,6 @@ class Pry
Pry.history.load
end
# Save new lines of Readline history if required.
def self.save_history
Pry.history.save
end
# @return [Boolean] Whether this is the first time a Pry session has
# been started since loading the Pry class.
def self.initial_session?
@ -276,7 +260,6 @@ class Pry
config.exception_whitelist = DEFAULT_EXCEPTION_WHITELIST
config.default_window_size = 5
config.hooks = DEFAULT_HOOKS
config.input_stack = []
config.color = Helpers::BaseHelpers.use_ansi_codes?
config.pager = true
config.system = DEFAULT_SYSTEM
@ -292,9 +275,9 @@ class Pry
config.output_prefix = "=> "
if defined?(Bond) && Readline::VERSION !~ /editline/i
config.completer = Pry::BondCompleter
config.completer = Pry::BondCompleter.start
else
config.completer = Pry::InputCompleter
config.completer = Pry::InputCompleter.start
end
config.gist ||= OpenStruct.new

View file

@ -22,19 +22,18 @@ require "pry/indent"
# * the IRC channel, which is #pry on the Freenode network
#
class Pry
attr_accessor :input
attr_accessor :output
attr_accessor :commands
attr_accessor :print
attr_accessor :exception_handler
attr_accessor :input_stack
attr_accessor :quiet
alias :quiet? :quiet
attr_accessor :custom_completions
attr_accessor :binding_stack
attr_accessor :eval_string
attr_accessor :last_result
attr_accessor :last_file
@ -54,45 +53,66 @@ class Pry
# This is exposed via Pry::Command#state.
attr_reader :command_state
# Special treatment for hooks as we want to alert people of the
# changed API
attr_reader :hooks
attr_reader :exit_value
# FIXME:
# This is a hack to alert people of the new API.
# @param [Pry::Hooks] v Only accept `Pry::Hooks` now!
def hooks=(v)
if v.is_a?(Hash)
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
@hooks = Pry::Hooks.from_hash(v)
attr_reader :hooks # Special treatment as we want to alert people of the
# changed API.
# FIXME: This is a hack to alert people of the new API.
# @param [Pry::Hooks] hooks
def hooks=(hooks)
if hooks.is_a?(Hash)
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object " \
"instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
@hooks = Pry::Hooks.from_hash(hooks)
else
@hooks = v
@hooks = hooks
end
end
# Create a new `Pry` object.
# @param [Hash] options The optional configuration parameters.
# @option options [#readline] :input The object to use for input.
# @option options [#puts] :output The object to use for output.
# @option options [Pry::CommandBase] :commands The object to use for commands.
# @option options [Hash] :hooks The defined hook Procs
# @option options [Array<Proc>] :prompt The array of Procs to use for the prompts.
# @option options [Proc] :print The Proc to use for the 'print'
# @option options [Boolean] :quiet If true, omit the whereami banner when starting.
# component of the REPL. (see print.rb)
# Create a new {Pry} instance.
# @param [Hash] options
# @option options [#readline] :input
# The object to use for input.
# @option options [#puts] :output
# The object to use for output.
# @option options [Pry::CommandBase] :commands
# The object to use for commands.
# @option options [Hash] :hooks
# The defined hook Procs.
# @option options [Array<Proc>] :prompt
# The array of Procs to use for prompts.
# @option options [Proc] :print
# The Proc to use for printing return values.
# @option options [Boolean] :quiet
# Omit the `whereami` banner when starting.
# @option options [Array<String>] :backtrace
# The backtrace of the session's `binding.pry` line, if applicable.
# @option options [Object] :target
# The initial context for this session.
def initialize(options={})
refresh(options)
@binding_stack = []
@indent = Pry::Indent.new
@command_state = {}
@eval_string = ""
@backtrace = options[:backtrace] || caller
refresh_config(options)
push_initial_binding(options[:target])
set_last_result nil
@input_array << nil # add empty input so _in_ and _out_ match
# yield the binding_stack to the hook for modification
exec_hook(:when_started, options[:target], options, self)
end
# Refresh the Pry instance settings from the Pry class.
# Allows options to be specified to override settings from Pry class.
# @param [Hash] options The options to override Pry class settings
# for this instance.
def refresh(options={})
def refresh_config(options={})
defaults = {}
attributes = [
:input, :output, :commands, :print, :quiet,
@ -104,8 +124,6 @@ class Pry
defaults[attribute] = Pry.send attribute
end
defaults[:input_stack] = Pry.input_stack.dup
defaults.merge!(options).each do |key, value|
send("#{key}=", value) if respond_to?("#{key}=")
end
@ -113,11 +131,25 @@ class Pry
true
end
# Initialize this instance by pushing its initial context into the binding
# stack. If no target is given, start at the top level.
def push_initial_binding(target=nil)
push_binding(target || Pry.toplevel_binding)
end
# The currently active `Binding`.
# @return [Binding] The currently active `Binding` for the session.
def current_context
def current_binding
binding_stack.last
end
alias current_context current_binding # support previous API
# Push a binding for the given object onto the stack. If this instance is
# currently stopped, mark it as usable again.
def push_binding(object)
@stopped = false
binding_stack << Pry.binding_for(object)
end
# The current prompt.
# This is the prompt at the top of the prompt stack.
@ -139,6 +171,17 @@ class Pry
end
end
# Generate completions.
# @param [String] input What the user has typed so far
# @return [Array<String>] Possible completions
def complete(input)
Pry.critical_section do
Pry.config.completer.call(input, :target => current_binding,
:pry => self,
:custom_completions => instance_eval(&custom_completions))
end
end
# Injects a local variable into the provided binding.
# @param [String] name The name of the local to inject.
# @param [Object] value The value to set the local to.
@ -162,11 +205,10 @@ class Pry
@output_array = Pry::HistoryArray.new(size)
end
# Inject all the sticky locals into the `target` binding.
# @param [Binding] target
def inject_sticky_locals(target)
# Inject all the sticky locals into the current binding.
def inject_sticky_locals!
sticky_locals.each_pair do |name, value|
inject_local(name, value, target)
inject_local(name, value, current_binding)
end
end
@ -193,139 +235,139 @@ class Pry
}.merge(extra_sticky_locals)
end
# Initialize the repl session.
# @param [Binding] target The target binding for the session.
def repl_prologue(target)
exec_hook :before_session, output, target, self
set_last_result(nil, target)
@input_array << nil # add empty input so _in_ and _out_ match
binding_stack.push target
# Reset the current eval string. If the user has entered part of a multiline
# expression, this discards that input.
def reset_eval_string
@eval_string = ""
end
# Clean-up after the repl session.
# @param [Binding] target The target binding for the session.
def repl_epilogue(target)
exec_hook :after_session, output, target, self
# Pass a line of input to Pry.
#
# This is the equivalent of `Binding#eval` but with extra Pry!
#
# In particular:
# 1. Pry commands will be executed immediately if the line matches.
# 2. Partial lines of input will be queued up until a complete expression has
# been accepted.
# 3. Output is written to {#output} in pretty colours, not returned.
#
# Once this method has raised an exception or returned false, this instance
# is no longer usable. {#exit_value} will return the session's breakout
# value if applicable.
#
# @param [String?] line The line of input; `nil` if the user types `<Ctrl-D>`
# @option options [Boolean] :generated Whether this line was generated automatically.
# Generated lines are not stored in history.
# @return [Boolean] Is Pry ready to accept more input?
# @raise [Exception] If the user uses the `raise-up` command, this method
# will raise that exception.
def eval(line, options={})
return false if @stopped
binding_stack.pop
Pry.save_history if Pry.config.history.should_save
end
# Start a read-eval-print-loop.
# If no parameter is given, default to top-level (main).
# @param [Object, Binding] target The receiver of the Pry session
# @return [Object] The target of the Pry session or an explictly given
# return value. If given return value is `nil` or no return value
# is specified then `target` will be returned.
# @example
# Pry.new.repl(Object.new)
def repl(target=TOPLEVEL_BINDING)
target = Pry.binding_for(target)
repl_prologue(target)
break_data = nil
exit_value = nil
exception = catch(:raise_up) do
break_data = catch(:breakout) do
loop do
throw(:breakout) if binding_stack.empty?
rep(binding_stack.last)
end
exit_value = catch(:breakout) do
handle_line(line, options)
# We use 'return !@stopped' here instead of 'return true' so that if
# handle_line has stopped this pry instance (e.g. by opening _pry_.repl and
# then popping all the bindings) we still exit immediately.
return !@stopped
end
exception = false
end
@stopped = true
@exit_value = exit_value
# TODO: make this configurable?
raise exception if exception
break_data
ensure
repl_epilogue(target)
return false
end
# Perform a read-eval-print.
# If no parameter is given, default to top-level (main).
# @param [Object, Binding] target The receiver of the read-eval-print
# @example
# Pry.new.rep(Object.new)
def rep(target=TOPLEVEL_BINDING)
target = Pry.binding_for(target)
result = re(target)
Pry.critical_section do
show_result(result)
def handle_line(line, options)
if line.nil?
Pry.config.control_d_handler.call(@eval_string, self)
return
end
end
# Perform a read-eval
# If no parameter is given, default to top-level (main).
# @param [Object, Binding] target The receiver of the read-eval-print
# @return [Object] The result of the eval or an `Exception` object in case of
# error. In the latter case, you can check whether the exception was raised
# or is just the result of the expression using #last_result_is_exception?
# @example
# Pry.new.re(Object.new)
def re(target=TOPLEVEL_BINDING)
target = Pry.binding_for(target)
ensure_correct_encoding!(line)
Pry.history << line unless options[:generated]
# It's not actually redundant to inject them continually as we may have
# moved into the scope of a new Binding (e.g the user typed `cd`).
inject_sticky_locals(target)
code = r(target)
evaluate_ruby(code, target)
rescue RescuableException => e
self.last_exception = e
e
end
# Perform a read.
# If no parameter is given, default to top-level (main).
# This is a multi-line read; so the read continues until a valid
# 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, eval_string="")
target = Pry.binding_for(target)
@suppress_output = false
loop do
begin
# eval_string will probably be mutated by this method
retrieve_line(eval_string, target)
rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
output.puts "Error: #{e.message}"
inject_sticky_locals!
begin
if !process_command_safely(line.lstrip)
@eval_string << "#{line.chomp}\n" unless line.empty?
end
rescue RescuableException => e
self.last_exception = e
result = e
begin
break if Pry::Code.complete_expression?(eval_string)
rescue SyntaxError => e
exception_handler.call(output, e.extend(UserError), self)
eval_string = ""
Pry.critical_section do
show_result(result)
end
return
end
if eval_string =~ /;\Z/ || eval_string.empty? || eval_string =~ /\A *#.*\n\z/
@suppress_output = true
end
# This hook is supposed to be executed after each line of ruby code
# has been read (regardless of whether eval_string is yet a complete expression)
exec_hook :after_read, eval_string, self
eval_string
begin
complete_expr = Pry::Code.complete_expression?(@eval_string)
rescue SyntaxError => e
output.puts "SyntaxError: #{e.message.sub(/.*syntax error, */m, '')}"
reset_eval_string
end
if complete_expr
if @eval_string =~ /;\Z/ || @eval_string.empty? || @eval_string =~ /\A *#.*\n\z/
@suppress_output = true
end
begin
# Reset eval string, in case we're evaluating Ruby that does something
# like open a nested REPL on this instance.
eval_string = @eval_string
reset_eval_string
result = evaluate_ruby(eval_string)
rescue RescuableException => e
self.last_exception = e
result = e
end
Pry.critical_section do
show_result(result)
end
end
throw(:breakout) if current_binding.nil?
end
private :handle_line
# @deprecated Use `Pry::REPL.new(pry, :target => target).start` instead.
def repl(target = nil)
@@repl_warning ||= (warn Pry::Helpers::CommandHelpers.unindent(<<-S); true)
DEPRECATION: Pry#repl is deprecated. Instead, use
Pry::REPL.new(pry, :target => target).start
where pry is the Pry instance you called #repl on and target is the
optional target parameter of #repl.
Call stack:
#{caller.join("\n" + (' ' * 8))}
S
Pry::REPL.new(self, :target => target).start
end
def evaluate_ruby(code, target = binding_stack.last)
target = Pry.binding_for(target)
inject_sticky_locals(target)
def evaluate_ruby(code)
inject_sticky_locals!
exec_hook :before_eval, code, self
result = target.eval(code, Pry.eval_path, Pry.current_line)
set_last_result(result, target, code)
result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
set_last_result(result, code)
ensure
update_input_history(code)
exec_hook :after_eval, result, self
@ -355,96 +397,28 @@ class Pry
end
end
def should_force_encoding?(eval_string, val)
eval_string.empty? && val.respond_to?(:encoding) && val.encoding != eval_string.encoding
end
private :should_force_encoding?
# Read and process a line of input -- check for ^D, determine which prompt to
# use, rewrite the indentation if `Pry.config.auto_indent` is enabled, and,
# if the line is a command, process it and alter the eval_string accordingly.
# This method should not need to be invoked directly.
#
# @param [String] eval_string The cumulative lines of input.
# @param [Binding] target The target of the session.
# @return [String] The line received.
def retrieve_line(eval_string, target)
@indent.reset if eval_string.empty?
current_prompt = select_prompt(eval_string, target)
completion_proc = Pry.config.completer.build_completion_proc(target, self,
instance_eval(&custom_completions))
safe_completion_proc = proc{ |*a| Pry.critical_section{ completion_proc.call(*a) } }
indentation = Pry.config.auto_indent ? @indent.current_prefix : ''
begin
val = readline("#{current_prompt}#{indentation}", safe_completion_proc)
# Handle <Ctrl+C> like Bash, empty the current input buffer but do not quit.
# This is only for ruby-1.9; other versions of ruby do not let you send Interrupt
# from within Readline.
rescue Interrupt
output.puts ""
eval_string.replace("")
return
end
# invoke handler if we receive EOF character (^D)
if !val
output.puts ""
Pry.config.control_d_handler.call(eval_string, self)
return
end
# Change the eval_string into the input encoding (Issue 284)
# TODO: This wouldn't be necessary if the eval_string was constructed from
# input strings only.
if should_force_encoding?(eval_string, val)
eval_string.force_encoding(val.encoding)
end
if Pry.config.auto_indent && !input.is_a?(StringIO)
original_val = "#{indentation}#{val}"
indented_val = @indent.indent(val)
if output.tty? && Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
output.print @indent.correct_indentation(current_prompt, indented_val, original_val.length - indented_val.length)
output.flush
end
else
indented_val = val
end
# Check this before processing the line, because a command might change
# Pry's input.
interactive = !input.is_a?(StringIO)
begin
if !process_command(val, eval_string, target)
eval_string << "#{indented_val.chomp}\n" unless val.empty?
end
ensure
Pry.history << indented_val if interactive
# Force `eval_string` into the encoding of `val`. [Issue #284]
def ensure_correct_encoding!(val)
if @eval_string.empty? &&
val.respond_to?(:encoding) &&
val.encoding != @eval_string.encoding
@eval_string.force_encoding(val.encoding)
end
end
private :ensure_correct_encoding!
# If the given line is a valid command, process it in the context of the
# current `eval_string` and context.
# This method should not need to be invoked directly.
# current `eval_string` and binding.
# @param [String] val The line to process.
# @param [String] eval_string The cumulative lines of input.
# @param [Binding] target The target of the Pry session.
# @return [Boolean] `true` if `val` is a command, `false` otherwise
def process_command(val, eval_string = '', target = binding_stack.last)
def process_command(val)
val = val.chomp
result = commands.process_line(val, {
:target => target,
result = commands.process_line(val,
:target => current_binding,
:output => output,
:eval_string => eval_string,
:eval_string => @eval_string,
:pry_instance => self
})
)
# set a temporary (just so we can inject the value we want into eval_string)
Pry.current[:pry_cmd_result] = result
@ -457,7 +431,7 @@ class Pry
# the command that was invoked was non-void (had a return value) and so we make
# the value of the current expression equal to the return value
# of the command.
eval_string.replace "::Pry.current[:pry_cmd_result].retval\n"
@eval_string.replace "::Pry.current[:pry_cmd_result].retval\n"
end
true
else
@ -465,17 +439,26 @@ class Pry
end
end
# Same as process_command, but outputs exceptions to {#output} instead of
# raising.
# @param [String] val The line to process.
# @return [Boolean] `true` if `val` is a command, `false` otherwise
def process_command_safely(val)
process_command(val)
rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
output.puts "Error: #{e.message}"
true
end
# Run the specified command.
# @param [String] val The command (and its params) to execute.
# @param [String] eval_string The current input buffer.
# @param [Binding] target The binding to use..
# @return [Pry::Command::VOID_VALUE]
# @example
# pry_instance.run_command("ls -m")
def run_command(val, eval_string = "", target = binding_stack.last)
def run_command(val)
commands.process_line(val,
:eval_string => eval_string,
:target => target,
:eval_string => @eval_string,
:target => current_binding,
:pry_instance => self,
:output => output
)
@ -504,9 +487,8 @@ class Pry
# Set the last result of an eval.
# This method should not need to be invoked directly.
# @param [Object] result The result.
# @param [Binding] target The binding to set `_` on.
# @param [String] code The code that was run.
def set_last_result(result, target, code="")
def set_last_result(result, code="")
@last_result_is_exception = false
@output_array << result
@ -555,81 +537,6 @@ class Pry
@last_result_is_exception
end
# Manage switching of input objects on encountering EOFErrors
def handle_read_errors
should_retry = true
exception_count = 0
begin
yield
rescue EOFError
if input_stack.empty?
self.input = Pry.config.input
if !should_retry
output.puts "Error: Pry ran out of things to read from! Attempting to break out of REPL."
throw(:breakout)
end
should_retry = false
else
self.input = input_stack.pop
end
retry
# Interrupts are handled in r() because they need to tweak eval_string
# TODO: Refactor this baby.
rescue Interrupt
raise
# If we get a random error when trying to read a line we don't want to automatically
# retry, as the user will see a lot of error messages scroll past and be unable to do
# anything about it.
rescue RescuableException => e
puts "Error: #{e.message}"
output.puts e.backtrace
exception_count += 1
if exception_count < 5
retry
end
puts "FATAL: Pry failed to get user input using `#{input}`."
puts "To fix this you may be able to pass input and output file descriptors to pry directly. e.g."
puts " Pry.config.input = STDIN"
puts " Pry.config.output = STDOUT"
puts " binding.pry"
throw(:breakout)
end
end
private :handle_read_errors
# Returns the next line of input to be used by the pry instance.
# This method should not need to be invoked directly.
# @param [String] current_prompt The prompt to use for input.
# @return [String] The next line of input.
def readline(current_prompt="> ", completion_proc=nil)
handle_read_errors do
if defined? Coolline and input.is_a? Coolline
input.completion_proc = proc do |cool|
completions = completion_proc.call cool.completed_word
completions.compact
end
elsif input.respond_to? :completion_proc=
input.completion_proc = completion_proc
end
if input == Readline
input.readline(current_prompt, false) # false since we'll add it manually
elsif defined? Coolline and input.is_a? Coolline
input.readline(current_prompt)
else
if input.method(:readline).arity == 1
input.readline(current_prompt)
else
input.readline
end
end
end
end
# Whether the print proc should be invoked.
# Currently only invoked if the output is not suppressed.
# @return [Boolean] Whether the print proc should be invoked.
@ -638,18 +545,15 @@ class Pry
end
# Returns the appropriate prompt to use.
# This method should not need to be invoked directly.
# @param [String] eval_string The current input buffer.
# @param [Binding] target The target Binding of the Pry session.
# @return [String] The prompt.
def select_prompt(eval_string, target)
target_self = target.eval('self')
def select_prompt
object = current_binding.eval('self')
open_token = @indent.open_delimiters.any? ? @indent.open_delimiters.last :
@indent.stack.last
c = OpenStruct.new(
:object => target_self,
:object => object,
:nesting_level => binding_stack.size - 1,
:open_token => open_token,
:session_line => Pry.history.session_line_count + 1,
@ -658,8 +562,8 @@ class Pry
:_pry_ => self,
:binding_stack => binding_stack,
:input_array => input_array,
:eval_string => eval_string,
:cont => !eval_string.empty?)
:eval_string => @eval_string,
:cont => !@eval_string.empty?)
Pry.critical_section do
# If input buffer is empty then use normal prompt

203
lib/pry/repl.rb Normal file
View file

@ -0,0 +1,203 @@
require 'forwardable'
class Pry
class REPL
extend Forwardable
def_delegators :@pry, :input, :output
# @return [Pry] The instance of {Pry} that the user is controlling.
attr_accessor :pry
# Instantiate a new {Pry} instance with the given options, then start a
# {REPL} instance wrapping it.
# @option options See {Pry#initialize}
def self.start(options)
new(Pry.new(options)).start
end
# Create an instance of {REPL} wrapping the given {Pry}.
# @param [Pry] pry The instance of {Pry} that this {REPL} will control.
# @param [Hash] options Options for this {REPL} instance.
# @option options [Object] :target The initial target of the session.
def initialize(pry, options = {})
@pry = pry
@indent = Pry::Indent.new
if options[:target]
@pry.push_binding options[:target]
end
end
# Start the read-eval-print loop.
# @return [Object?] If the session throws `:breakout`, return the value
# thrown with it.
# @raise [Exception] If the session throws `:raise_up`, raise the exception
# thrown with it.
def start
prologue
repl
ensure
epilogue
end
private
# Set up the repl session.
# @return [void]
def prologue
pry.exec_hook :before_session, pry.output, pry.current_binding, pry
# Clear the line before starting Pry. This fixes issue #566.
if Pry.config.correct_indent
Kernel.print Pry::Helpers::BaseHelpers.windows_ansi? ? "\e[0F" : "\e[0G"
end
end
# The actual read-eval-print loop.
#
# The {REPL} instance is responsible for reading and looping, whereas the
# {Pry} instance is responsible for evaluating user input and printing
# return values and command output.
#
# @return [Object?] If the session throws `:breakout`, return the value
# thrown with it.
# @raise [Exception] If the session throws `:raise_up`, raise the exception
# thrown with it.
def repl
loop do
case val = read
when :control_c
output.puts ""
pry.reset_eval_string
when :no_more_input
output.puts "" if output.tty?
break
else
output.puts "" if val.nil? && output.tty?
return pry.exit_value unless pry.eval(val)
end
end
end
# Clean up after the repl session.
# @return [void]
def epilogue
pry.exec_hook :after_session, pry.output, pry.current_binding, pry
end
# Read a line of input from the user.
# @return [String] The line entered by the user.
# @return [nil] On `<Ctrl-D>`.
# @return [:control_c] On `<Ctrl+C>`.
# @return [:no_more_input] On EOF.
def read
@indent.reset if pry.eval_string.empty?
current_prompt = pry.select_prompt
indentation = Pry.config.auto_indent ? @indent.current_prefix : ''
begin
val = read_line("#{current_prompt}#{indentation}")
# Handle <Ctrl+C> like Bash: empty the current input buffer, but don't
# quit. This is only for MRI 1.9; other versions of Ruby don't let you
# send Interrupt from within Readline.
rescue Interrupt
val = :control_c
end
# Return nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
return val unless String === val
if Pry.config.auto_indent
original_val = "#{indentation}#{val}"
indented_val = @indent.indent(val)
if output.tty? && @indent.should_correct_indentation?
output.print @indent.correct_indentation(
current_prompt, indented_val,
original_val.length - indented_val.length
)
output.flush
end
else
indented_val = val
end
indented_val
end
# Manage switching of input objects on encountering `EOFError`s.
# @return [Object] Whatever the given block returns.
# @return [:no_more_input] Indicates that no more input can be read.
def handle_read_errors
should_retry = true
exception_count = 0
begin
yield
rescue EOFError
pry.input = Pry.config.input
if !should_retry
output.puts "Error: Pry ran out of things to read from! " \
"Attempting to break out of REPL."
return :no_more_input
end
should_retry = false
retry
rescue Interrupt
raise
# If we get a random error when trying to read a line we don't want to
# automatically retry, as the user will see a lot of error messages
# scroll past and be unable to do anything about it.
rescue RescuableException => e
puts "Error: #{e.message}"
output.puts e.backtrace
exception_count += 1
if exception_count < 5
retry
end
puts "FATAL: Pry failed to get user input using `#{input}`."
puts "To fix this you may be able to pass input and output file " \
"descriptors to pry directly. e.g."
puts " Pry.config.input = STDIN"
puts " Pry.config.output = STDOUT"
puts " binding.pry"
return :no_more_input
end
end
# Returns the next line of input to be sent to the {Pry} instance.
# @param [String] current_prompt The prompt to use for input.
# @return [String?] The next line of input, or `nil` on <Ctrl-D>.
def read_line(current_prompt)
handle_read_errors do
if defined? Coolline and input.is_a? Coolline
input.completion_proc = proc do |cool|
completions = @pry.complete cool.completed_word
completions.compact
end
elsif input.respond_to? :completion_proc=
input.completion_proc = proc do |input|
@pry.complete input
end
end
if input == Readline
input.readline(current_prompt, false) # false since we'll add it manually
elsif defined? Coolline and input.is_a? Coolline
input.readline(current_prompt)
else
if input.method(:readline).arity == 1
input.readline(current_prompt)
else
input.readline
end
end
end
end
end
end

View file

@ -15,7 +15,8 @@ class Pry
full_name = File.expand_path(file_name)
raise RuntimeError, "No such file: #{full_name}" if !File.exists?(full_name)
@content = StringIO.new(File.read(full_name))
define_additional_commands
@content = File.read(full_name)
end
# Switch to interactive mode, i.e take input from the user
@ -25,19 +26,24 @@ class Pry
_pry_.input = Pry.config.input
_pry_.print = Pry.config.print
_pry_.exception_handler = Pry.config.exception_handler
Pry::REPL.new(_pry_).start
end
# Switch to non-interactive mode. Essentially
# this means there is no result output
# and that the session becomes interactive when an exception is encountered.
# @param [Pry] _pry_ the Pry instance to make non-interactive.
def non_interactive_mode(_pry_)
def non_interactive_mode(_pry_, content)
_pry_.print = proc {}
_pry_.exception_handler = proc do |o, e, _pry_|
_pry_.run_command "cat --ex"
_pry_.exception_handler = proc do |o, e, _p_|
_p_.run_command "cat --ex"
o.puts "...exception encountered, going interactive!"
interactive_mode(_pry_)
end
content.lines.each do |line|
break unless _pry_.eval line, :generated => true
end
end
# Define a few extra commands useful for flipping back & forth
@ -46,35 +52,18 @@ class Pry
s = self
Pry::Commands.command "make-interactive", "Make the session interactive" do
_pry_.input_stack.push _pry_.input
s.interactive_mode(_pry_)
end
Pry::Commands.command "make-non-interactive", "Make the session non-interactive" do
_pry_.input = _pry_.input_stack.pop
s.non_interactive_mode(_pry_)
end
Pry::Commands.command "load-file", "Load another file through the repl" do |file_name|
content = StringIO.new(File.read(File.expand_path(file_name)))
_pry_.input_stack.push(_pry_.input)
_pry_.input = content
s.non_interactive_mode(_pry_, File.read(File.expand_path(file_name)))
end
end
# Actually load the file through the REPL by setting file content
# as the REPL input stream.
def load
Pry.initial_session_setup
define_additional_commands
Pry.config.hooks.add_hook(:when_started, :start_non_interactively) do |o, t, _pry_|
non_interactive_mode(_pry_)
end
Pry.start(Pry.toplevel_binding,
:input => @content,
:input_stack => [StringIO.new("exit-all\n")])
non_interactive_mode(Pry.new, @content)
end
end
end

View file

@ -14,7 +14,7 @@ class << Pry
Pry.config.should_load_plugins = false
Pry.config.history.should_load = false
Pry.config.history.should_save = false
Pry.config.auto_indent = false
Pry.config.correct_indent = false
Pry.config.hooks = Pry::Hooks.new
Pry.config.collision_warning = false
end
@ -101,18 +101,17 @@ def pry_eval(*eval_strs)
end
class PryTester
extend Forwardable
attr_reader :pry, :out
def initialize(context = TOPLEVEL_BINDING, options = {})
@pry = Pry.new(options)
def_delegators :@pry, :eval_string, :eval_string=
if context
target = Pry.binding_for(context)
@pry.binding_stack << target
@pry.inject_sticky_locals(target)
end
def initialize(target = TOPLEVEL_BINDING, options = {})
@pry = Pry.new(options.merge(:target => target))
@history = options[:history]
@pry.input_array << nil # TODO: shouldn't need this
@pry.inject_sticky_locals!
reset_output
end
@ -122,6 +121,8 @@ class PryTester
strs.flatten.each do |str|
str = "#{str.strip}\n"
@history.push str if @history
if @pry.process_command(str)
result = last_command_result_or_output
else
@ -132,43 +133,32 @@ class PryTester
result
end
def context=(context)
@pry.binding_stack << Pry.binding_for(context)
def push(*lines)
Array(lines).flatten.each do |line|
@pry.eval(line)
end
end
# TODO: eliminate duplication with Pry#repl
def simulate_repl
didnt_exit = nil
break_data = nil
didnt_exit = catch(:didnt_exit) do
break_data = catch(:breakout) do
yield self
throw(:didnt_exit, true)
end
nil
end
raise "Failed to exit REPL" if didnt_exit
break_data
def push_binding(context)
@pry.push_binding context
end
def last_output
@out.string if @out
end
def process_command(command_str, eval_str = '')
@pry.process_command(command_str, eval_str) or raise "Not a valid command"
def process_command(command_str)
@pry.process_command(command_str) or raise "Not a valid command"
last_command_result_or_output
end
protected
def last_command_result
result = Pry.current[:pry_cmd_result]
result.retval if result
end
protected
def last_command_result_or_output
result = last_command_result
if result != Pry::Command::VOID_VALUE

View file

@ -28,6 +28,7 @@ describe "commands" do
after do
Pad.clear
Pry.reset_defaults
end
describe "alias_command" do
@ -38,15 +39,10 @@ describe "commands" do
end
alias_command "test-alias", "test-command"
end
redirect_pry_io(InputTester.new("test-alias"), out1 = StringIO.new) do
Pry.start self, :commands => set
end
redirect_pry_io(InputTester.new("test-command"), out2 = StringIO.new) do
Pry.start self, :commands => set
pry_tester(:commands => set).tap do |t|
t.eval('test-command').should == t.eval('test-alias')
end
out1.string.should == out2.string
end
it 'should pass on arguments to original' do
@ -181,9 +177,7 @@ describe "commands" do
end
end
Pry.new(:input => InputTester.new("run_v"), :output => @str_output, :commands => klass).rep
@str_output.string.should =~ /v command/
pry_tester(:commands => klass).eval('run_v').should =~ /v command/
end
it 'should run a regex command from within a command' do
@ -197,11 +191,7 @@ describe "commands" do
end
end
redirect_pry_io(InputTester.new("run_v"), @str_output) do
Pry.new(:commands => klass).rep
end
@str_output.string.should =~ /v baby/
pry_tester(:commands => klass).eval('run_v').should =~ /v baby/
end
it 'should run a command from within a command with arguments' do
@ -220,33 +210,22 @@ describe "commands" do
end
["run_v_explicit_parameter", "run_v_embedded_parameter"].each do |cmd|
redirect_pry_io(InputTester.new(cmd), @str_output) do
Pry.new(:commands => klass).rep
end
@str_output.string.should =~ /v baby param/
pry_tester(:commands => klass).eval(cmd).should =~ /v baby param/
end
end
end
describe "Pry#run_command" do
it 'should run a command in a specified context' do
b = Pry.binding_for('seven')
p = Pry.new(:output => @str_output)
p.run_command("ls -m", "", b)
p.output.string.should =~ /downcase/
end
it 'should run a command that modifies the passed in eval_string' do
b = Pry.binding_for(7)
p = Pry.new(:output => @str_output)
eval_string = "def hello\npeter pan\n"
p.run_command("amend-line !", eval_string, b)
eval_string.should =~ /def hello/
eval_string.should.not =~ /peter pan/
p.eval "def hello\npeter pan\n"
p.run_command "amend-line !"
p.eval_string.should =~ /def hello/
p.eval_string.should.not =~ /peter pan/
end
it 'should run a command in the context of a session' do
pry_tester.tap do |t|
pry_tester(Object.new).tap do |t|
t.eval "@session_ivar = 10", "_pry_.run_command('ls')"
t.last_output.should =~ /@session_ivar/
end
@ -260,10 +239,7 @@ describe "commands" do
end
end
str_input = StringIO.new('hello #{Pad.bong}')
Pry.new(:input => str_input, :output => @str_output, :commands => set).rep
@str_output.string.should =~ /bong/
pry_tester(:commands => set).eval('hello #{Pad.bong}').should =~ /bong/
end
# bug fix for https://github.com/pry/pry/issues/170
@ -282,39 +258,32 @@ describe "commands" do
end
end
str_input = StringIO.new('hello #{Pad.bong}')
Pry.new(:input => str_input, :output => @str_output, :commands => set).rep
@str_output.string.should =~ /Pad\.bong/
pry_tester(:commands => set).eval('hello #{Pad.bong}').
should =~ /Pad\.bong/
end
it 'should NOT try to interpolate pure ruby code (no commands) ' do
Pry.new(:input => StringIO.new('format \'#{aggy}\''), :output => @str_output).rep
@str_output.string.should.not =~ /NameError/
# These should raise RuntimeError instead of NameError
proc {
pry_eval 'raise \'#{aggy}\''
}.should.raise(RuntimeError)
@str_output = StringIO.new
Pry.new(:input => StringIO.new('format #{aggy}'), :output => @str_output).rep
@str_output.string.should.not =~ /NameError/
proc {
pry_eval 'raise #{aggy}'
}.should.raise(RuntimeError)
@str_output = StringIO.new
Pad.interp = "bong"
Pry.new(:input => StringIO.new('format \'#{Pad.interp}\''), :output => @str_output).rep
@str_output.string.should.not =~ /bong/
pry_eval('format \'#{my_var}\'').should == "\#{my_var}"
end
it 'should create a command with a space in its name' do
it 'should create a command with a space in its name zzz' do
set = Pry::CommandSet.new do
command "hello baby", "" do
output.puts "hello baby command"
end
end
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/
pry_tester(:commands => set).eval('hello baby').
should =~ /hello baby command/
end
it 'should create a command with a space in its name and pass an argument' do
@ -324,11 +293,8 @@ describe "commands" do
end
end
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/
pry_tester(:commands => set).eval('hello baby john').
should =~ /hello baby command john/
end
it 'should create a regex command and be able to invoke it' do
@ -339,11 +305,7 @@ describe "commands" do
end
end
redirect_pry_io(InputTester.new("hello1"), @str_output) do
Pry.new(:commands => set).rep
end
@str_output.string.should =~ /hello1/
pry_tester(:commands => set).eval('hello1').should =~ /hello1/
end
it 'should create a regex command and pass captures into the args list before regular arguments' do
@ -353,11 +315,7 @@ describe "commands" do
end
end
redirect_pry_io(InputTester.new("hello1 baby"), @str_output) do
Pry.new(:commands => set).rep
end
@str_output.string.should =~ /hello 1 baby/
pry_tester(:commands => set).eval('hello1 baby').should =~ /hello 1 baby/
end
it 'should create a regex command and interpolate the captures' do
@ -367,11 +325,9 @@ describe "commands" do
end
end
redirect_pry_io(InputTester.new('hello #{Pad.bong}'), @str_output) do
Pry.new(:commands => set).rep
end
@str_output.string.should =~ /hello bong/
bong = "bong"
pry_tester(binding, :commands => set).eval('hello #{bong}').
should =~ /hello bong/
end
it 'should create a regex command and arg_string should be interpolated' do
@ -381,14 +337,13 @@ describe "commands" do
end
end
Pad.bing = "bing"
Pad.bang = "bang"
redirect_pry_io(InputTester.new('hellojohn #{Pad.bing} #{Pad.bong} #{Pad.bang}'),
@str_output) do
Pry.new(:commands => set).rep
end
bing = 'bing'
bong = 'bong'
bang = 'bang'
@str_output.string.should =~ /hello john bing bong bang/
pry_tester(binding, :commands => set).
eval('hellojohn #{bing} #{bong} #{bang}').
should =~ /hello john bing bong bang/
end
it 'if a regex capture is missing it should be nil' do
@ -398,20 +353,17 @@ describe "commands" do
end
end
redirect_pry_io(InputTester.new("hello baby"), @str_output) do
Pry.new(:commands => set).rep
end
@str_output.string.should =~ /hello nil baby/
pry_tester(:commands => set).eval('hello baby').should =~ /hello nil baby/
end
it 'should create a command in a nested context and that command should be accessible from the parent' do
x = "@x=nil\ncd 7\n_pry_.commands.instance_eval {\ncommand('bing') { |arg| run arg }\n}\ncd ..\nbing ls\nexit-all"
redirect_pry_io(StringIO.new("@x=nil\ncd 7\n_pry_.commands.instance_eval {\ncommand('bing') { |arg| run arg }\n}\ncd ..\nbing ls\nexit-all"), @str_output) do
Pry.new.repl('0')
end
@str_output.string.should =~ /@x/
pry_tester(Object.new).eval(*(<<-RUBY.split("\n"))).should =~ /instance variables:\s+@x/m
@x = nil
cd 7
_pry_.commands.instance_eval { command('bing') { |arg| run arg } }
cd ..
bing ls
RUBY
end
it 'should define a command that keeps its return value' do
@ -421,9 +373,9 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
@str_output.string.should =~ /:kept_hello/
@str_output.string.should =~ /=>/
t = pry_tester(:commands => klass)
t.eval("hello\n")
t.last_command_result.should == :kept_hello
end
it 'should define a command that does NOT keep its return value' do
@ -433,9 +385,9 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
(@str_output.string =~ /:kept_hello/).should == nil
@str_output.string !~ /=>/
t = pry_tester(:commands => klass)
t.eval("hello\n").should == ''
t.last_command_result.should == Pry::Command::VOID_VALUE
end
it 'should define a command that keeps its return value even when nil' do
@ -445,10 +397,9 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
@str_output.string.should =~ /nil/
@str_output.string.should =~ /=>/
t = pry_tester(:commands => klass)
t.eval("hello\n")
t.last_command_result.should == nil
end
it 'should define a command that keeps its return value but does not return when value is void' do
@ -458,8 +409,7 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
@str_output.string.empty?.should == true
pry_tester(:commands => klass).eval("hello\n").empty?.should == true
end
it 'a command (with :keep_retval => false) that replaces eval_string with a valid expression should not have the expression value suppressed' do
@ -469,8 +419,13 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => @str_output, :commands => klass).rep
@str_output.string.should =~ /6/
output = StringIO.new
redirect_pry_io(InputTester.new('def yo', 'hello'), output) do
Pry.start self, :commands => klass
end
output.string.should =~ /6/
end
it 'a command (with :keep_retval => true) that replaces eval_string with a valid expression should overwrite the eval_string with the return value' do
@ -481,10 +436,7 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => @str_output, :commands => klass).rep
@str_output.string.should =~ /7/
@str_output.string.should.not =~ /6/
pry_tester(:commands => klass).eval("def yo\nhello\n").should == 7
end
it 'a command that return a value in a multi-line expression should clear the expression and return the value' do
@ -494,9 +446,7 @@ describe "commands" do
end
end
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => @str_output, :commands => klass).rep
@str_output.string.should =~ /5/
pry_tester(:commands => klass).eval("def yo\nhello\n").should == 5
end
it 'should set the commands default, and the default should be overridable' do
@ -506,21 +456,15 @@ describe "commands" do
end
end
Pry.commands = klass
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/
Pry.commands = klass
pry_tester.eval("hello").should == "hello world\n"
pry_tester(:commands => other_klass).eval("goodbye").should == "goodbye world\n"
end
it 'should inherit commands from Pry::Commands' do
@ -557,10 +501,10 @@ describe "commands" do
child_klass = Pry::CommandSet.new klass do
end
Pry.new(:print => proc {}, :input => InputTester.new("v"),
:output => @str_output, :commands => child_klass).rep("john")
mock_pry(Pry.binding_for('john'), "v", :print => proc {}, :commands => child_klass,
:output => @str_output)
@str_output.string.rstrip.should == "john"
@str_output.string.should == "john\n"
end
it 'should import commands from another command object' do
@ -601,44 +545,18 @@ describe "commands" do
end
end
# suppress evaluation output
Pry.print = proc {}
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.should == "help to the music\n"
Pry.reset_defaults
Pry.color = false
t = pry_tester(:commands => klass)
t.eval('jump-to').should == "jump-to the music\n"
t.eval('help').should == "help to the music\n"
end
it 'should run a command with no parameter' do
pry_tester = Pry.new
pry_tester.commands = @command_tester
pry_tester.input = InputTester.new("command1", "exit-all")
pry_tester.commands = @command_tester
pry_tester.output = @str_output
pry_tester.rep
@str_output.string.should =~ /command1/
pry_tester(:commands => @command_tester).eval('command1').
should == "command1\n"
end
it 'should run a command with one parameter' do
pry_tester = Pry.new
pry_tester.commands = @command_tester
pry_tester.input = InputTester.new("command2 horsey", "exit-all")
pry_tester.commands = @command_tester
pry_tester.output = @str_output
pry_tester.rep
@str_output.string.should =~ /horsey/
pry_tester(:commands => @command_tester).eval('command2 horsey').
should == "horsey\n"
end
end

View file

@ -6,29 +6,29 @@ describe "amend-line" do
end
it 'should amend the last line of input when no line number specified' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
STR
@t.process_command 'amend-line puts :blah', eval_str
@t.process_command 'amend-line puts :blah'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :blah
STR
end
it 'should amend the specified line of input when line number given' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
STR
@t.process_command 'amend-line 1 def goodbye', eval_str
@t.process_command 'amend-line 1 def goodbye'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def goodbye
puts :bing
puts :bang
@ -36,15 +36,15 @@ describe "amend-line" do
end
it 'should amend the first line of input when 0 given as line number' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
STR
@t.process_command 'amend-line 0 def goodbye', eval_str
@t.process_command 'amend-line 0 def goodbye'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def goodbye
puts :bing
puts :bang
@ -52,23 +52,23 @@ describe "amend-line" do
end
it 'should amend a specified line when negative number given' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
STR
@t.process_command 'amend-line -1 puts :bink', eval_str
@t.process_command 'amend-line -1 puts :bink'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bing
puts :bink
STR
@t.process_command 'amend-line -2 puts :bink', eval_str
@t.process_command 'amend-line -2 puts :bink'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bink
puts :bink
@ -76,16 +76,16 @@ describe "amend-line" do
end
it 'should amend a range of lines of input when negative numbers given' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
puts :boat
STR
@t.process_command 'amend-line -3..-2 puts :bink', eval_str
@t.process_command 'amend-line -3..-2 puts :bink'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bink
puts :boat
@ -93,15 +93,15 @@ describe "amend-line" do
end
it 'should correctly amend the specified line with interpolated text' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
STR
@t.process_command 'amend-line puts "#{goodbye}"', eval_str
@t.process_command 'amend-line puts "#{goodbye}"'
eval_str.should == unindent(<<-'STR')
@t.eval_string.should == unindent(<<-'STR')
def hello
puts :bing
puts "#{goodbye}"
@ -122,16 +122,16 @@ describe "amend-line" do
end
it 'should correctly amend the specified range of lines' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
puts :heart
STR
@t.process_command 'amend-line 2..3 puts :bong', eval_str
@t.process_command 'amend-line 2..3 puts :bong'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bong
puts :heart
@ -139,7 +139,7 @@ describe "amend-line" do
end
it 'should correctly delete a specific line using the ! for content' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
@ -147,9 +147,9 @@ describe "amend-line" do
puts :heart
STR
@t.process_command 'amend-line 3 !', eval_str
@t.process_command 'amend-line 3 !'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bing
puts :boast
@ -158,7 +158,7 @@ describe "amend-line" do
end
it 'should correctly delete a range of lines using the ! for content' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
@ -166,16 +166,16 @@ describe "amend-line" do
puts :heart
STR
@t.process_command 'amend-line 2..4 !', eval_str
@t.process_command 'amend-line 2..4 !'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :heart
STR
end
it 'should correctly delete the previous line using the ! for content' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
@ -183,9 +183,9 @@ describe "amend-line" do
puts :heart
STR
@t.process_command 'amend-line !', eval_str
@t.process_command 'amend-line !'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bing
puts :bang
@ -194,7 +194,7 @@ describe "amend-line" do
end
it 'should amend the specified range of lines, with numbers < 0 in range' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
@ -202,9 +202,9 @@ describe "amend-line" do
puts :heart
STR
@t.process_command 'amend-line 2..-2 puts :bong', eval_str
@t.process_command 'amend-line 2..-2 puts :bong'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :bong
puts :heart
@ -212,15 +212,15 @@ describe "amend-line" do
end
it 'should correctly insert a line before a specified line using >' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
STR
@t.process_command 'amend-line 2 > puts :inserted', eval_str
@t.process_command 'amend-line 2 > puts :inserted'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :inserted
puts :bing
@ -229,15 +229,15 @@ describe "amend-line" do
end
it 'should ignore second value of range with > syntax' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
puts :bang
STR
@t.process_command 'amend-line 2..21 > puts :inserted', eval_str
@t.process_command 'amend-line 2..21 > puts :inserted'
eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def hello
puts :inserted
puts :bing

View file

@ -6,14 +6,13 @@ describe "!" do
end
it 'should correctly clear the input buffer ' do
eval_str = unindent(<<-STR)
@t.push unindent(<<-STR)
def hello
puts :bing
STR
@t.process_command '!', eval_str
@t.process_command '!'
@t.last_output.should =~ /Input buffer cleared!/
eval_str.should == ''
@t.eval_string.should == ''
end
end

View file

@ -283,8 +283,8 @@ describe "edit" do
end
it "should edit the current expression if it's incomplete" do
eval_str = 'def a'
@t.process_command 'edit', eval_str
@t.push 'def a'
@t.process_command 'edit'
@contents.should == "def a\n"
end
@ -299,14 +299,14 @@ describe "edit" do
end
it "should use a blank file if -t given, even during an expression" do
eval_str = 'def a;'
@t.process_command 'edit -t', eval_str
@t.push 'def a;'
@t.process_command 'edit -t'
@contents.should == "\n"
end
it "should position the cursor at the end of the expression" do
eval_str = "def a; 2;\nend"
@t.process_command 'edit', eval_str
@t.eval "def a; 2;\nend"
@t.process_command 'edit'
@line.should == 2
end
@ -315,9 +315,8 @@ describe "edit" do
File.open(file, 'w'){|f| f << "'FOO'\n" }
nil
}
eval_str = ''
@t.process_command 'edit', eval_str
eval_str.should == "'FOO'\n"
@t.process_command 'edit'
@t.eval_string.should == "'FOO'\n"
end
it "should not evaluate the expression with -n" do
@ -325,9 +324,8 @@ describe "edit" do
File.open(file, 'w'){|f| f << "'FOO'\n" }
nil
}
eval_str = ''
@t.process_command 'edit -n', eval_str
eval_str.should == ''
@t.process_command 'edit -n'
@t.eval_string.should == ''
end
end

View file

@ -1,34 +1,27 @@
require 'helper'
describe "exit-all" do
it 'should break out of the repl loop of Pry instance and return nil' do
pry_tester(0).simulate_repl do |t|
t.eval 'exit-all'
end.should == nil
before { @pry = Pry.new }
it "should break out of the repl and return nil" do
@pry.eval("exit-all").should.be.false
@pry.exit_value.should.be.nil
end
it 'should break out of the repl loop of Pry instance wth a user specified value' do
pry_tester(0).simulate_repl do |t|
t.eval "exit-all 'message'"
end.should == 'message'
it "should break out of the repl wth a user specified value" do
@pry.eval("exit-all 'message'").should.be.false
@pry.exit_value.should == "message"
end
it 'should break of the repl loop even if multiple bindings still on stack' do
pry_tester(0).simulate_repl do |t|
t.eval 'cd 1', 'cd 2', "exit-all 'message'"
end.should == 'message'
it "should break out of the repl even if multiple bindings still on stack" do
["cd 1", "cd 2"].each { |line| @pry.eval(line).should.be.true }
@pry.eval("exit-all 'message'").should.be.false
@pry.exit_value.should == "message"
end
it 'binding_stack should be empty after breaking out of the repl loop' do
t = pry_tester(0) do
def binding_stack
@pry.binding_stack
end
end
t.simulate_repl do |t|
t.eval 'cd 1', 'cd 2', 'exit-all'
end
t.binding_stack.empty?.should == true
it "should have empty binding_stack after breaking out of the repl" do
["cd 1", "cd 2"].each { |line| @pry.eval(line).should.be.true }
@pry.eval("exit-all").should.be.false
@pry.binding_stack.should.be.empty
end
end

View file

@ -1,34 +1,28 @@
require 'helper'
describe "exit" do
it 'should pop a binding with exit' do
pry_tester(:outer).simulate_repl do |t|
t.eval 'cd :inner'
t.eval('self').should == :inner
t.eval 'exit'
t.eval('self').should == :outer
t.eval 'exit-all'
end
before { @pry = Pry.new(:target => :outer, :output => StringIO.new) }
it "should pop a binding" do
@pry.eval "cd :inner"
@pry.evaluate_ruby("self").should == :inner
@pry.eval "exit"
@pry.evaluate_ruby("self").should == :outer
end
it 'should break out of the repl loop of Pry instance when binding_stack has only one binding with exit' do
pry_tester(0).simulate_repl do |t|
t.eval 'exit'
end.should == nil
it "should break out of the repl when binding_stack has only one binding" do
@pry.eval("exit").should.be.false
@pry.exit_value.should.be.nil
end
it 'should break out of the repl loop of Pry instance when binding_stack has only one binding with exit, and return user-given value' do
pry_tester(0).simulate_repl do |t|
t.eval 'exit :john'
end.should == :john
it "should break out of the repl and return user-given value" do
@pry.eval("exit :john").should.be.false
@pry.exit_value.should == :john
end
it 'should break out the repl loop of Pry instance even after an exception in user-given value' do
pry_tester(0).simulate_repl do |t|
proc {
t.eval 'exit = 42'
}.should.raise(SyntaxError)
t.eval 'exit'
end.should == nil
it "should break out of the repl even after an exception" do
@pry.eval "exit = 42"
@pry.output.string.should =~ /^SyntaxError/
@pry.eval("exit").should.be.false
end
end

View file

@ -6,7 +6,7 @@ describe "hist" do
@hist = Pry.history
@str_output = StringIO.new
@t = pry_tester do
@t = pry_tester :history => @hist do
# For looking at what hist pushes into the input stack. The
# implementation of this helper will definitely have to change at some
# point.
@ -29,10 +29,10 @@ describe "hist" do
@hist.push "@y = 20"
@hist.push "@z = 30"
@t.context = o
@t.push_binding o
@t.eval 'hist --replay -1'
@t.next_input.should == "@z = 30\n"
o.instance_variable_get(:@z).should == 30
end
it 'should replay a range of history correctly (range of items)' do
@ -40,10 +40,9 @@ describe "hist" do
@hist.push "@x = 10"
@hist.push "@y = 20"
@t.context = o
@t.push_binding o
@t.eval 'hist --replay 0..2'
@t.next_input.should == "@x = 10\n@y = 20\n"
@t.eval('[@x, @y]').should == [10, 20]
end
# this is to prevent a regression where input redirection is
@ -52,12 +51,10 @@ describe "hist" do
o = Object.new
@hist.push "cd 1"
@hist.push "cd 2"
redirect_pry_io(InputTester.new("hist --replay 0..2", "Pad.stack = _pry_.binding_stack.dup", "exit-all")) do
o.pry
end
o = Pad.stack[-2..-1].map { |v| v.eval('self') }
o.should == [1, 2]
Pad.clear
@t.eval("hist --replay 0..2")
stack = @t.eval("Pad.stack = _pry_.binding_stack.dup")
stack.map{ |b| b.eval("self") }.should == [TOPLEVEL_BINDING.eval("self"), 1, 2]
end
it 'should grep for correct lines in history' do
@ -139,43 +136,37 @@ describe "hist" do
end
it "should store a call with `--replay` flag" do
redirect_pry_io(InputTester.new(":banzai", "hist --replay 1",
"hist", "exit-all"), @str_output) do
Pry.start
end
@str_output.string.should =~ /hist --replay 1/
@t.eval ":banzai"
@t.eval "hist --replay 1"
@t.eval("hist").should =~ /hist --replay 1/
end
it "should not contain lines produced by `--replay` flag" do
redirect_pry_io(InputTester.new(":banzai", ":geronimo", ":huzzah",
"hist --replay 1..3", "hist",
"exit-all"), @str_output) do
Pry.start
end
@t.eval ":banzai"
@t.eval ":geronimo"
@t.eval ":huzzah"
@t.eval("hist --replay 1..3")
@str_output.string.each_line.to_a.reject { |line| line.start_with?("=>") }.size.should == 4
@str_output.string.each_line.to_a.last.should =~ /hist --replay 1\.\.3/
@str_output.string.each_line.to_a[-2].should =~ /:huzzah/
output = @t.eval("hist")
output.should == "1: :banzai\n2: :geronimo\n3: :huzzah\n4: hist --replay 1..3\n"
end
it "should raise CommandError when index of `--replay` points out to another `hist --replay`" do
redirect_pry_io(InputTester.new(":banzai", "hist --replay 1",
"hist --replay 2", "exit-all"), @str_output) do
Pry.start
end
@str_output.string.should =~ /Replay index 2 points out to another replay call: `hist --replay 1`/
@t.eval ":banzai"
@t.eval "hist --replay 1"
lambda do
@t.eval "hist --replay 2"
end.should.raise(Pry::CommandError, /Replay index 4 points out to another replay call: `hist --replay 1`/)
end
it "should disallow execution of `--replay <i>` when CommandError raised" do
redirect_pry_io(InputTester.new("a = 0", "a += 1", "hist --replay 2",
"hist --replay 3", "'a is ' + a.to_s",
"hist", "exit-all"), @str_output) do
Pry.start
end
@str_output.string.each_line.to_a.reject { |line| line !~ /\A\d/ }.size.should == 5
@str_output.string.should =~ /a is 2/
@t.eval "a = 0"
@t.eval "a += 1"
@t.eval "hist --replay 2"
lambda{
@t.eval "hist --replay 3"
}.should.raise(Pry::CommandError)
@t.eval("a").should == 2
@t.eval("hist").lines.to_a.size.should == 5
end
end

View file

@ -6,8 +6,8 @@ require 'helper'
describe "play" do
before do
@t = pry_tester
@eval_str = ''
@o = Object.new
@t = pry_tester(@o)
end
describe "with an argument" do
@ -38,7 +38,6 @@ describe "play" do
describe "whatever" do
before do
@o = Object.new
def @o.test_method
:test_method_content
end
@ -51,9 +50,8 @@ describe "play" do
:test_method_content
end
pry_tester(@o).process_command 'play -d test_method', @eval_str
@eval_str.should == unindent(<<-STR)
@t.process_command 'play -d test_method'
@t.eval_string.should == unindent(<<-STR)
@v = 10
@y = 20
STR
@ -68,9 +66,8 @@ describe "play" do
:test_method_content
end
pry_tester(@o).process_command 'play -d test_method --lines 2..3', @eval_str
@eval_str.should == unindent(<<-STR)
@t.process_command 'play -d test_method --lines 2..3'
@t.eval_string.should == unindent(<<-STR)
@v = 10
@y = 20
STR
@ -81,8 +78,8 @@ describe "play" do
end
it 'should play a method (a single line)' do
pry_tester(@o).process_command 'play test_method --lines 2', @eval_str
@eval_str.should == ":test_method_content\n"
@t.process_command 'play test_method --lines 2'
@t.eval_string.should == ":test_method_content\n"
end
it 'should properly reindent lines' do
@ -90,18 +87,18 @@ describe "play" do
'hello world'
end
pry_tester(@o).process_command 'play test_method --lines 2', @eval_str
@eval_str.should == "'hello world'\n"
@t.process_command 'play test_method --lines 2'
@t.eval_string.should == "'hello world'\n"
end
it 'should APPEND to the input buffer when playing a method line, not replace it' do
@eval_str = unindent(<<-STR)
@t.eval_string = unindent(<<-STR)
def another_test_method
STR
pry_tester(@o).process_command 'play test_method --lines 2', @eval_str
@t.process_command 'play test_method --lines 2'
@eval_str.should == unindent(<<-STR)
@t.eval_string.should == unindent(<<-STR)
def another_test_method
:test_method_content
STR
@ -115,9 +112,8 @@ describe "play" do
@var3 = 40
end
pry_tester(@o).process_command 'play test_method --lines 3..4', @eval_str
@eval_str.should == unindent(<<-STR, 0)
@t.process_command 'play test_method --lines 3..4'
@t.eval_string.should == unindent(<<-STR, 0)
@var1 = 20
@var2 = 30
STR

View file

@ -13,13 +13,13 @@ describe "raise-up" do
it "should raise the exception with raise-up" do
redirect_pry_io(InputTester.new("raise NoMethodError", "raise-up NoMethodError")) do
lambda { Pry.new.repl(0) }.should.raise NoMethodError
lambda { Object.new.pry }.should.raise NoMethodError
end
end
it "should raise an unamed exception with raise-up" do
redirect_pry_io(InputTester.new("raise 'stop'","raise-up 'noreally'")) do
lambda { Pry.new.repl(0) }.should.raise RuntimeError, "noreally"
lambda { Object.new.pry }.should.raise RuntimeError, "noreally"
end
end

View file

@ -6,12 +6,12 @@ describe "show-input" do
end
it 'should correctly show the current lines in the input buffer' do
eval_str = unindent(<<-STR)
@t.push *unindent(<<-STR).split("\n")
def hello
puts :bing
STR
@t.process_command 'show-input', eval_str
@t.process_command 'show-input'
@t.last_output.should =~ /\A\d+: def hello\n\d+: puts :bing/
end
end

View file

@ -1,12 +1,8 @@
require 'helper'
def new_completer(bind, pry=nil)
Pry::InputCompleter.build_completion_proc(Pry.binding_for(bind), pry)
end
def completer_test(bind, pry=nil, assert_flag=true)
completer = new_completer(bind, pry)
test = proc {|symbol| completer.call(symbol[0..-2]).include?(symbol).should == assert_flag}
test = proc {|symbol|
Pry::InputCompleter.call(symbol[0..-2], :target => Pry.binding_for(bind), :pry => pry).include?(symbol).should == assert_flag}
return proc {|*symbols| symbols.each(&test) }
end
@ -40,16 +36,12 @@ describe Pry::InputCompleter do
# another jruby hack :((
if !Pry::Helpers::BaseHelpers.jruby?
it "should not crash if there's a Module that has a symbolic name." do
completer = Pry::InputCompleter.build_completion_proc(Pry.binding_for(Object.new))
lambda{ completer.call "a.to_s." }.should.not.raise Exception
lambda{ Pry::InputCompleter.call "a.to_s.", :target => Pry.binding_for(Object.new) }.should.not.raise Exception
end
end
it 'should take parenthesis and other characters into account for symbols' do
b = Pry.binding_for(Object.new)
completer = Pry::InputCompleter.build_completion_proc(b)
lambda { completer.call(":class)") }.should.not.raise(RegexpError)
lambda { Pry::InputCompleter.call(":class)", :target => Pry.binding_for(Object.new)) }.should.not.raise(RegexpError)
end
it 'should complete instance variables' do
@ -128,7 +120,7 @@ describe Pry::InputCompleter do
completer_test(binding).call('o.foo')
# trailing slash
new_completer(Mod).call('Mod2/').include?('Mod2/').should == true
Pry::InputCompleter.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true
end
it 'should complete for arbitrary scopes' do
@ -142,10 +134,8 @@ describe Pry::InputCompleter do
Con = :constant
end
pry = Pry.new()
stack = pry.binding_stack
stack.push(Pry.binding_for(Baz))
stack.push(Pry.binding_for(Bar))
pry = Pry.new(:target => Baz)
pry.push_binding(Bar)
b = Pry.binding_for(Bar)
completer_test(b, pry).call("../@bazvar")
@ -201,7 +191,7 @@ describe Pry::InputCompleter do
completer_test(binding).call('o.foo')
# trailing slash
new_completer(Mod).call('Mod2/').include?('Mod2/').should == true
Pry::InputCompleter.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true
end
@ -216,10 +206,8 @@ describe Pry::InputCompleter do
Con = :constant
end
pry = Pry.new()
stack = pry.binding_stack
stack.push(Pry.binding_for(Baz))
stack.push(Pry.binding_for(Bar))
pry = Pry.new(:target => Baz)
pry.push_binding(Bar)
b = Pry.binding_for(Bar)
completer_test(b, pry).call("../@bazvar")
@ -228,6 +216,6 @@ describe Pry::InputCompleter do
it 'should not return nil in its output' do
pry = Pry.new
new_completer(binding, pry).call("pry.").should.not.include nil
Pry::InputCompleter.call("pry.", :target => binding).should.not.include nil
end
end

View file

@ -17,38 +17,42 @@ describe Pry::DEFAULT_CONTROL_D_HANDLER do
end
end
describe "at top-level session" do
it "breaks out of a REPL" do
pry_tester(0).simulate_repl do |t|
t.eval @control_d
end.should == nil
describe 'at top-level session' do
it 'should break out of a REPL loop' do
instance = Pry.new
instance.binding_stack.should.not.be.empty
instance.eval(nil).should.be.false
instance.binding_stack.should.be.empty
end
end
describe "in a nested session" do
it "pops last binding from the binding stack" do
pry_tester(0).simulate_repl { |t|
t.eval 'cd :foo'
t.eval('_pry_.binding_stack.size').should == 2
t.eval(@control_d)
t.eval('_pry_.binding_stack.size').should == 1
t.eval 'exit-all'
}
describe 'in a nested session' do
it 'should pop last binding from the binding stack' do
t = pry_tester
t.eval "cd Object.new"
t.eval("_pry_.binding_stack.size").should == 2
t.eval("_pry_.eval(nil)").should.be.true
t.eval("_pry_.binding_stack.size").should == 1
end
it "breaks out of the parent session" do
pry_tester(:outer).simulate_repl do |o|
o.context = :inner
o.simulate_repl { |i|
i.eval('_pry_.current_context.eval("self")').should == :inner
i.eval('_pry_.binding_stack.size').should == 2
i.eval @control_d
i.eval('_pry_.binding_stack.size').should == 1
i.eval('_pry_.current_context.eval("self")').should == :outer
i.eval 'throw :breakout'
}
o.eval 'exit-all'
ReplTester.start do
input 'Pry::REPL.new(_pry_, :target => 10).start'
output ''
prompt(/10.*> $/)
input 'self'
output '=> 10'
input nil # Ctrl-D
output ''
input 'self'
output '=> main'
input nil # Ctrl-D
output '=> nil' # Exit value of nested REPL.
assert_exited
end
end
end

View file

@ -8,6 +8,7 @@ require 'mocha/api'
require 'pry/test/helper'
require 'helpers/bacon'
require 'helpers/mock_pry'
require 'helpers/repl_tester'
class Module
public :remove_const

View file

@ -1,12 +1,13 @@
def mock_pry(*args)
args.flatten!
binding = args.first.is_a?(Binding) ? args.shift : binding()
options = args.last.is_a?(Hash) ? args.pop : {}
input = InputTester.new(*args)
output = StringIO.new
redirect_pry_io(input, output) do
binding.pry
binding.pry(options)
end
output.string

112
spec/helpers/repl_tester.rb Normal file
View file

@ -0,0 +1,112 @@
# This is for super-high-level integration testing.
require 'thread'
require 'delegate'
class ReplTester
class Input
def initialize(tester_mailbox)
@tester_mailbox = tester_mailbox
end
def readline(prompt)
@tester_mailbox.push prompt
mailbox.pop
end
def mailbox
Thread.current[:mailbox]
end
end
class Output < SimpleDelegator
def clear
__setobj__(StringIO.new)
end
end
def self.start(options = {}, &block)
Thread.current[:mailbox] = Queue.new
instance = nil
input = Input.new(Thread.current[:mailbox])
output = Output.new(StringIO.new)
redirect_pry_io input, output do
instance = new(options)
instance.instance_eval(&block)
instance.ensure_exit
end
ensure
if instance && instance.thread && instance.thread.alive?
instance.thread.kill
end
end
attr_accessor :thread, :mailbox, :last_prompt
def initialize(options = {})
@pry = Pry.new(options)
@repl = Pry::REPL.new(@pry)
@mailbox = Thread.current[:mailbox]
@thread = Thread.new do
begin
Thread.current[:mailbox] = Queue.new
@repl.start
ensure
Thread.current[:session_ended] = true
mailbox.push nil
end
end
wait # wait until the instance reaches its first readline
end
# Accept a line of input, as if entered by a user.
def input(input)
reset_output
repl_mailbox.push input
wait
Pry.output.string
end
# Assert that the current prompt matches the given string or regex.
def prompt(match)
match.should === last_prompt
end
# Assert that the most recent output (since the last time input was called)
# matches the given string or regex.
def output(match)
match.should === Pry.output.string.chomp
end
# Assert that the Pry session ended naturally after the last input.
def assert_exited
@should_exit_naturally = true
end
# @private
def ensure_exit
if @should_exit_naturally
@thread[:session_ended].should.be.true
else
input "exit-all"
raise "REPL didn't die" unless @thread[:session_ended]
end
end
private
def reset_output
Pry.output.clear
end
def repl_mailbox
@thread[:mailbox]
end
def wait
@last_prompt = mailbox.pop
end
end

View file

@ -1,90 +0,0 @@
# coding: utf-8
require 'helper'
describe "Pry#input_stack" do
before do
@str_output = StringIO.new
end
it 'should accept :input_stack as a config option' do
stack = [StringIO.new("test")]
Pry.new(:input_stack => stack).input_stack.should == stack
end
it 'should use defaults from Pry.config' do
Pry.config.input_stack = [StringIO.new("exit")]
Pry.new.input_stack.should == Pry.config.input_stack
Pry.config.input_stack = []
end
it 'should read from all input objects on stack and exit session (usingn repl)' do
stack = [b = StringIO.new(":cloister\nexit\n"), c = StringIO.new(":baron\n")]
instance = Pry.new(:input => StringIO.new(":alex\n"),
:output => @str_output,
:input_stack => stack)
instance.repl
@str_output.string.should =~ /:alex/
@str_output.string.should =~ /:baron/
@str_output.string.should =~ /:cloister/
end
it 'input objects should be popped off stack as they are used up' do
stack = [StringIO.new(":cloister\n"), StringIO.new(":baron\n")]
instance = Pry.new(:input => StringIO.new(":alex\n"),
:output => @str_output,
:input_stack => stack)
stack.size.should == 2
instance.rep
@str_output.string.should =~ /:alex/
instance.rep
@str_output.string.should =~ /:baron/
stack.size.should == 1
instance.rep
@str_output.string.should =~ /:cloister/
stack.size.should == 0
end
it 'should revert to Pry.config.input when it runs out of input objects in input_stack' do
redirect_pry_io(StringIO.new(":rimbaud\nexit\n"), StringIO.new) do
stack = [StringIO.new(":cloister\n"), StringIO.new(":baron\n")]
instance = Pry.new(:input => StringIO.new(":alex\n"),
:output => @str_output,
:input_stack => stack)
instance.repl
@str_output.string.should =~ /:alex/
@str_output.string.should =~ /:baron/
@str_output.string.should =~ /:cloister/
@str_output.string.should =~ /:rimbaud/
end
end
it 'should display error and throw(:breakout) if at end of input after using up input_stack objects' do
catch(:breakout) do
redirect_pry_io(StringIO.new(":rimbaud\n"), @str_output) do
Pry.new(:input_stack => [StringIO.new(":a\n"), StringIO.new(":b\n")]).repl
end
end
@str_output.string.should =~ /Error: Pry ran out of things to read/
end
if "".respond_to?(:encoding)
after do
Pry.line_buffer = [""]
Pry.current_line = 1
end
it "should pass strings to Pry in the right encoding" do
input1 = "'f。。'.encoding.name" # utf-8, see coding declaration
input2 = input1.encode('Shift_JIS')
mock_pry(input1, input2).should == %{=> "UTF-8"\n=> "Shift_JIS"\n\n}
end
it "should be able to use unicode regexes on a UTF-8 terminal" do
mock_pry('":-Þ" =~ /þ/i').should == %{=> 2\n\n}
end
end
end

View file

@ -23,6 +23,7 @@ describe "Prompts" do
redirect_pry_io(InputTester.new("def hello", "exit-all")) do
Pry.start(self, :prompt => proc { |v| config = v })
end
config.eval_string.should =~ /def hello/
config.nesting_level.should == 0
config.expr_number.should == 1

View file

@ -20,12 +20,12 @@ describe "test Pry defaults" do
it 'should set the input default, and the default should be overridable' do
Pry.input = InputTester.new("5")
Pry.output = @str_output
Pry.new.rep
Object.new.pry
@str_output.string.should =~ /5/
Pry.new(:input => InputTester.new("6")).rep
Pry.output = @str_output
Object.new.pry :input => InputTester.new("6")
@str_output.string.should =~ /6/
end
@ -75,18 +75,19 @@ describe "test Pry defaults" do
end
it 'should set the output default, and the default should be overridable' do
Pry.input = InputTester.new("5", "6", "7")
Pry.output = @str_output
Pry.new.rep
Pry.input = InputTester.new("5")
Object.new.pry
@str_output.string.should =~ /5/
Pry.new.rep
Pry.input = InputTester.new("6")
Object.new.pry
@str_output.string.should =~ /5\n.*6/
Pry.input = InputTester.new("7")
@str_output = StringIO.new
Pry.new(:output => @str_output).rep
Object.new.pry :output => @str_output
@str_output.string.should.not =~ /5\n.*6/
@str_output.string.should =~ /7/
end
@ -96,17 +97,17 @@ describe "test Pry defaults" do
Pry.print = new_print
Pry.new.print.should == Pry.print
Pry.new(:input => InputTester.new("\"test\""), :output => @str_output).rep
Object.new.pry :input => InputTester.new("\"test\""), :output => @str_output
@str_output.string.should == "=> LOL\n"
@str_output = StringIO.new
Pry.new(:input => InputTester.new("\"test\""), :output => @str_output,
:print => proc { |out, value| out.puts value.reverse }).rep
Object.new.pry :input => InputTester.new("\"test\""), :output => @str_output,
:print => proc { |out, value| out.puts value.reverse }
@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
Object.new.pry :input => InputTester.new("\"test\""), :output => @str_output
@str_output.string.should == "=> LOL\n"
end
@ -134,47 +135,51 @@ describe "test Pry defaults" do
describe "prompts" do
before do
@empty_input_buffer = ""
@non_empty_input_buffer = "def hello"
@context = Pry.binding_for(0)
Pry.output = StringIO.new
end
def get_prompts(pry)
a = pry.select_prompt
pry.eval "["
b = pry.select_prompt
pry.eval "]"
[a, b]
end
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(@empty_input_buffer, @context).should == "test prompt> "
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt> "
Pry.prompt = proc { "test prompt> " }
new_prompt = proc { "A" }
pry_tester = Pry.new(:prompt => new_prompt)
pry_tester.prompt.should == new_prompt
pry_tester.select_prompt(@empty_input_buffer, @context).should == "A"
pry_tester.select_prompt(@non_empty_input_buffer, @context).should == "A"
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(@empty_input_buffer, @context).should == "test prompt> "
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt> "
pry = Pry.new
pry.prompt.should == Pry.prompt
get_prompts(pry).should == ["test prompt> ", "test prompt> "]
pry = Pry.new(:prompt => new_prompt)
pry.prompt.should == new_prompt
get_prompts(pry).should == ["A", "A"]
pry = Pry.new
pry.prompt.should == Pry.prompt
get_prompts(pry).should == ["test prompt> ", "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(@empty_input_buffer, @context).should == "test prompt> "
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt* "
Pry.prompt = [proc { "test prompt> " }, proc { "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(@empty_input_buffer, @context).should == "A"
pry_tester.select_prompt(@non_empty_input_buffer, @context).should == "B"
Pry.new.prompt.should == Pry.prompt
Pry.new.select_prompt(@empty_input_buffer, @context).should == "test prompt> "
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt* "
pry = Pry.new
pry.prompt.should == Pry.prompt
get_prompts(pry).should == ["test prompt> ", "test prompt* "]
pry = Pry.new(:prompt => new_prompt)
pry.prompt.should == new_prompt
get_prompts(pry).should == ["A", "B"]
pry = Pry.new
pry.prompt.should == Pry.prompt
get_prompts(pry).should == ["test prompt> ", "test prompt* "]
end
describe 'storing and restoring the prompt' do
@ -198,13 +203,12 @@ describe "test Pry defaults" do
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(@empty_input_buffer, @context).should == "P>"
pry.re
pry.select_prompt(@empty_input_buffer, @context).should =~ /\Apry .* \$ \z/
pry.re
pry.select_prompt(@empty_input_buffer, @context).should == "P>"
pry = Pry.new(:prompt => [ proc { 'P>' } ] * 2)
pry.select_prompt.should == "P>"
pry.process_command('shell-mode')
pry.select_prompt.should =~ /\Apry .* \$ \z/
pry.process_command('shell-mode')
pry.select_prompt.should == "P>"
end
it '#pop_prompt should return the popped prompt' do
@ -378,18 +382,17 @@ describe "test Pry defaults" do
add_hook(:before_session, :my_name) { |out,_,_| out.puts "HELLO" }.
add_hook(:after_session, :my_name) { |out,_,_| out.puts "BYE" }
Pry.new(:output => @str_output).repl
Object.new.pry :output => @str_output
@str_output.string.should =~ /HELLO/
@str_output.string.should =~ /BYE/
Pry.input.rewind
@str_output = StringIO.new
Pry.new(:output => @str_output,
:hooks => Pry::Hooks.new.
add_hook( :before_session, :my_name) { |out,_,_| out.puts "MORNING" }.
add_hook(:after_session, :my_name) { |out,_,_| out.puts "EVENING" }
).repl
Object.new.pry :output => @str_output,
:hooks => Pry::Hooks.new.
add_hook( :before_session, :my_name) { |out,_,_| out.puts "MORNING" }.
add_hook(:after_session, :my_name) { |out,_,_| out.puts "EVENING" }
@str_output.string.should =~ /MORNING/
@str_output.string.should =~ /EVENING/
@ -397,19 +400,17 @@ describe "test Pry defaults" do
# try below with just defining one hook
Pry.input.rewind
@str_output = StringIO.new
Pry.new(:output => @str_output,
:hooks => Pry::Hooks.new.
add_hook(:before_session, :my_name) { |out,_,_| out.puts "OPEN" }
).repl
Object.new.pry :output => @str_output,
:hooks => Pry::Hooks.new.
add_hook(:before_session, :my_name) { |out,_,_| out.puts "OPEN" }
@str_output.string.should =~ /OPEN/
Pry.input.rewind
@str_output = StringIO.new
Pry.new(:output => @str_output,
:hooks => Pry::Hooks.new.
add_hook(:after_session, :my_name) { |out,_,_| out.puts "CLOSE" }
).repl
Object.new.pry :output => @str_output,
:hooks => Pry::Hooks.new.
add_hook(:after_session, :my_name) { |out,_,_| out.puts "CLOSE" }
@str_output.string.should =~ /CLOSE/

View file

@ -11,10 +11,6 @@ describe Pry do
@saved_history.lines.each { |l| blk.call(l) }
end
Pry.history.saver = proc do |lines|
@saved_history << lines.map { |l| "#{l}\n" }.join
end
Pry.load_history
end
@ -44,56 +40,30 @@ describe Pry do
end
end
describe ".save_history" do
it "should include a trailing newline" do
Pry.history << "4"
Pry.save_history
@saved_history.should =~ /4\n\z/
describe "saving to a file" do
before do
@histfile = Tempfile.new(["pryhistory", "txt"])
@history = Pry::History.new(:file_path => @histfile.path)
Pry.config.history.should_save = true
@history.pusher = proc{ }
end
it "should not change anything if history is not changed" do
@saved_history = "4\n5\n6\n"
Pry.save_history
@saved_history.should == "4\n5\n6\n"
after do
@histfile.close(true)
Pry.config.history.should_save = false
end
it "should append new lines to the file" do
Pry.history << "4"
Pry.save_history
@saved_history.should == "1\n2\n3\n4\n"
it "should save lines to a file as they are written" do
@history.push "5"
File.read(@histfile.path).should == "5\n"
end
it "should not clobber lines written by other Pry's in the meantime" do
Pry.history << "5"
@saved_history << "4\n"
Pry.save_history
it "should interleave lines from many places" do
@history.push "5"
File.open(@histfile.path, 'a'){ |f| f.puts "6" }
@history.push "7"
Pry.history.to_a[-3..-1].should == ["2", "3", "5"]
@saved_history.should == "1\n2\n3\n4\n5\n"
end
it "should not delete lines from the file if this session's history was cleared" do
Pry.history.clear
Pry.save_history
@saved_history.should == "1\n2\n3\n"
end
it "should save new lines that are added after the history was cleared" do
Pry.history.clear
Pry.history << "4"
Pry.save_history
@saved_history.should =~ /1\n2\n3\n4\n/
end
it "should only append new lines the second time it is saved" do
Pry.history << "4"
Pry.save_history
@saved_history << "5\n"
Pry.history << "6"
Pry.save_history
Pry.history.to_a[-4..-1].should == ["2", "3", "4", "6"]
@saved_history.should == "1\n2\n3\n4\n5\n6\n"
File.read(@histfile.path).should == "5\n6\n7\n"
end
end
end

View file

@ -77,19 +77,19 @@ describe Pry do
@t = pry_tester
end
it "should normally output the result" do
mock_pry("1 + 2").should == "=> 3\n\n"
mock_pry("1 + 2").should == "=> 3\n"
end
it "should not output anything if the input ends with a semicolon" do
mock_pry("1 + 2;").should == "\n"
mock_pry("1 + 2;").should == ""
end
it "should output something if the input ends with a comment" do
mock_pry("1 + 2 # basic addition").should == "=> 3\n\n"
mock_pry("1 + 2 # basic addition").should == "=> 3\n"
end
it "should not output something if the input is only a comment" do
mock_pry("# basic addition").should == "\n"
mock_pry("# basic addition").should == ""
end
end
end

86
spec/pry_repl_spec.rb Normal file
View file

@ -0,0 +1,86 @@
require 'helper'
describe "The whole thing" do
it "should let you run commands in the middle of multiline expressions" do
ReplTester.start do
input 'def a'
input '!'
output /^Input buffer cleared/
input '5'
output '=> 5'
end
end
describe "eval_string and binding_stack" do
it "shouldn't break if we start a nested REPL" do
ReplTester.start do
input 'Pry::REPL.new(_pry_, :target => 10).start'
output ''
prompt /10.*> $/
input 'self'
output '=> 10'
input nil # Ctrl-D
output ''
input 'self'
output '=> main'
end
end
it "shouldn't break if we start a nested instance" do
ReplTester.start do
input 'Pry.start(10)'
output ''
prompt /10.*> $/
input 'self'
output '=> 10'
input nil # Ctrl-D
output '=> nil' # return value of Pry session
input 'self'
output '=> main'
end
end
it "shouldn't break if we pop bindings in Ruby" do
ReplTester.start do
input 'cd 10'
output ''
prompt /10.*> $/
input '_pry_.binding_stack.pop'
output /^=> #<Binding/
prompt /main.*> $/
input '_pry_.binding_stack.pop'
output /^=> #<Binding/
assert_exited
end
end
it "should immediately evaluate eval_string after cmd if complete" do
set = Pry::CommandSet.new do
import Pry::Commands
command 'hello!' do
eval_string.replace('"hello"')
end
end
ReplTester.start(:commands => set) do
input 'def x'
output ''
prompt /\* $/
input 'hello!'
output '=> "hello"'
prompt /> $/
end
end
end
end

View file

@ -10,11 +10,9 @@ describe Pry do
# regression test for exotic object support
it "Should not error when return value is a BasicObject instance" do
lambda do
redirect_pry_io(InputTester.new("BasicObject.new", "exit-all"), StringIO.new) do
Pry.start
end
end.should.not.raise NoMethodError
ReplTester.start do
input('BasicObject.new').should =~ /^=> #<BasicObject:/
end
end
end
@ -81,111 +79,66 @@ describe Pry do
# bug fix for https://github.com/banister/pry/issues/93
it 'should not leak pry constants into Object namespace' do
input_string = "Command"
o = Object.new
pry_tester = Pry.new(:input => StringIO.new(input_string),
:output => @str_output,
:exception_handler => proc { |_, exception, _pry_| @excep = exception },
:print => proc {}
).rep(o)
@excep.is_a?(NameError).should == true
lambda{
pry_eval(Object.new, "Command")
}.should.raise(NameError)
end
if defined?(BasicObject)
it 'should be able to operate inside the BasicObject class' do
redirect_pry_io(InputTester.new(":foo", "Pad.obj = _", "exit-all")) do
BasicObject.pry
end
pry_eval(BasicObject, ":foo", "Pad.obj = _")
Pad.obj.should == :foo
end
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 => StringIO.new)
pry_tester.rep(o)
pry_eval(o, "@x = 10")
o.instance_variable_get(:@x).should == 10
end
it 'should display error if Pry instance runs out of input' do
redirect_pry_io(StringIO.new, @str_output) do
Pry.new.repl
Pry.start
end
@str_output.string.should =~ /Error: Pry ran out of things to read/
end
it 'should make self evaluate to the receiver of the rep session' do
o = :john
pry_tester = Pry.new(:input => InputTester.new("self"), :output => @str_output)
pry_tester.rep(o)
@str_output.string.should =~ /:john/
pry_eval(o, "self").should == o
end
it 'should work with multi-line input' do
o = Object.new
pry_tester = Pry.new(:input => InputTester.new("x = ", "1 + 4"), :output => @str_output)
pry_tester.rep(o)
@str_output.string.should =~ /5/
mock_pry("x = ", "1 + 4").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 => StringIO.new)
pry_tester.rep(Hello)
mock_pry(Pry.binding_for(Hello), "class Nested", "end")
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
pry_tester = Pry.new(:input => InputTester.new("Exception.new;"), :output => @str_output)
pry_tester.rep(o)
@str_output.string.should == ""
mock_pry("Exception.new;").should == ""
end
it 'should suppress output if input ends in a ";" (single line)' do
o = Object.new
pry_tester = Pry.new(:input => InputTester.new("x = 5;"), :output => @str_output)
pry_tester.rep(o)
@str_output.string.should == ""
mock_pry("x = 5;").should == ""
end
it 'should suppress output if input ends in a ";" (multi-line)' do
o = Object.new
pry_tester = Pry.new(:input => InputTester.new("def self.blah", ":test", "end;"), :output => @str_output)
pry_tester.rep(o)
@str_output.string.should == ""
mock_pry("def self.blah", ":test", "end;").should == ""
end
it 'should be able to evaluate exceptions normally' do
o = Exception.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)
mock_pry("RuntimeError.new", :exception_handler => proc{ was_called = true })
was_called.should == false
end
it 'should notice when exceptions are raised' do
o = Exception.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)
mock_pry("raise RuntimeError", :exception_handler => proc{ was_called = true })
was_called.should == true
end
@ -221,62 +174,46 @@ describe Pry do
describe "history arrays" do
it 'sets _ to the last result' do
res = []
input = InputTester.new *[":foo", "self << _", "42", "self << _"]
pry = Pry.new(:input => input, :output => StringIO.new)
pry.repl(res)
res.should == [:foo, 42]
t = pry_tester
t.eval ":foo"
t.eval("_").should == :foo
t.eval "42"
t.eval("_").should == 42
end
it 'sets out to an array with the result' do
res = {}
input = InputTester.new *[":foo", "42", "self[:res] = _out_"]
pry = Pry.new(:input => input, :output => StringIO.new)
pry.repl(res)
t = pry_tester
t.eval ":foo"
t.eval "42"
res = t.eval "_out_"
res[:res].should.be.kind_of Pry::HistoryArray
res[:res][1..2].should == [:foo, 42]
res.should.be.kind_of Pry::HistoryArray
res[1..2].should == [:foo, 42]
end
it 'sets _in_ to an array with the entered lines' do
res = {}
input = InputTester.new *[":foo", "42", "self[:res] = _in_"]
pry = Pry.new(:input => input, :output => StringIO.new)
pry.repl(res)
t = pry_tester
t.eval ":foo"
t.eval "42"
res = t.eval "_in_"
res[:res].should.be.kind_of Pry::HistoryArray
res[:res][1..2].should == [":foo\n", "42\n"]
res.should.be.kind_of Pry::HistoryArray
res[1..2].should == [":foo\n", "42\n"]
end
it 'uses 100 as the size of _in_ and _out_' do
res = []
input = InputTester.new *["self << _out_.max_size << _in_.max_size"]
pry = Pry.new(:input => input, :output => StringIO.new)
pry.repl(res)
res.should == [100, 100]
pry_tester.eval("[_in_.max_size, _out_.max_size]").should == [100, 100]
end
it 'can change the size of the history arrays' do
res = []
input = InputTester.new *["self << _out_.max_size << _in_.max_size"]
pry = Pry.new(:input => input, :output => StringIO.new,
:memory_size => 1000)
pry.repl(res)
res.should == [1000, 1000]
pry_tester(:memory_size => 1000).eval("[_out_, _in_].map(&:max_size)").should == [1000, 1000]
end
it 'store exceptions' do
res = []
input = InputTester.new *["foo!","self << _in_[-1] << _out_[-1]"]
pry = Pry.new(:input => input, :output => StringIO.new,
:memory_size => 1000)
pry.repl(res)
mock_pry("foo!", "Pad.in = _in_[-1]; Pad.out = _out_[-1]")
res.first.should == "foo!\n"
res.last.should.be.kind_of NoMethodError
Pad.in.should == "foo!\n"
Pad.out.should.be.kind_of NoMethodError
end
end
@ -346,13 +283,6 @@ describe Pry do
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-all\n"), :output => StringIO.new).repl(self)
Object.const_defined?(:TEST_RC).should == false
Pry.config.should_load_rc = false
end
describe "that raise exceptions" do
before do
Pry::HOME_RC_FILE = "spec/fixtures/testrcbad"
@ -417,24 +347,20 @@ describe Pry do
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)
pry_eval(val, 'def hello; end')
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)
pry_eval(hello, "def hello; end")
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)
pry_eval(hello, "def hello; end")
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
@ -442,8 +368,7 @@ describe Pry 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)
pry_eval(val, "def hello; end");
val.class.instance_methods(false).map(&:to_sym).include?(:hello).should == true
end
end
@ -501,4 +426,14 @@ describe Pry do
}
end
end
describe "a fresh instance" do
it "should use `caller` as its backtrace" do
location = "#{__FILE__}:#{__LINE__ + 1}"
backtrace = Pry.new.backtrace
backtrace.should.not.be.nil
backtrace.any? { |l| l.include?(location) }.should.be.true
end
end
end

View file

@ -122,31 +122,21 @@ describe "Sticky locals (_file_ and friends)" do
end
it 'should create a new sticky local' do
o = Object.new
pi = Pry.new
pi.add_sticky_local(:test_local) { :test_value }
pi.input, pi.output = InputTester.new("@value = test_local", "exit-all"), StringIO.new
pi.repl(o)
o.instance_variable_get(:@value).should == :test_value
t = pry_tester
t.eval "_pry_.add_sticky_local(:test_local){ :test_value }"
t.eval("test_local").should == :test_value
end
it 'should still exist after cd-ing into new binding' do
o = Object.new
o2 = Object.new
o.instance_variable_set(:@o2, o2)
pi = Pry.new
pi.add_sticky_local(:test_local) { :test_value }
pi.input = InputTester.new("cd @o2\n",
"@value = test_local", "exit-all")
pi.output = StringIO.new
pi.repl(o)
o2.instance_variable_get(:@value).should == :test_value
t = pry_tester
t.eval "_pry_.add_sticky_local(:test_local){ :test_value }"
t.eval "cd Object.new"
t.eval("test_local").should == :test_value
end
it 'should provide different values for successive block invocations' do
pry = Pry.new
pry.push_binding binding
pry.add_sticky_local(:test_local) { rand }
value1 = pry.evaluate_ruby 'test_local'
value2 = pry.evaluate_ruby 'test_local'