pry 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 9/12/2010 version 0.1.3
2
+ * Got rid of rubygems dependency, refactored some code.
3
+ 8/12/2010 version 0.1.2
4
+ * now rescuing SyntaxError as well as Racc::Parser error in valid_expression?
1
5
  8/12/2010 version 0.1.0
2
6
  * release!
3
7
 
data/README.markdown CHANGED
@@ -3,21 +3,23 @@ Pry
3
3
 
4
4
  (C) John Mair (banisterfiend) 2010
5
5
 
6
- _attach an irb-like session to any object_
6
+ _attach an irb-like session to any object at runtime_
7
7
 
8
- Pry is a simple Ruby REPL that specializes in the interactive
8
+ Pry is a simple Ruby REPL (Read-Eval-Print-Loop) that specializes in the interactive
9
9
  manipulation of objects during the running of a program.
10
10
 
11
+ It is not based on the IRB codebase and is small, at around 260 LOC.
12
+
11
13
  * Install the [gem](https://rubygems.org/gems/pry): `gem install pry`
12
14
  * Read the [documentation](http://rdoc.info/github/banister/pry/master/file/README.markdown)
13
15
  * See the [source code](http://github.com/banister/pry)
14
16
 
15
- example: prying on an object at runtime
17
+ example: Interacting with an object at runtime
16
18
  ---------------------------------------
17
19
 
18
- With the `Pry.into()` method we can pry (open an irb-like session) on
20
+ With the `Pry.start()` method we can pry (open an irb-like session) on
19
21
  an object. In the example below we open a Pry session for the `Test` class and execute a method and add
20
- an instance variable. The program is halted for the duration of the session.
22
+ an instance variable. The current thread is halted for the duration of the session.
21
23
 
22
24
  require 'pry'
23
25
 
@@ -25,7 +27,7 @@ an instance variable. The program is halted for the duration of the session.
25
27
  def self.hello() "hello world" end
26
28
  end
27
29
 
28
- Pry.into(Test)
30
+ Pry.start(Test)
29
31
 
30
32
  # Pry session begins on stdin
31
33
  Beginning Pry session for Test
@@ -44,49 +46,85 @@ If we now inspect the `Test` object we can see our changes have had
44
46
  effect:
45
47
 
46
48
  Test.instance_variable_get(:@y) #=> 20
47
-
48
49
 
50
+ #### Alternative Syntax
51
+
52
+ You can also use the `obj.pry` or `pry(obj)` syntax to start a pry session on
53
+ `obj`. e.g
54
+
55
+ 5.pry
56
+ Beginning Pry session for 5
57
+ pry(5)>
58
+
59
+ OR
60
+
61
+ pry 6
62
+ beginning Pry session for 6
63
+ pry(6)>
64
+
49
65
  example: Pry sessions can nest arbitrarily deep so we can pry on objects inside objects:
50
66
  ----------------------------------------------------------------------------------------
51
67
 
52
68
  Here we will begin Pry at top-level, then pry on a class and then on
53
69
  an instance variable inside that class:
54
70
 
55
- # Pry.into() without parameters begins a Pry session on top-level (main)
56
- Pry.into
71
+ # Pry.start() without parameters begins a Pry session on top-level (main)
72
+ Pry.start
57
73
  Beginning Pry session for main
58
74
  pry(main)> class Hello
59
75
  pry(main)* @x = 20
60
76
  pry(main)* end
61
77
  => 20
62
- pry(main)> Pry.into Hello
78
+ pry(main)> Pry.start Hello
63
79
  Beginning Pry session for Hello
64
- pry(Hello)> instance_variables
80
+ pry(Hello):1> instance_variables
65
81
  => [:@x]
66
- pry(Hello)> Pry.into @x
82
+ pry(Hello):1> Pry.start @x
67
83
  Beginning Pry session for 20
68
- pry(20)> self + 10
84
+ pry(20:2)> self + 10
69
85
  => 30
70
- pry(20)> exit
86
+ pry(20:2)> exit
71
87
  Ending Pry session for 20
72
- pry(Hello)> exit
88
+ pry(Hello):1> exit
73
89
  Ending Pry session for Hello
74
90
  pry(main)> exit
75
91
  Ending Pry session for main
76
92
 
77
- # program resumes here
93
+ The number after the `:` in the pry prompt indicates the nesting
94
+ level. To display more information about nesting, use the `nesting`
95
+ command. E.g
96
+
97
+ pry("friend":3)> nesting
98
+ Nesting status:
99
+ 0. main (Pry top level)
100
+ 1. Hello
101
+ 2. 100
102
+ 3. "friend"
103
+ => nil
104
+
105
+ We can then jump back to any of the previous nesting levels by using
106
+ the `jump_to` command:
78
107
 
108
+ pry("friend":3)> jump_to 1
109
+ Ending Pry session for "friend"
110
+ Ending Pry session for 100
111
+ => 100
112
+ pry(Hello):1>
79
113
 
80
- example: Spawn a separate thread so you can use `Pry` to manipulate an
81
- object without halting the program.
82
- ---------------------------------------------------------------------------------------------------
114
+ If we just want to go back one level of nesting we can of course
115
+ use the `quit` or `exit` or `back` commands.
83
116
 
84
- If we embed our `Pry.into` method inside its own thread we can examine
85
- and manipulate objects without halting the program.
117
+ To break out of all levels of Pry nesting and return immediately to the
118
+ calling process use `exit_all`:
86
119
 
87
- # Pry.into() without parameters opens up the top-level (main)
88
- Thread.new { Pry.into }
120
+ pry("friend":3)> exit_all
121
+ Ending Pry session for "friend"
122
+ Ending Pry session for 100
123
+ Ending Pry session for Hello
124
+ Ending Pry session for main
125
+ => main
89
126
 
127
+ # program resumes here
90
128
 
91
129
  Features and limitations
92
130
  ------------------------
@@ -102,11 +140,15 @@ end.
102
140
  Features:
103
141
 
104
142
  * Pry can be invoked at any time and on any object in the running program.
105
- * Pry sessions can nest arbitrarily deeply -- to go back one level of nesting type 'exit' or 'quit'
143
+ * Pry sessions can nest arbitrarily deeply -- to go back one level of nesting type 'exit' or 'quit' or 'back'
144
+ * Use `_` to recover last result.
106
145
  * Pry has multi-line support built in.
146
+ * Pry gives good control over nested sessions (important when exploring complicated runtime state)
147
+ * Pry is not based on the IRB codebase.
148
+ * Pry is small; around 260 LOC.
107
149
  * Pry implements all the methods in the REPL chain separately: `Pry.r`
108
150
  for reading; `Pry.re` for eval; `Pry.rep` for printing; and `Pry.repl`
109
- for the loop (`Pry.into` is simply an alias for `Pry.repl`). You can
151
+ for the loop (`Pry.start` is simply an alias for `Pry.repl`). You can
110
152
  invoke any of these methods directly depending on exactly what aspect of the functionality you need.
111
153
 
112
154
  Limitations:
@@ -123,6 +165,53 @@ Limitations:
123
165
  hash literal syntax (this: syntax) or the 'stabby lambda' syntax
124
166
  (->).
125
167
 
168
+ Commands
169
+ -----------
170
+
171
+ ### The Pry API:
172
+
173
+ * `Pry.start()` and `Pry.into()` and `Pry.repl()` are all aliases of
174
+ oneanother. They all start a Read-Eval-Print-Loop on the object they
175
+ receive as a parameter. In the case of no parameter they operate on
176
+ top-level (main). They can receive any object or a `Binding`
177
+ object as parameter.
178
+ * `obj.pry` and `pry(obj)` may also be used as alternative syntax to `Pry.start(obj)`
179
+ * If, for some reason you do not want to 'loop' then use `Pry.rep()`; it
180
+ only performs the Read-Eval-Print section of the REPL - it ends the
181
+ session after just one line of input. It takes the same parameters as
182
+ `Pry.repl()`
183
+ * Likewise `Pry.re()` only performs the Read-Eval section of the REPL,
184
+ it returns the result of the evaluation or an Exception object in
185
+ case of error. It also takes the same parameters as `Pry.repl()`
186
+ * Similarly `Pry.r()` only performs the Read section of the REPL, only
187
+ returning the Ruby expression (as a string). It takes the same parameters as all the others.
188
+
189
+ ### Session commands
190
+
191
+ Pry supports a few commands inside the session itself. These commands are
192
+ not methods and must start at the beginning of a line, with no
193
+ whitespace in between.
194
+
195
+ If you want to access a method of the same name, prefix the invocation by whitespace.
196
+
197
+ * Typing `!` on a line by itself will refresh the REPL - useful for
198
+ getting you out of a situation if the parsing process
199
+ goes wrong.
200
+ * `status` shows status information about the current session.
201
+ * `help` shows the list of session commands with brief explanations.
202
+ * `exit` or `quit` or `back` will end the current Pry session and go
203
+ back to the calling process or back one level of nesting (if there
204
+ are nested sessions).
205
+ * `exit_program` or `quit_program` will end the currently running
206
+ program.
207
+ * `nesting` shows Pry nesting information.
208
+ * `jump_to <nest_level>` unwinds the Pry stack (nesting level) until the appropriate nesting level is reached
209
+ -- as per the output of `nesting`
210
+ * `exit_all` breaks out of all Pry nesting levels and returns to the
211
+ calling process.
212
+ * You can type `Pry.start(obj)` or `obj.pry` to nest another Pry session within the
213
+ current one with `obj` as the receiver of the new session. Very useful
214
+ when exploring large or complicated runtime state.
126
215
 
127
216
  Contact
128
217
  -------
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ CLEAN.include("ext/**/*.#{dlext}", "ext/**/*.log", "ext/**/*.o",
12
12
 
13
13
  def apply_spec_defaults(s)
14
14
  s.name = "pry"
15
- s.summary = "attach an irb-like session to any object"
15
+ s.summary = "attach an irb-like session to any object at runtime"
16
16
  s.version = Pry::VERSION
17
17
  s.date = Time.now.strftime '%Y-%m-%d'
18
18
  s.author = "John Mair (banisterfiend)"
data/lib/pry/input.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'readline'
2
+
3
+ module Pry
4
+ class Input
5
+ def read(prompt)
6
+ Readline.readline(prompt, true)
7
+ end
8
+ end
9
+ end
data/lib/pry/output.rb ADDED
@@ -0,0 +1,67 @@
1
+ module Pry
2
+ class Output
3
+ def refresh
4
+ puts "Refreshed REPL"
5
+ end
6
+
7
+ def session_start(obj)
8
+ puts "Beginning Pry session for #{obj.inspect}"
9
+ end
10
+
11
+ def session_end(obj)
12
+ puts "Ending Pry session for #{obj.inspect}"
13
+ end
14
+
15
+ # the print component of READ-EVAL-PRINT-LOOP
16
+ def print(value)
17
+ case value
18
+ when Exception
19
+ puts "#{value.class}: #{value.message}"
20
+ else
21
+ puts "=> #{value.inspect}"
22
+ end
23
+ end
24
+
25
+ def show_help
26
+ puts "Command list:"
27
+ puts "--"
28
+ puts "help This menu"
29
+ puts "status Show status information"
30
+ puts "! Refresh the REPL"
31
+ puts "nesting Show nesting information"
32
+ puts "exit/quit/back End the current Pry session"
33
+ puts "exit_all End all nested Pry sessions"
34
+ puts "exit_program/quit_program End the current program"
35
+ puts "jump_to <level> Jump to a Pry session further up the stack, exiting all sessions below"
36
+ end
37
+
38
+ def show_nesting(nesting)
39
+ puts "Nesting status:"
40
+ puts "--"
41
+ nesting.each do |level, obj|
42
+ if level == 0
43
+ puts "#{level}. #{obj.inspect} (Pry top level)"
44
+ else
45
+ puts "#{level}. #{obj.inspect}"
46
+ end
47
+ end
48
+ end
49
+
50
+ def show_status(nesting, target)
51
+ puts "Status:"
52
+ puts "--"
53
+ puts "Receiver: #{target.eval('self').inspect}"
54
+ puts "Nesting level: #{nesting.level}"
55
+ puts "Local variables: #{target.eval("local_variables").inspect}"
56
+ puts "Last result: #{Pry.last_result.inspect}"
57
+ end
58
+
59
+ def error_invalid_nest_level(nest_level, max_nest_level)
60
+ puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{nest_level}."
61
+ end
62
+
63
+ def exit() end
64
+ def jump_to(nesting_level_breakout) end
65
+ def exit_program() end
66
+ end
67
+ end
data/lib/pry/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pry
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/pry.rb CHANGED
@@ -1,35 +1,79 @@
1
- require 'rubygems'
2
- require 'readline'
1
+ # (C) John Mair (banisterfiend) 2010
2
+ # MIT License
3
+
4
+ direc = File.dirname(__FILE__)
5
+
3
6
  require 'ruby_parser'
7
+ require "#{direc}/pry/version"
8
+ require "#{direc}/pry/input"
9
+ require "#{direc}/pry/output"
4
10
 
5
11
  module Pry
12
+
13
+ # class accessors
6
14
  class << self
7
- attr_accessor :default_prompt, :wait_prompt,
8
- :session_start_msg, :session_end_msg
15
+ attr_reader :nesting
16
+ attr_reader :last_result
17
+ attr_accessor :default_prompt
18
+ attr_accessor :wait_prompt
19
+ attr_accessor :input
20
+ attr_accessor :output
21
+ end
22
+
23
+ @default_prompt = lambda do |v, nest|
24
+ if nest == 0
25
+ "pry(#{v.inspect})> "
26
+ else
27
+ "pry(#{v.inspect}):#{nest.inspect}> "
28
+ end
29
+ end
30
+
31
+ @wait_prompt = lambda do |v, nest|
32
+ if nest == 0
33
+ "pry(#{v.inspect})* "
34
+ else
35
+ "pry(#{v.inspect}):#{nest.inspect}* "
36
+ end
9
37
  end
10
38
 
11
- @default_prompt = proc { |v| "pry(#{v})> " }
12
- @wait_prompt = proc { |v| "pry(#{v})* " }
13
- @session_start_msg = proc { |v| "Beginning Pry session for #{v}" }
14
- @session_end_msg = proc { |v| "Ending Pry session for #{v}" }
39
+ @output = Output.new
40
+ @input = Input.new
41
+
42
+ @nesting = []
43
+
44
+ def @nesting.level
45
+ last.is_a?(Array) ? last.first : nil
46
+ end
15
47
 
16
48
  # loop
17
49
  def self.repl(target=TOPLEVEL_BINDING)
18
- if !target.is_a?(Binding)
19
- target = target.instance_eval { binding }
20
- end
21
-
22
- puts session_start_msg.call(target.eval('self'))
50
+ target = binding_for(target)
51
+ target_self = target.eval('self')
52
+ output.session_start(target_self)
23
53
 
24
- loop do
25
- if catch(:pop) { rep(target) } == :return
26
- break target.eval('self')
54
+ nesting_level = @nesting.size
55
+
56
+ # Make sure _ exists
57
+ target.eval("_ = Pry.last_result")
58
+
59
+ nesting_level_breakout = catch(:breakout) do
60
+ @nesting << [@nesting.size, target_self]
61
+ loop do
62
+ rep(target)
27
63
  end
28
64
  end
29
65
 
30
- puts session_end_msg.call(target.eval('self'))
31
- end
66
+ @nesting.pop
67
+ output.session_end(target_self)
32
68
 
69
+ # we only enter here if :breakout has been thrown
70
+ if nesting_level_breakout
71
+ throw :breakout, nesting_level_breakout if nesting_level != nesting_level_breakout
72
+ end
73
+
74
+ target_self
75
+ end
76
+
33
77
  class << self
34
78
  alias_method :into, :repl
35
79
  alias_method :start, :repl
@@ -37,31 +81,25 @@ module Pry
37
81
 
38
82
  # print
39
83
  def self.rep(target=TOPLEVEL_BINDING)
40
- if !target.is_a?(Binding)
41
- target = target.instance_eval { binding }
42
- end
43
-
44
- value = re(target)
45
- case value
46
- when Exception
47
- puts "#{value.class}: #{value.message}"
48
- else
49
- puts "=> #{value.inspect}"
50
- end
84
+ target = binding_for(target)
85
+ output.print re(target)
51
86
  end
52
87
 
53
88
  # eval
54
89
  def self.re(target=TOPLEVEL_BINDING)
55
- target.eval r(target)
90
+ target = binding_for(target)
91
+ @last_result = target.eval r(target)
92
+ target.eval("_ = Pry.last_result")
56
93
  rescue StandardError => e
57
94
  e
58
95
  end
59
96
 
60
97
  # read
61
98
  def self.r(target=TOPLEVEL_BINDING)
99
+ target = binding_for(target)
62
100
  eval_string = ""
63
101
  loop do
64
- val = Readline.readline(prompt(eval_string, target), true)
102
+ val = input.read(prompt(eval_string, target, nesting.level))
65
103
  eval_string += "#{val}\n"
66
104
  process_commands(val, eval_string, target)
67
105
 
@@ -70,32 +108,79 @@ module Pry
70
108
  end
71
109
 
72
110
  def self.process_commands(val, eval_string, target)
111
+ def eval_string.clear() replace("") end
112
+
73
113
  case val
74
- when "#exit", "#quit"
114
+ when "exit_program", "quit_program"
115
+ output.exit_program
75
116
  exit
76
117
  when "!"
77
- eval_string.replace("")
78
- puts "Refreshed REPL."
79
- when "exit", "quit"
80
- throw(:pop, :return)
118
+ output.refresh
119
+ eval_string.clear
120
+ when "help"
121
+ output.show_help
122
+ eval_string.clear
123
+ when "nesting"
124
+ output.show_nesting(nesting)
125
+ eval_string.clear
126
+ when "status"
127
+ output.show_status(nesting, target)
128
+ eval_string.clear
129
+ when "exit_all"
130
+ throw(:breakout, 0)
131
+ when "exit", "quit", "back"
132
+ output.exit
133
+ throw(:breakout, nesting.level)
134
+ when /jump_to\s*(\d*)/
135
+ nesting_level_breakout = ($~.captures).first.to_i
136
+ output.jump_to(nesting_level_breakout)
137
+
138
+ if nesting_level_breakout < 0 || nesting_level_breakout >= nesting.level
139
+ output.error_invalid_nest_level(nesting_level_breakout, nesting.level - 1)
140
+ eval_string.clear
141
+ else
142
+ throw(:breakout, nesting_level_breakout + 1)
143
+ end
81
144
  end
82
145
  end
83
146
 
84
- def self.prompt(eval_string, target)
85
- context = target.eval('self')
147
+ def self.prompt(eval_string, target, nest)
148
+ target_self = target.eval('self')
86
149
 
87
150
  if eval_string.empty?
88
- default_prompt.call(context)
151
+ default_prompt.call(target_self, nest)
89
152
  else
90
- wait_prompt.call(context)
153
+ wait_prompt.call(target_self, nest)
91
154
  end
92
155
  end
93
156
 
94
157
  def self.valid_expression?(code)
95
158
  RubyParser.new.parse(code)
96
- rescue Racc::ParseError
159
+ rescue Racc::ParseError, SyntaxError
97
160
  false
98
161
  else
99
162
  true
100
163
  end
164
+
165
+ def self.binding_for(target)
166
+ if target.is_a?(Binding)
167
+ target
168
+ else
169
+ if target == TOPLEVEL_BINDING.eval('self')
170
+ TOPLEVEL_BINDING
171
+ else
172
+ target.instance_eval { binding }
173
+ end
174
+ end
175
+ end
176
+
177
+ module ObjectExtensions
178
+ def pry(target=self)
179
+ Pry.start(Pry.binding_for(target))
180
+ end
181
+ end
182
+ end
183
+
184
+ class Object
185
+ include Pry::ObjectExtensions
101
186
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pry
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 23
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
- - 1
8
+ - 2
8
9
  - 0
9
- version: 0.1.0
10
+ version: 0.2.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - John Mair (banisterfiend)
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-12-09 00:00:00 +13:00
18
+ date: 2010-12-11 00:00:00 +13:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
@@ -25,6 +26,7 @@ dependencies:
25
26
  requirements:
26
27
  - - ">="
27
28
  - !ruby/object:Gem::Version
29
+ hash: 5
28
30
  segments:
29
31
  - 2
30
32
  - 0
@@ -32,7 +34,7 @@ dependencies:
32
34
  version: 2.0.5
33
35
  type: :runtime
34
36
  version_requirements: *id001
35
- description: attach an irb-like session to any object
37
+ description: attach an irb-like session to any object at runtime
36
38
  email: jrmair@gmail.com
37
39
  executables: []
38
40
 
@@ -41,6 +43,8 @@ extensions: []
41
43
  extra_rdoc_files: []
42
44
 
43
45
  files:
46
+ - lib/pry/input.rb
47
+ - lib/pry/output.rb
44
48
  - lib/pry/version.rb
45
49
  - lib/pry.rb
46
50
  - CHANGELOG
@@ -60,6 +64,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
64
  requirements:
61
65
  - - ">="
62
66
  - !ruby/object:Gem::Version
67
+ hash: 3
63
68
  segments:
64
69
  - 0
65
70
  version: "0"
@@ -68,6 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
73
  requirements:
69
74
  - - ">="
70
75
  - !ruby/object:Gem::Version
76
+ hash: 3
71
77
  segments:
72
78
  - 0
73
79
  version: "0"
@@ -77,6 +83,6 @@ rubyforge_project:
77
83
  rubygems_version: 1.3.7
78
84
  signing_key:
79
85
  specification_version: 3
80
- summary: attach an irb-like session to any object
86
+ summary: attach an irb-like session to any object at runtime
81
87
  test_files: []
82
88