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:
parent
3fa74ea862
commit
c7bb475c61
4 changed files with 111 additions and 2 deletions
|
@ -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 = ""
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
80
lib/pry/repl_file_loader.rb
Normal file
80
lib/pry/repl_file_loader.rb
Normal 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
|
Loading…
Add table
Reference in a new issue