Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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.
  • Loading branch information...
commit c7bb475c61a6d86f949b5bea4f234a6984afb84a 1 parent 3fa74ea
@banister banister authored
View
13 lib/pry/cli.rb
@@ -14,6 +14,10 @@ class << self
# @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 @@ def reset
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 @@ def parse_options(args=ARGV.dup)
: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
17 lib/pry/pry_class.rb
@@ -62,7 +62,7 @@ def self.load_rc
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 @@ def self.start(target=toplevel_binding, options={})
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
3  lib/pry/pry_instance.rb
@@ -517,6 +517,7 @@ def handle_read_errors
begin
yield
rescue EOFError
+ old_input = self.input
if input_stack.empty?
self.input = Pry.config.input
if !should_retry
@@ -527,6 +528,8 @@ def handle_read_errors
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
80 lib/pry/repl_file_loader.rb
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.