summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mair <jrmair@gmail.com>2010-12-09 13:59:30 +1300
committerJohn Mair <jrmair@gmail.com>2010-12-11 18:19:55 +1300
commita25c40d2dbbb13a6606246208aec5170abe598a8 (patch)
tree41d2926138401fc04d1635c44b5043cf237a05c4
parentbaeaebfa5ba8b9d0962847eb76c4dd452bbf6e73 (diff)
downloadpry-a25c40d2dbbb13a6606246208aec5170abe598a8.tar.gz
version bump to 0.1.3 and slight aesthetic changes to codev0.1.3
-rw-r--r--CHANGELOG2
-rw-r--r--README.markdown97
-rw-r--r--lib/pry.rb129
-rw-r--r--lib/pry/input.rb9
-rw-r--r--lib/pry/output.rb44
-rw-r--r--lib/pry/version.rb2
6 files changed, 222 insertions, 61 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 6cfc9bc6..30fe42af 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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
* now rescuing SyntaxError as well as Racc::Parser error in valid_expression?
8/12/2010 version 0.1.0
diff --git a/README.markdown b/README.markdown
index b5d9ee66..ccfddd00 100644
--- a/README.markdown
+++ b/README.markdown
@@ -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
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`
* 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
---------------------------------------
-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 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
end
- Pry.into(Test)
+ Pry.start(Test)
# Pry session begins on stdin
Beginning Pry session for Test
@@ -46,36 +46,77 @@ If we now inspect the `Test` object we can see our changes have had
effect:
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:
----------------------------------------------------------------------------------------
Here we will begin Pry at top-level, then pry on a class and then on
an instance variable inside that class:
- # Pry.into() without parameters begins a Pry session on top-level (main)
- Pry.into
+ # Pry.start() without parameters begins a Pry session on top-level (main)
+ Pry.start
Beginning Pry session for main
pry(main)> class Hello
pry(main)* @x = 20
pry(main)* end
=> 20
- pry(main)> Pry.into Hello
+ pry(main)> Pry.start Hello
Beginning Pry session for Hello
- pry(Hello)> instance_variables
+ pry(Hello):1> instance_variables
=> [:@x]
- pry(Hello)> Pry.into @x
+ pry(Hello):1> Pry.start @x
Beginning Pry session for 20
- pry(20)> self + 10
+ pry(20:2)> self + 10
=> 30
- pry(20)> exit
+ pry(20:2)> exit
Ending Pry session for 20
- pry(Hello)> exit
+ pry(Hello):1> exit
Ending Pry session for Hello
pry(main)> exit
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
------------------------
@@ -91,13 +132,14 @@ end.
Features:
* 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 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`
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.
Limitations:
@@ -119,11 +161,12 @@ Commands
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
receive as a parameter. In the case of no parameter they operate on
top-level (main). They can receive any object or a `Binding`
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
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
@@ -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
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
getting you out of a situation if the parsing process
goes wrong.
-* `exit` or `quit` will end the current Pry session. Note that it will
- not end any containing Pry sessions if the current session happens
- to be nested.
-* `#exit` or `#quit` will end the currently running program.
-* You can type `Pry.into(obj)` to nest another Pry session within the
+* `exit` or `quit` or `back` will end the current Pry session and go
+ back to the calling process or back one level of nesting.
+* `exit_program` or `quit_program` will end the currently running
+ program.
+* `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
when exploring large or complicated runtime state.
diff --git a/lib/pry.rb b/lib/pry.rb
index 61ebf5a6..6be0242c 100644
--- a/lib/pry.rb
+++ b/lib/pry.rb
@@ -3,42 +3,79 @@
direc = File.dirname(__FILE__)
-require 'rubygems'
-require 'readline'
require 'ruby_parser'
require "#{direc}/pry/version"
+require "#{direc}/pry/input"
+require "#{direc}/pry/output"
module Pry
+
+ # class accessors
class << self
- attr_accessor :default_prompt, :wait_prompt,
- :session_start_msg, :session_end_msg
+ attr_reader :nesting
+ attr_reader :last_result
+ attr_accessor :default_prompt
+ attr_accessor :wait_prompt
+ attr_accessor :input
+ attr_accessor :output
end
- @default_prompt = proc { |v| "pry(#{v})> " }
- @wait_prompt = proc { |v| "pry(#{v})* " }
- @session_start_msg = proc { |v| "Beginning Pry session for #{v}" }
- @session_end_msg = proc { |v| "Ending Pry session for #{v}" }
+ @default_prompt = lambda do |v, nest|
+ if nest == 0
+ "pry(#{v.inspect})> "
+ 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
- @dead = false
+ def @nesting.level
+ last.is_a?(Array) ? last.first : nil
+ end
# loop
def self.repl(target=TOPLEVEL_BINDING)
target = binding_for(target)
target_self = target.eval('self')
- puts session_start_msg.call(target_self)
+ output.session_start(target_self)
- loop do
- if catch(:pop) { rep(target) } == :return || @dead
- break
+ # 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
+ rep(target)
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
end
-
+
class << self
alias_method :into, :repl
alias_method :start, :repl
@@ -47,19 +84,14 @@ module Pry
# print
def self.rep(target=TOPLEVEL_BINDING)
target = binding_for(target)
- value = re(target)
- case value
- when Exception
- puts "#{value.class}: #{value.message}"
- else
- puts "=> #{value.inspect}"
- end
+ output.print re(target)
end
# eval
def self.re(target=TOPLEVEL_BINDING)
target = binding_for(target)
- target.eval r(target)
+ @last_result = target.eval r(target)
+ target.eval("_ = Pry.last_result")
rescue StandardError => e
e
end
@@ -69,7 +101,7 @@ module Pry
target = binding_for(target)
eval_string = ""
loop do
- val = Readline.readline(prompt(eval_string, target), true)
+ val = input.read(prompt(eval_string, target, nesting.level))
eval_string += "#{val}\n"
process_commands(val, eval_string, target)
@@ -78,24 +110,43 @@ module Pry
end
def self.process_commands(val, eval_string, target)
+ def eval_string.clear() replace("") end
+
case val
- when "#exit", "#quit"
+ when "exit_program", "quit_program"
+ output.exit_program
exit
when "!"
- eval_string.replace("")
- puts "Refreshed REPL."
- when "exit", "quit"
- throw(:pop, :return)
+ output.refresh
+ eval_string.clear
+ when "nesting"
+ 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
- def self.prompt(eval_string, target)
+ def self.prompt(eval_string, target, nest)
target_self = target.eval('self')
if eval_string.empty?
- default_prompt.call(target_self)
+ default_prompt.call(target_self, nest)
else
- wait_prompt.call(target_self)
+ wait_prompt.call(target_self, nest)
end
end
@@ -115,11 +166,13 @@ module Pry
end
end
- def self.kill
- @dead = true
+ module ObjectExtensions
+ def pry
+ Pry.start(self)
+ end
end
+end
- def self.revive
- @dead = false
- end
+class Object
+ include Pry::ObjectExtensions
end
diff --git a/lib/pry/input.rb b/lib/pry/input.rb
new file mode 100644
index 00000000..c877d040
--- /dev/null
+++ b/lib/pry/input.rb
@@ -0,0 +1,9 @@
+require 'readline'
+
+module Pry
+ class Input
+ def read(prompt)
+ Readline.readline(prompt, true)
+ end
+ end
+end
diff --git a/lib/pry/output.rb b/lib/pry/output.rb
new file mode 100644
index 00000000..088d20c0
--- /dev/null
+++ b/lib/pry/output.rb
@@ -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
diff --git a/lib/pry/version.rb b/lib/pry/version.rb
index 88527da8..7148d961 100644
--- a/lib/pry/version.rb
+++ b/lib/pry/version.rb
@@ -1,3 +1,3 @@
module Pry
- VERSION = "0.1.2"
+ VERSION = "0.2.0"
end