mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
version bump to 0.1.3 and slight aesthetic changes to code
This commit is contained in:
parent
baeaebfa5b
commit
a25c40d2db
6 changed files with 223 additions and 62 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
9/12/2010 version 0.1.3
|
||||||
|
* Got rid of rubygems dependency, refactored some code.
|
||||||
8/12/2010 version 0.1.2
|
8/12/2010 version 0.1.2
|
||||||
* now rescuing SyntaxError as well as Racc::Parser error in valid_expression?
|
* now rescuing SyntaxError as well as Racc::Parser error in valid_expression?
|
||||||
8/12/2010 version 0.1.0
|
8/12/2010 version 0.1.0
|
||||||
|
|
|
@ -8,7 +8,7 @@ _attach an irb-like session to any object at runtime_
|
||||||
Pry is a simple Ruby REPL (Read-Eval-Print-Loop) that specializes in the interactive
|
Pry is a simple Ruby REPL (Read-Eval-Print-Loop) that specializes in the interactive
|
||||||
manipulation of objects during the running of a program.
|
manipulation of objects during the running of a program.
|
||||||
|
|
||||||
It is not based on the IRB codebase and is small, at around 120 LOC.
|
It is not based on the IRB codebase and is small, at around 230 LOC.
|
||||||
|
|
||||||
* Install the [gem](https://rubygems.org/gems/pry): `gem install pry`
|
* Install the [gem](https://rubygems.org/gems/pry): `gem install pry`
|
||||||
* Read the [documentation](http://rdoc.info/github/banister/pry/master/file/README.markdown)
|
* Read the [documentation](http://rdoc.info/github/banister/pry/master/file/README.markdown)
|
||||||
|
@ -17,7 +17,7 @@ It is not based on the IRB codebase and is small, at around 120 LOC.
|
||||||
example: Interacting with an object at runtime
|
example: Interacting with an object at runtime
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
With the `Pry.into()` method we can pry (open an irb-like session) on
|
With the `Pry.start()` method we can pry (open an irb-like session) on
|
||||||
an object. In the example below we open a Pry session for the `Test` class and execute a method and add
|
an object. In the example below we open a Pry session for the `Test` class and execute a method and add
|
||||||
an instance variable. The current thread is halted for the duration of the session.
|
an instance variable. The current thread is halted for the duration of the session.
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ an instance variable. The current thread is halted for the duration of the sessi
|
||||||
def self.hello() "hello world" end
|
def self.hello() "hello world" end
|
||||||
end
|
end
|
||||||
|
|
||||||
Pry.into(Test)
|
Pry.start(Test)
|
||||||
|
|
||||||
# Pry session begins on stdin
|
# Pry session begins on stdin
|
||||||
Beginning Pry session for Test
|
Beginning Pry session for Test
|
||||||
|
@ -47,6 +47,12 @@ effect:
|
||||||
|
|
||||||
Test.instance_variable_get(:@y) #=> 20
|
Test.instance_variable_get(:@y) #=> 20
|
||||||
|
|
||||||
|
Note: you can also use the `obj.pry` syntax to start a pry session on
|
||||||
|
`obj`. e.g
|
||||||
|
|
||||||
|
5.pry
|
||||||
|
Beginning Pry session for 5
|
||||||
|
pry(5)>
|
||||||
|
|
||||||
example: Pry sessions can nest arbitrarily deep so we can pry on objects inside objects:
|
example: Pry sessions can nest arbitrarily deep so we can pry on objects inside objects:
|
||||||
----------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------
|
||||||
|
@ -54,28 +60,63 @@ example: Pry sessions can nest arbitrarily deep so we can pry on objects inside
|
||||||
Here we will begin Pry at top-level, then pry on a class and then on
|
Here we will begin Pry at top-level, then pry on a class and then on
|
||||||
an instance variable inside that class:
|
an instance variable inside that class:
|
||||||
|
|
||||||
# Pry.into() without parameters begins a Pry session on top-level (main)
|
# Pry.start() without parameters begins a Pry session on top-level (main)
|
||||||
Pry.into
|
Pry.start
|
||||||
Beginning Pry session for main
|
Beginning Pry session for main
|
||||||
pry(main)> class Hello
|
pry(main)> class Hello
|
||||||
pry(main)* @x = 20
|
pry(main)* @x = 20
|
||||||
pry(main)* end
|
pry(main)* end
|
||||||
=> 20
|
=> 20
|
||||||
pry(main)> Pry.into Hello
|
pry(main)> Pry.start Hello
|
||||||
Beginning Pry session for Hello
|
Beginning Pry session for Hello
|
||||||
pry(Hello)> instance_variables
|
pry(Hello):1> instance_variables
|
||||||
=> [:@x]
|
=> [:@x]
|
||||||
pry(Hello)> Pry.into @x
|
pry(Hello):1> Pry.start @x
|
||||||
Beginning Pry session for 20
|
Beginning Pry session for 20
|
||||||
pry(20)> self + 10
|
pry(20:2)> self + 10
|
||||||
=> 30
|
=> 30
|
||||||
pry(20)> exit
|
pry(20:2)> exit
|
||||||
Ending Pry session for 20
|
Ending Pry session for 20
|
||||||
pry(Hello)> exit
|
pry(Hello):1> exit
|
||||||
Ending Pry session for Hello
|
Ending Pry session for Hello
|
||||||
pry(main)> exit
|
pry(main)> exit
|
||||||
Ending Pry session for main
|
Ending Pry session for main
|
||||||
|
|
||||||
|
The number after the `:` in the pry prompt indicates the nesting
|
||||||
|
level. To display more information about nesting, use the `nesting`
|
||||||
|
command. E.g
|
||||||
|
|
||||||
|
pry("friend":3)> nesting
|
||||||
|
Nesting status:
|
||||||
|
0. main (Pry top level)
|
||||||
|
1. Hello
|
||||||
|
2. 100
|
||||||
|
3. "friend"
|
||||||
|
=> nil
|
||||||
|
|
||||||
|
We can then jump back to any of the previous nesting levels by using
|
||||||
|
the `jump_to` or `exit_at` commands:
|
||||||
|
|
||||||
|
pry(100:2)> jump_to 1
|
||||||
|
Ending Pry session for "friend"
|
||||||
|
Ending Pry session for 100
|
||||||
|
=> 100
|
||||||
|
pry(Hello):1>
|
||||||
|
|
||||||
|
If we just want to go back one level of nesting we can of course just
|
||||||
|
use the `quit` or `exit` or `back` commands.
|
||||||
|
|
||||||
|
To breakout of all levels of pry nesting and return immediately to the
|
||||||
|
calling process use `exit_all`:
|
||||||
|
|
||||||
|
pry("friend":3) exit_all
|
||||||
|
Ending Pry session for "friend"
|
||||||
|
Ending Pry session for 100
|
||||||
|
Ending Pry session for Hello
|
||||||
|
Ending Pry session for main
|
||||||
|
=> main
|
||||||
|
|
||||||
|
# program resumes here
|
||||||
|
|
||||||
Features and limitations
|
Features and limitations
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -91,13 +132,14 @@ end.
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
* Pry can be invoked at any time and on any object in the running program.
|
* Pry can be invoked at any time and on any object in the running program.
|
||||||
* Pry sessions can nest arbitrarily deeply -- to go back one level of nesting type 'exit' or 'quit'
|
* Pry sessions can nest arbitrarily deeply -- to go back one level of nesting type 'exit' or 'quit' or 'back'
|
||||||
|
* Use `_` to recover last result.
|
||||||
* Pry has multi-line support built in.
|
* Pry has multi-line support built in.
|
||||||
* Pry is not based on the IRB codebase.
|
* Pry is not based on the IRB codebase.
|
||||||
* Pry is Only 120 LOC.
|
* Pry is small; around 230 LOC.
|
||||||
* Pry implements all the methods in the REPL chain separately: `Pry.r`
|
* Pry implements all the methods in the REPL chain separately: `Pry.r`
|
||||||
for reading; `Pry.re` for eval; `Pry.rep` for printing; and `Pry.repl`
|
for reading; `Pry.re` for eval; `Pry.rep` for printing; and `Pry.repl`
|
||||||
for the loop (`Pry.into` is simply an alias for `Pry.repl`). You can
|
for the loop (`Pry.start` is simply an alias for `Pry.repl`). You can
|
||||||
invoke any of these methods directly depending on exactly what aspect of the functionality you need.
|
invoke any of these methods directly depending on exactly what aspect of the functionality you need.
|
||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
|
@ -119,11 +161,12 @@ Commands
|
||||||
|
|
||||||
The Pry API:
|
The Pry API:
|
||||||
|
|
||||||
* `Pry.into()` and `Pry.start()` and `Pry.repl()` are all aliases of
|
* `Pry.start()` and `Pry.into()` and `Pry.repl()` are all aliases of
|
||||||
oneanother. They all start a Read-Eval-Print-Loop on the object they
|
oneanother. They all start a Read-Eval-Print-Loop on the object they
|
||||||
receive as a parameter. In the case of no parameter they operate on
|
receive as a parameter. In the case of no parameter they operate on
|
||||||
top-level (main). They can receive any object or a `Binding`
|
top-level (main). They can receive any object or a `Binding`
|
||||||
object as parameter.
|
object as parameter.
|
||||||
|
* `obj.pry` may also be used as an alternate syntax to `Pry.start(obj)`
|
||||||
* If, for some reason you do not want to 'loop' then use `Pry.rep()`; it
|
* If, for some reason you do not want to 'loop' then use `Pry.rep()`; it
|
||||||
only performs the Read-Eval-Print section of the REPL - it ends the
|
only performs the Read-Eval-Print section of the REPL - it ends the
|
||||||
session after just one line of input. It takes the same parameters as
|
session after just one line of input. It takes the same parameters as
|
||||||
|
@ -134,16 +177,26 @@ case of error. It also takes the same parameters as `Pry.repl()`
|
||||||
* Similarly `Pry.r()` only performs the Read section of the REPL, only
|
* Similarly `Pry.r()` only performs the Read section of the REPL, only
|
||||||
returning the Ruby expression (as a string). It takes the same parameters as all the others.
|
returning the Ruby expression (as a string). It takes the same parameters as all the others.
|
||||||
|
|
||||||
Pry supports a few commands inside the session itself:
|
Pry supports a few commands inside the session itself; these are
|
||||||
|
not methods and must start at the beginning of a line, with no
|
||||||
|
whitespace in between.
|
||||||
|
|
||||||
|
If you want to access a method of the same name, prefix the invocation by whitespace.
|
||||||
|
|
||||||
* Typing `!` on a line by itself will refresh the REPL - useful for
|
* Typing `!` on a line by itself will refresh the REPL - useful for
|
||||||
getting you out of a situation if the parsing process
|
getting you out of a situation if the parsing process
|
||||||
goes wrong.
|
goes wrong.
|
||||||
* `exit` or `quit` will end the current Pry session. Note that it will
|
* `exit` or `quit` or `back` will end the current Pry session and go
|
||||||
not end any containing Pry sessions if the current session happens
|
back to the calling process or back one level of nesting.
|
||||||
to be nested.
|
* `exit_program` or `quit_program` will end the currently running
|
||||||
* `#exit` or `#quit` will end the currently running program.
|
program.
|
||||||
* You can type `Pry.into(obj)` to nest another Pry session within the
|
* `nesting` shows Pry nesting information.
|
||||||
|
* `jump_to <nest_level>` or `exit_at <nest_level>` unwinds the Pry
|
||||||
|
stack (nesting level) until the appropriate nesting level is reached
|
||||||
|
-- as per the output of `nesting`
|
||||||
|
* `exit_all` breaks out of all Pry nesting levels and returns to the
|
||||||
|
calling process.
|
||||||
|
* You can type `Pry.start(obj)` or `obj.pry` to nest another Pry session within the
|
||||||
current one with `obj` as the receiver of the new session. Very useful
|
current one with `obj` as the receiver of the new session. Very useful
|
||||||
when exploring large or complicated runtime state.
|
when exploring large or complicated runtime state.
|
||||||
|
|
||||||
|
|
125
lib/pry.rb
125
lib/pry.rb
|
@ -3,38 +3,75 @@
|
||||||
|
|
||||||
direc = File.dirname(__FILE__)
|
direc = File.dirname(__FILE__)
|
||||||
|
|
||||||
require 'rubygems'
|
|
||||||
require 'readline'
|
|
||||||
require 'ruby_parser'
|
require 'ruby_parser'
|
||||||
require "#{direc}/pry/version"
|
require "#{direc}/pry/version"
|
||||||
|
require "#{direc}/pry/input"
|
||||||
|
require "#{direc}/pry/output"
|
||||||
|
|
||||||
module Pry
|
module Pry
|
||||||
|
|
||||||
|
# class accessors
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :default_prompt, :wait_prompt,
|
attr_reader :nesting
|
||||||
:session_start_msg, :session_end_msg
|
attr_reader :last_result
|
||||||
|
attr_accessor :default_prompt
|
||||||
|
attr_accessor :wait_prompt
|
||||||
|
attr_accessor :input
|
||||||
|
attr_accessor :output
|
||||||
end
|
end
|
||||||
|
|
||||||
@default_prompt = proc { |v| "pry(#{v})> " }
|
@default_prompt = lambda do |v, nest|
|
||||||
@wait_prompt = proc { |v| "pry(#{v})* " }
|
if nest == 0
|
||||||
@session_start_msg = proc { |v| "Beginning Pry session for #{v}" }
|
"pry(#{v.inspect})> "
|
||||||
@session_end_msg = proc { |v| "Ending Pry session for #{v}" }
|
else
|
||||||
|
"pry(#{v.inspect}):#{nest.inspect}> "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# useful for ending all Pry sessions currently active
|
@wait_prompt = lambda do |v, nest|
|
||||||
@dead = false
|
if nest == 0
|
||||||
|
"pry(#{v.inspect})* "
|
||||||
|
else
|
||||||
|
"pry(#{v.inspect}):#{nest.inspect}* "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@output = Output.new
|
||||||
|
@input = Input.new
|
||||||
|
|
||||||
|
@nesting = []
|
||||||
|
|
||||||
|
def @nesting.level
|
||||||
|
last.is_a?(Array) ? last.first : nil
|
||||||
|
end
|
||||||
|
|
||||||
# loop
|
# loop
|
||||||
def self.repl(target=TOPLEVEL_BINDING)
|
def self.repl(target=TOPLEVEL_BINDING)
|
||||||
target = binding_for(target)
|
target = binding_for(target)
|
||||||
target_self = target.eval('self')
|
target_self = target.eval('self')
|
||||||
puts session_start_msg.call(target_self)
|
output.session_start(target_self)
|
||||||
|
|
||||||
|
# NOTE: this is set PRIOR TO the << to @nesting, so the value here
|
||||||
|
# is equal to the ultimate value in nesting.level
|
||||||
|
nesting_level = @nesting.size
|
||||||
|
|
||||||
|
# Make sure _ exists
|
||||||
|
target.eval("_ = Pry.last_result")
|
||||||
|
|
||||||
|
nesting_level_breakout = catch(:breakout) do
|
||||||
|
@nesting << [@nesting.size, target_self]
|
||||||
loop do
|
loop do
|
||||||
if catch(:pop) { rep(target) } == :return || @dead
|
rep(target)
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
puts session_end_msg.call(target_self)
|
@nesting.pop
|
||||||
|
output.session_end(target_self)
|
||||||
|
|
||||||
|
# we only enter here if :breakout has been thrown
|
||||||
|
if nesting_level_breakout
|
||||||
|
throw :breakout, nesting_level_breakout if nesting_level != nesting_level_breakout
|
||||||
|
end
|
||||||
|
|
||||||
target_self
|
target_self
|
||||||
end
|
end
|
||||||
|
@ -47,19 +84,14 @@ module Pry
|
||||||
# print
|
# print
|
||||||
def self.rep(target=TOPLEVEL_BINDING)
|
def self.rep(target=TOPLEVEL_BINDING)
|
||||||
target = binding_for(target)
|
target = binding_for(target)
|
||||||
value = re(target)
|
output.print re(target)
|
||||||
case value
|
|
||||||
when Exception
|
|
||||||
puts "#{value.class}: #{value.message}"
|
|
||||||
else
|
|
||||||
puts "=> #{value.inspect}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# eval
|
# eval
|
||||||
def self.re(target=TOPLEVEL_BINDING)
|
def self.re(target=TOPLEVEL_BINDING)
|
||||||
target = binding_for(target)
|
target = binding_for(target)
|
||||||
target.eval r(target)
|
@last_result = target.eval r(target)
|
||||||
|
target.eval("_ = Pry.last_result")
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
e
|
e
|
||||||
end
|
end
|
||||||
|
@ -69,7 +101,7 @@ module Pry
|
||||||
target = binding_for(target)
|
target = binding_for(target)
|
||||||
eval_string = ""
|
eval_string = ""
|
||||||
loop do
|
loop do
|
||||||
val = Readline.readline(prompt(eval_string, target), true)
|
val = input.read(prompt(eval_string, target, nesting.level))
|
||||||
eval_string += "#{val}\n"
|
eval_string += "#{val}\n"
|
||||||
process_commands(val, eval_string, target)
|
process_commands(val, eval_string, target)
|
||||||
|
|
||||||
|
@ -78,24 +110,43 @@ module Pry
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.process_commands(val, eval_string, target)
|
def self.process_commands(val, eval_string, target)
|
||||||
|
def eval_string.clear() replace("") end
|
||||||
|
|
||||||
case val
|
case val
|
||||||
when "#exit", "#quit"
|
when "exit_program", "quit_program"
|
||||||
|
output.exit_program
|
||||||
exit
|
exit
|
||||||
when "!"
|
when "!"
|
||||||
eval_string.replace("")
|
output.refresh
|
||||||
puts "Refreshed REPL."
|
eval_string.clear
|
||||||
when "exit", "quit"
|
when "nesting"
|
||||||
throw(:pop, :return)
|
output.show_nesting(nesting)
|
||||||
|
eval_string.clear
|
||||||
|
when "exit_all"
|
||||||
|
throw(:breakout, 0)
|
||||||
|
when "exit", "quit", "back"
|
||||||
|
output.exit
|
||||||
|
throw(:breakout, nesting.level)
|
||||||
|
when /exit_at\s*(\d*)/, /jump_to\s*(\d*)/
|
||||||
|
nesting_level_breakout = ($~.captures).first.to_i
|
||||||
|
output.exit_at(nesting_level_breakout)
|
||||||
|
|
||||||
|
if nesting_level_breakout < 0 || nesting_level_breakout >= nesting.level
|
||||||
|
output.error_invalid_nest_level(nesting_level_breakout, nesting.level - 1)
|
||||||
|
eval_string.clear
|
||||||
|
else
|
||||||
|
throw(:breakout, nesting_level_breakout + 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.prompt(eval_string, target)
|
def self.prompt(eval_string, target, nest)
|
||||||
target_self = target.eval('self')
|
target_self = target.eval('self')
|
||||||
|
|
||||||
if eval_string.empty?
|
if eval_string.empty?
|
||||||
default_prompt.call(target_self)
|
default_prompt.call(target_self, nest)
|
||||||
else
|
else
|
||||||
wait_prompt.call(target_self)
|
wait_prompt.call(target_self, nest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -115,11 +166,13 @@ module Pry
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.kill
|
module ObjectExtensions
|
||||||
@dead = true
|
def pry
|
||||||
|
Pry.start(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.revive
|
class Object
|
||||||
@dead = false
|
include Pry::ObjectExtensions
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
9
lib/pry/input.rb
Normal file
9
lib/pry/input.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require 'readline'
|
||||||
|
|
||||||
|
module Pry
|
||||||
|
class Input
|
||||||
|
def read(prompt)
|
||||||
|
Readline.readline(prompt, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
44
lib/pry/output.rb
Normal file
44
lib/pry/output.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
module Pry
|
||||||
|
class Output
|
||||||
|
def refresh
|
||||||
|
puts "Refreshed REPL"
|
||||||
|
end
|
||||||
|
|
||||||
|
def session_start(obj)
|
||||||
|
puts "Beginning Pry session for #{obj.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def session_end(obj)
|
||||||
|
puts "Ending Pry session for #{obj.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# the print component of READ-EVAL-PRINT-LOOP
|
||||||
|
def print(value)
|
||||||
|
case value
|
||||||
|
when Exception
|
||||||
|
puts "#{value.class}: #{value.message}"
|
||||||
|
else
|
||||||
|
puts "=> #{value.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_nesting(nesting)
|
||||||
|
puts "Nesting status:"
|
||||||
|
nesting.each do |level, obj|
|
||||||
|
if level == 0
|
||||||
|
puts "#{level}. #{obj.inspect} (Pry top level)"
|
||||||
|
else
|
||||||
|
puts "#{level}. #{obj.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def error_invalid_nest_level(nest_level, max_nest_level)
|
||||||
|
puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{nest_level}."
|
||||||
|
end
|
||||||
|
|
||||||
|
def exit() end
|
||||||
|
def exit_at(nesting_level_breakout) end
|
||||||
|
def exit_program() end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,3 @@
|
||||||
module Pry
|
module Pry
|
||||||
VERSION = "0.1.2"
|
VERSION = "0.2.0"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue