diff options
author | John Mair <jrmair@gmail.com> | 2010-12-09 13:59:30 +1300 |
---|---|---|
committer | John Mair <jrmair@gmail.com> | 2010-12-11 18:19:55 +1300 |
commit | a25c40d2dbbb13a6606246208aec5170abe598a8 (patch) | |
tree | 41d2926138401fc04d1635c44b5043cf237a05c4 | |
parent | baeaebfa5ba8b9d0962847eb76c4dd452bbf6e73 (diff) | |
download | pry-a25c40d2dbbb13a6606246208aec5170abe598a8.tar.gz |
version bump to 0.1.3 and slight aesthetic changes to codev0.1.3
-rw-r--r-- | CHANGELOG | 2 | ||||
-rw-r--r-- | README.markdown | 97 | ||||
-rw-r--r-- | lib/pry.rb | 129 | ||||
-rw-r--r-- | lib/pry/input.rb | 9 | ||||
-rw-r--r-- | lib/pry/output.rb | 44 | ||||
-rw-r--r-- | lib/pry/version.rb | 2 |
6 files changed, 222 insertions, 61 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 * 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. @@ -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 |