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

provide CLI 'pry my_file.rb' functionality to load file through REPL loop

This is an interesting trick as it processes your file as if it
was user input in an interactive session. As a result, all Pry
commands are available, and they are executed non-interactively. Furthermore
the session becomes interactive when the repl loop processes a
'make-interactive' command in the file. The session also becomes
interactive when an exception is encountered, enabling you to fix
the error before returning to non-interactive processing with the
'make-non-interactive' command.
This commit is contained in:
John Mair 2012-06-17 22:50:18 +12:00
parent 3fa74ea862
commit c7bb475c61
4 changed files with 111 additions and 2 deletions

View file

@ -14,6 +14,10 @@ class Pry
# @return [Array] The Procs that process the parsed options.
attr_accessor :option_processors
# @return [Array<String>] The input array of strings to process
# as CLI options.
attr_accessor :input_args
# Add another set of CLI options (a Slop block)
def add_options(&block)
if options
@ -55,6 +59,8 @@ class Pry
def parse_options(args=ARGV.dup)
raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to add command line options." if !options
self.input_args = args
opts = Slop.parse(args, :help => true, :multiple_switches => false, &options)
option_processors.each { |processor| processor.call(opts) } if option_processors # option processors are optional
@ -134,13 +140,18 @@ Copyright (c) 2011 John Mair (banisterfiend)
:default => "Pry.toplevel_binding"
)
end.process_options do |opts|
# invoked via cli
Pry.cli = true
# create the actual context
context = Pry.binding_for(eval(opts[:context]))
if opts[:exec]
if Pry::CLI.input_args.any? && Pry::CLI.input_args != ["pry"]
full_name = File.expand_path(Pry::CLI.input_args.first)
Pry.load_file_through_repl(full_name)
exit
elsif opts[:exec]
exec_string = opts[:exec] + "\n"
else
exec_string = ""

View file

@ -62,7 +62,7 @@ class Pry
files = RC_FILES.collect { |file_name| File.expand_path(file_name) }.uniq
files.each do |file_name|
begin
load(file_name) if File.exists?(file_name)
toplevel_binding.eval(File.read(file_name)) if File.exists?(file_name)
rescue RescuableException => e
puts "Error loading #{file_name}: #{e}"
end
@ -145,6 +145,21 @@ class Pry
pry_instance.repl(head)
end
# @return [Array<String>] return rc files with expanded path.
def self.rc_files
RC_FILES.collect do |file_name|
full_name = File.expand_path(file_name)
File.expand_path(file_name) if File.exists?(full_name)
end.uniq.compact
end
# Execute the file through the REPL loop, non-interactively.
# @param [String] file_name File name to load through the REPL.
def self.load_file_through_repl(file_name)
require "pry/repl_file_loader"
REPLFileLoader.new(file_name).load
end
# An inspector that clips the output to `max_length` chars.
# In case of > `max_length` chars the `#<Object...> notation is used.
# @param obj The object to view.

View file

@ -517,6 +517,7 @@ class Pry
begin
yield
rescue EOFError
old_input = self.input
if input_stack.empty?
self.input = Pry.config.input
if !should_retry
@ -527,6 +528,8 @@ class Pry
else
self.input = input_stack.pop
end
exec_hook :input_object_changed, old_input, self.input, self
retry
# Interrupts are handled in r() because they need to tweak eval_string

View file

@ -0,0 +1,80 @@
class Pry
# A class to manage the loading of files through the REPL loop.
# This is an interesting trick as it processes your file as if it
# was user input in an interactive session. As a result, all Pry
# commands are available, and they are executed non-interactively. Furthermore
# the session becomes interactive when the repl loop processes a
# 'make-interactive' command in the file. The session also becomes
# interactive when an exception is encountered, enabling you to fix
# the error before returning to non-interactive processing with the
# 'make-non-interactive' command.
class REPLFileLoader
def initialize(file_name)
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))
end
# Switch to interactive mode, i.e take input from the user
# and use the regular print and exception handlers.
# @param [Pry] _pry_ the Pry instance to make interactive.
def interactive_mode(_pry_)
_pry_.input = Pry.config.input
_pry_.print = Pry.config.print
_pry_.exception_handler = Pry.config.exception_handler
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_)
_pry_.print = proc {}
_pry_.exception_handler = proc do |o, e, _pry_|
_pry_.run_command "cat --ex"
o.puts "...exception encountered, going interactive!"
interactive_mode(_pry_)
end
end
# Define a few extra commands useful for flipping back & forth
# between interactive/non-interactive modes
def define_additional_commands
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
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")])
end
end
end