1
0
Fork 0
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:
John Mair 2010-12-09 13:59:30 +13:00
parent baeaebfa5b
commit a25c40d2db
6 changed files with 223 additions and 62 deletions

View file

@ -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

View file

@ -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
@ -46,36 +46,77 @@ If we now inspect the `Test` object we can see our changes have had
effect: 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:
---------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------
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.

View file

@ -3,42 +3,79 @@
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
@wait_prompt = lambda do |v, nest|
if nest == 0
"pry(#{v.inspect})* "
else
"pry(#{v.inspect}):#{nest.inspect}* "
end
end
@output = Output.new
@input = Input.new
@nesting = []
# useful for ending all Pry sessions currently active def @nesting.level
@dead = false 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)
loop do # NOTE: this is set PRIOR TO the << to @nesting, so the value here
if catch(:pop) { rep(target) } == :return || @dead # is equal to the ultimate value in nesting.level
break 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
rep(target)
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
class << self class << self
alias_method :into, :repl alias_method :into, :repl
alias_method :start, :repl alias_method :start, :repl
@ -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
end Pry.start(self)
end
def self.revive
@dead = false
end end
end end
class Object
include Pry::ObjectExtensions
end

9
lib/pry/input.rb Normal file
View 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
View 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

View file

@ -1,3 +1,3 @@
module Pry module Pry
VERSION = "0.1.2" VERSION = "0.2.0"
end end