pry 0.9.7.4 → 0.9.8pre2

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.
Files changed (49) hide show
  1. data/.gitignore +1 -3
  2. data/README.markdown +3 -1
  3. data/Rakefile +48 -31
  4. data/bin/pry +2 -80
  5. data/lib/pry.rb +17 -20
  6. data/lib/pry/cli.rb +152 -0
  7. data/lib/pry/command_processor.rb +13 -0
  8. data/lib/pry/command_set.rb +102 -9
  9. data/lib/pry/config.rb +28 -6
  10. data/lib/pry/default_commands/context.rb +9 -8
  11. data/lib/pry/default_commands/documentation.rb +55 -13
  12. data/lib/pry/default_commands/easter_eggs.rb +1 -1
  13. data/lib/pry/default_commands/input.rb +25 -25
  14. data/lib/pry/default_commands/introspection.rb +19 -18
  15. data/lib/pry/default_commands/ls.rb +23 -38
  16. data/lib/pry/default_commands/shell.rb +47 -15
  17. data/lib/pry/helpers/command_helpers.rb +28 -6
  18. data/lib/pry/helpers/options_helpers.rb +7 -4
  19. data/lib/pry/helpers/text.rb +23 -3
  20. data/lib/pry/history.rb +55 -17
  21. data/lib/pry/history_array.rb +2 -0
  22. data/lib/pry/hooks.rb +108 -0
  23. data/lib/pry/indent.rb +9 -5
  24. data/lib/pry/method.rb +99 -50
  25. data/lib/pry/plugins.rb +10 -2
  26. data/lib/pry/pry_class.rb +48 -20
  27. data/lib/pry/pry_instance.rb +106 -91
  28. data/lib/pry/version.rb +1 -1
  29. data/lib/pry/wrapped_module.rb +73 -0
  30. data/man/pry.1 +195 -0
  31. data/man/pry.1.html +204 -0
  32. data/man/pry.1.ronn +141 -0
  33. data/pry.gemspec +21 -24
  34. data/test/helper.rb +12 -3
  35. data/test/test_cli.rb +78 -0
  36. data/test/test_command_set.rb +193 -1
  37. data/test/test_default_commands/test_context.rb +19 -4
  38. data/test/test_default_commands/test_input.rb +2 -2
  39. data/test/test_default_commands/test_introspection.rb +63 -6
  40. data/test/test_default_commands/test_ls.rb +8 -35
  41. data/test/test_default_commands/test_shell.rb +36 -1
  42. data/test/test_hooks.rb +175 -0
  43. data/test/test_indent.rb +2 -0
  44. data/test/test_method.rb +10 -0
  45. data/test/test_pry.rb +35 -34
  46. data/test/test_pry_history.rb +24 -24
  47. data/test/test_syntax_checking.rb +47 -0
  48. data/test/test_wrapped_module.rb +71 -0
  49. metadata +40 -34
data/lib/pry/history.rb CHANGED
@@ -1,61 +1,99 @@
1
1
  class Pry
2
2
  # The History class is responsible for maintaining the user's input history, both
3
- # internally and within Readline::HISTORY.
3
+ # internally and within Readline.
4
4
  class History
5
+ attr_accessor :loader, :saver, :pusher, :clearer
6
+
5
7
  def initialize
6
8
  @history = []
7
9
  @saved_lines = 0
10
+ restore_default_behavior
11
+ end
12
+
13
+ # Assign the default methods for loading, saving, pushing, and clearing.
14
+ def restore_default_behavior
15
+ @loader = method(:read_from_file)
16
+ @saver = method(:write_to_file)
17
+ @pusher = method(:push_to_readline)
18
+ @clearer = method(:clear_readline)
8
19
  end
9
20
 
10
- # Loads a file's contents into the input history.
11
- # @param [String] filename
21
+ # Load the input history using `History.loader`.
12
22
  # @return [Integer] The number of lines loaded
13
- def load(filename)
14
- File.foreach(filename) do |line|
15
- Readline::HISTORY << line.chomp
23
+ def load
24
+ @loader.call do |line|
25
+ @pusher.call(line.chomp)
16
26
  @history << line.chomp
17
27
  end
18
28
  @saved_lines = @history.length
19
29
  end
20
30
 
21
- # Appends input history from this session to a file.
22
- # @param [String] filename
31
+ # Write this session's history using `History.saver`.
23
32
  # @return [Integer] The number of lines saved
24
- def save(filename)
33
+ def save
25
34
  history_to_save = @history[@saved_lines..-1]
26
- File.open(filename, 'a') do |f|
27
- history_to_save.each { |ln| f.puts ln }
28
- end
35
+ @saver.call(history_to_save)
29
36
  @saved_lines = @history.length
30
37
  history_to_save.length
31
38
  end
32
39
 
33
- # Adds a line to the input history, ignoring blank and duplicate lines.
40
+ # Add a line to the input history, ignoring blank and duplicate lines.
34
41
  # @param [String] line
35
42
  # @return [String] The same line that was passed in
36
43
  def push(line)
37
44
  unless line.empty? || (@history.last && line == @history.last)
38
- Readline::HISTORY << line
45
+ @pusher.call(line)
39
46
  @history << line
40
47
  end
41
48
  line
42
49
  end
43
50
  alias << push
44
51
 
45
- # Clears all history. Anything the user entered before this point won't be
52
+ # Clear all history. Anything the user entered before this point won't be
46
53
  # saved, but anything they put in afterwards will still be appended to the
47
54
  # history file on exit.
48
55
  def clear
49
- Readline::HISTORY.shift until Readline::HISTORY.empty?
56
+ @clearer.call
50
57
  @history = []
51
58
  @saved_lines = 0
52
59
  end
53
60
 
54
- # Returns an Array containing all stored history.
61
+ # Return an Array containing all stored history.
55
62
  # @return [Array<String>] An Array containing all lines of history loaded
56
63
  # or entered by the user in the current session.
57
64
  def to_a
58
65
  @history.dup
59
66
  end
67
+
68
+ private
69
+ # The default loader. Yields lines from `Pry.history.config.file`.
70
+ def read_from_file
71
+ history_file = File.expand_path(Pry.config.history.file)
72
+
73
+ if File.exists?(history_file)
74
+ File.foreach(history_file) { |line| yield(line) }
75
+ end
76
+ end
77
+
78
+ # The default saver. Appends the given lines to `Pry.history.config.file`.
79
+ # @param [Array<String>] lines
80
+ def write_to_file(lines)
81
+ history_file = File.expand_path(Pry.config.history.file)
82
+
83
+ File.open(history_file, 'a') do |f|
84
+ lines.each { |ln| f.puts ln }
85
+ end
86
+ end
87
+
88
+ # The default pusher. Appends the given line to Readline::HISTORY.
89
+ # @param [String] line
90
+ def push_to_readline(line)
91
+ Readline::HISTORY << line
92
+ end
93
+
94
+ # The default clearer. Clears Readline::HISTORY.
95
+ def clear_readline
96
+ Readline::HISTORY.shift until Readline::HISTORY.empty?
97
+ end
60
98
  end
61
99
  end
@@ -72,6 +72,8 @@ class Pry
72
72
  def size
73
73
  @count
74
74
  end
75
+ alias count size
76
+ alias length size
75
77
 
76
78
  def empty?
77
79
  size == 0
data/lib/pry/hooks.rb ADDED
@@ -0,0 +1,108 @@
1
+ class Pry
2
+ class Hooks
3
+
4
+ def initialize
5
+ @hooks = {}
6
+ end
7
+
8
+ # Add a new hook to be executed for the `name` even.
9
+ # @param [Symbol] event_name The name of the event.
10
+ # @param [Symbol] hook_name The name of the hook.
11
+ # @param [#call] callable The callable.
12
+ # @yield The block to use as the callable (if `callable` parameter not provided)
13
+ def add_hook(event_name, hook_name, callable=nil, &block)
14
+ @hooks[event_name] ||= []
15
+
16
+ # do not allow duplicates
17
+ raise ArgumentError, "Hook with name '#{hook_name}' already defined!" if hook_exists?(event_name, hook_name)
18
+
19
+ if block
20
+ @hooks[event_name] << [hook_name, block]
21
+ elsif callable
22
+ @hooks[event_name] << [hook_name, callable]
23
+ else
24
+ raise ArgumentError, "Must provide a block or callable."
25
+ end
26
+
27
+ self
28
+ end
29
+
30
+ # Execute the list of hooks for the `event_name` event.
31
+ # @param [Symbol] event_name The name of the event.
32
+ # @param [Array] args The arguments to pass to each hook function.
33
+ # @return [Object] The return value of the last executed hook.
34
+ def exec_hook(event_name, *args, &block)
35
+ @hooks[event_name] ||= []
36
+
37
+ # silence warnings to get rid of 1.8's "warning: multiple values
38
+ # for a block parameter" warnings
39
+ Pry::Helpers::BaseHelpers.silence_warnings do
40
+ @hooks[event_name].map { |hook_name, callable| callable.call(*args, &block) }.last
41
+ end
42
+ end
43
+
44
+ # Return the number of hook functions registered for the `event_name` event.
45
+ # @param [Symbol] event_name The name of the event.
46
+ # @return [Fixnum] The number of hook functions for `event_name`.
47
+ def hook_count(event_name)
48
+ @hooks[event_name] ||= []
49
+ @hooks[event_name].size
50
+ end
51
+
52
+ # Return a specific hook for a given event.
53
+ # @param [Symbol] event_name The name of the event.
54
+ # @param [Symbol[ hook_name The name of the hook
55
+ # @return [#call] The requested hook.
56
+ def get_hook(event_name, hook_name)
57
+ @hooks[event_name] ||= []
58
+ hook = @hooks[event_name].find { |current_hook_name, callable| current_hook_name == hook_name }
59
+ hook.last if hook
60
+ end
61
+
62
+ # Return the hash of hook names / hook functions for a
63
+ # given event. (Note that modifying the returned hash does not
64
+ # alter the hooks, use add_hook/delete_hook for that).
65
+ # @param [Symbol] event_name The name of the event.
66
+ # @return [Hash] The hash of hook names / hook functions.
67
+ def get_hooks(event_name)
68
+ @hooks[event_name] ||= []
69
+ Hash[@hooks[event_name]]
70
+ end
71
+
72
+ # Delete a hook for an event.
73
+ # @param [Symbol] event_name The name of the event.
74
+ # @param [Symbol] hook_name The name of the hook.
75
+ # to delete.
76
+ # @return [#call] The deleted hook.
77
+ def delete_hook(event_name, hook_name)
78
+ @hooks[event_name] ||= []
79
+ deleted_callable = nil
80
+
81
+ @hooks[event_name].delete_if do |current_hook_name, callable|
82
+ if current_hook_name == hook_name
83
+ deleted_callable = callable
84
+ true
85
+ else
86
+ false
87
+ end
88
+ end
89
+ deleted_callable
90
+ end
91
+
92
+ # Clear all hooks functions for a given event.
93
+ # @param [String] event_name The name of the event.
94
+ def clear(event_name)
95
+ @hooks[event_name] = []
96
+ end
97
+
98
+ # @param [Symbol] event_name Name of the event.
99
+ # @param [Symbol] hook_name Name of the hook.
100
+ # @return [Boolean] Whether the hook by the name `hook_name`
101
+ # is defined for the event.
102
+ def hook_exists?(event_name, hook_name)
103
+ !!@hooks[event_name].find { |name, _| name == hook_name }
104
+ end
105
+ private :hook_exists?
106
+
107
+ end
108
+ end
data/lib/pry/indent.rb CHANGED
@@ -43,7 +43,11 @@ class Pry
43
43
  # Collection of token types that should be ignored. Without this list
44
44
  # keywords such as "class" inside strings would cause the code to be
45
45
  # indented incorrectly.
46
- IGNORE_TOKENS = [:space, :content, :string, :delimiter, :method, :ident]
46
+ #
47
+ # :pre_constant and :preserved_constant are the CodeRay 0.9.8 and 1.0.0
48
+ # classifications of "true", "false", and "nil".
49
+ IGNORE_TOKENS = [:space, :content, :string, :delimiter, :method, :ident,
50
+ :constant, :pre_constant, :predefined_constant]
47
51
 
48
52
  # Tokens that indicate the end of a statement (i.e. that, if they appear
49
53
  # directly before an "if" indicates that that if applies to the same line,
@@ -199,11 +203,11 @@ class Pry
199
203
  end
200
204
 
201
205
  if defined?(Win32::Console)
202
- move_up = "\e[#{lines}F"
203
- move_down = "\e[#{lines}E"
206
+ move_up = "\e[#{lines}F"
207
+ move_down = "\e[#{lines}E"
204
208
  else
205
- move_up = "\e[#{lines}A\e[0G"
206
- move_down = "\e[#{lines}B\e[0G"
209
+ move_up = "\e[#{lines}A\e[0G"
210
+ move_down = "\e[#{lines}B\e[0G"
207
211
  end
208
212
  whitespace = ' ' * overhang
209
213
 
data/lib/pry/method.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  class Pry
2
3
  class Method
3
4
  include RbxMethod if Helpers::BaseHelpers.rbx?
@@ -48,7 +49,11 @@ class Pry
48
49
  if [:__script__, nil, :__binding__, :__binding_impl__].include?(meth_name)
49
50
  nil
50
51
  else
51
- new(b.eval("method(:#{meth_name})"))
52
+ begin
53
+ new(b.eval("method(#{meth_name.to_s.inspect})"))
54
+ rescue NameError, NoMethodError
55
+ Disowned.new(b.eval('self'), meth_name.to_s)
56
+ end
52
57
  end
53
58
  end
54
59
 
@@ -128,7 +133,7 @@ class Pry
128
133
  end
129
134
 
130
135
  # Acts like send but ignores any methods defined below Object or Class in the
131
- # inheritance heirarchy.
136
+ # inheritance hierarchy.
132
137
  # This is required to introspect methods on objects like Net::HTTP::Get that
133
138
  # have overridden the `method` method.
134
139
  def safe_send(obj, method, *args, &block)
@@ -166,6 +171,27 @@ class Pry
166
171
  @method.name.to_s
167
172
  end
168
173
 
174
+ # Get the owner of the method as a Pry::Module
175
+ # @return [Pry::Module]
176
+ def wrapped_owner
177
+ @wrapped_owner ||= Pry::WrappedModule.new(owner)
178
+ end
179
+
180
+ # Is the method undefined? (aka `Disowned`)
181
+ # @return [Boolean] false
182
+ def undefined?
183
+ false
184
+ end
185
+
186
+ # Get the name of the method including the class on which it was defined.
187
+ # @example
188
+ # method(:puts).method_name
189
+ # => "Kernel.puts"
190
+ # @return [String]
191
+ def name_with_owner
192
+ "#{wrapped_owner.method_prefix}#{name}"
193
+ end
194
+
169
195
  # @return [String, nil] The source code of the method, or `nil` if it's unavailable.
170
196
  def source
171
197
  @source ||= case source_type
@@ -174,11 +200,11 @@ class Pry
174
200
  if info and info.source
175
201
  code = strip_comments_from_c_code(info.source)
176
202
  end
177
- when :rbx
178
- strip_leading_whitespace(core_code)
179
203
  when :ruby
180
- if pry_method?
181
- code = Pry.new(:input => StringIO.new(Pry.line_buffer[source_line..-1].join), :prompt => proc {""}, :hooks => {}).r
204
+ if Helpers::BaseHelpers.rbx? && core?
205
+ code = core_code
206
+ elsif pry_method?
207
+ code = Pry.new(:input => StringIO.new(Pry.line_buffer[source_line..-1].join), :prompt => proc {""}, :hooks => Pry::Hooks.new).r
182
208
  else
183
209
  code = @method.source
184
210
  end
@@ -194,10 +220,10 @@ class Pry
194
220
  when :c
195
221
  info = pry_doc_info
196
222
  info.docstring if info
197
- when :rbx
198
- strip_leading_hash_and_whitespace_from_ruby_comments(core_doc)
199
223
  when :ruby
200
- if pry_method?
224
+ if Helpers::BaseHelpers.rbx? && core?
225
+ strip_leading_hash_and_whitespace_from_ruby_comments(core_doc)
226
+ elsif pry_method?
201
227
  raise CommandError, "Can't view doc for a REPL-defined method."
202
228
  else
203
229
  strip_leading_hash_and_whitespace_from_ruby_comments(@method.comment)
@@ -206,14 +232,9 @@ class Pry
206
232
  end
207
233
 
208
234
  # @return [Symbol] The source type of the method. The options are
209
- # `:ruby` for ordinary Ruby methods, `:c` for methods written in
210
- # C, or `:rbx` for Rubinius core methods written in Ruby.
235
+ # `:ruby` for Ruby methods or `:c` for methods written in C.
211
236
  def source_type
212
- if Helpers::BaseHelpers.rbx?
213
- if core? then :rbx else :ruby end
214
- else
215
- if source_location.nil? then :c else :ruby end
216
- end
237
+ source_location.nil? ? :c : :ruby
217
238
  end
218
239
 
219
240
  # @return [String, nil] The name of the file the method is defined in, or
@@ -286,43 +307,11 @@ class Pry
286
307
  Pry::Method.new(sup) if sup
287
308
  end
288
309
 
289
- # @return [Symbol, nil] The original name the method was defined under,
310
+ # @return [String, nil] The original name the method was defined under,
290
311
  # before any aliasing, or `nil` if it can't be determined.
291
312
  def original_name
292
313
  return nil if source_type != :ruby
293
-
294
- first_line = source.lines.first
295
- return nil if first_line.strip !~ /^def /
296
-
297
- if RUBY_VERSION =~ /^1\.9/ && RUBY_ENGINE == "ruby"
298
- require 'ripper'
299
-
300
- # Ripper is ok with an extraneous end, so we don't need to worry about
301
- # whether it's a one-liner.
302
- tree = Ripper::SexpBuilder.new(first_line + ";end").parse
303
-
304
- name = tree.flatten(2).each do |lst|
305
- break lst[1] if lst[0] == :@ident
306
- end
307
-
308
- name.is_a?(String) ? name : nil
309
- else
310
- require 'ruby_parser'
311
-
312
- # RubyParser breaks if there's an extra end, so we'll just rescue
313
- # and try again.
314
- tree = begin
315
- RubyParser.new.parse(first_line + ";end")
316
- rescue Racc::ParseError
317
- RubyParser.new.parse(first_line)
318
- end
319
-
320
- name = tree.each_cons(2) do |a, b|
321
- break a if b.is_a?(Array) && b.first == :args
322
- end
323
-
324
- name.is_a?(Symbol) ? name.to_s : nil
325
- end
314
+ method_name_from_first_line(source.lines.first)
326
315
  end
327
316
 
328
317
  # @return [Boolean] Was the method defined outside a source file?
@@ -409,5 +398,65 @@ class Pry
409
398
  end
410
399
  next_owner.instance_method(name) rescue nil
411
400
  end
401
+
402
+ # @param [String] first_ln The first line of a method definition.
403
+ # @return [String, nil]
404
+ def method_name_from_first_line(first_ln)
405
+ return nil if first_ln.strip !~ /^def /
406
+
407
+ tokens = CodeRay.scan(first_ln, :ruby)
408
+ tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens)
409
+ tokens.each_cons(2) do |t1, t2|
410
+ if t2.last == :method || t2.last == :ident && t1 == [".", :operator]
411
+ return t2.first
412
+ end
413
+ end
414
+
415
+ nil
416
+ end
417
+
418
+ # A Disowned Method is one that's been removed from the class on which it was defined.
419
+ #
420
+ # e.g.
421
+ # class C
422
+ # def foo
423
+ # C.send(:undefine_method, :foo)
424
+ # Pry::Method.from_binding(binding)
425
+ # end
426
+ # end
427
+ #
428
+ # In this case we assume that the "owner" is the singleton class of the receiver.
429
+ #
430
+ # This occurs mainly in Sinatra applications.
431
+ class Disowned < Method
432
+ attr_reader :receiver, :name
433
+
434
+ # Create a new Disowned method.
435
+ #
436
+ # @param [Object] receiver
437
+ # @param [String] method_name
438
+ def initialize(*args)
439
+ @receiver, @name = *args
440
+ end
441
+
442
+ # Is the method undefined? (aka `Disowned`)
443
+ # @return [Boolean] true
444
+ def undefined?
445
+ true
446
+ end
447
+
448
+ # Get the hypothesized owner of the method.
449
+ #
450
+ # @return [Object]
451
+ def owner
452
+ class << receiver; self; end
453
+ end
454
+
455
+ # Raise a more useful error message instead of trying to forward to nil.
456
+ def method_missing(meth_name, *args, &block)
457
+ raise "Cannot call '#{meth_name}' on an undef'd method." if method(:name).respond_to?(meth_name)
458
+ Object.instance_method(:method_missing).bind(self).call(meth_name, *args, &block)
459
+ end
460
+ end
412
461
  end
413
462
  end