pry 0.9.11.4 → 0.9.12pre1

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 (41) hide show
  1. data/.travis.yml +2 -0
  2. data/Rakefile +4 -0
  3. data/lib/pry.rb +1 -1
  4. data/lib/pry/cli.rb +14 -8
  5. data/lib/pry/code.rb +3 -3
  6. data/lib/pry/command.rb +20 -5
  7. data/lib/pry/command_set.rb +3 -3
  8. data/lib/pry/commands.rb +1 -1
  9. data/lib/pry/commands/disabled_commands.rb +2 -0
  10. data/lib/pry/commands/ls.rb +1 -2
  11. data/lib/pry/commands/reload_code.rb +8 -1
  12. data/lib/pry/commands/show_info.rb +27 -4
  13. data/lib/pry/commands/show_source.rb +2 -1
  14. data/lib/pry/commands/whereami.rb +87 -19
  15. data/lib/pry/completion.rb +7 -4
  16. data/lib/pry/helpers/base_helpers.rb +5 -2
  17. data/lib/pry/helpers/command_helpers.rb +3 -1
  18. data/lib/pry/helpers/documentation_helpers.rb +18 -7
  19. data/lib/pry/helpers/table.rb +4 -4
  20. data/lib/pry/indent.rb +2 -7
  21. data/lib/pry/method.rb +89 -129
  22. data/lib/pry/method/disowned.rb +53 -0
  23. data/lib/pry/method/weird_method_locator.rb +186 -0
  24. data/lib/pry/module_candidate.rb +3 -3
  25. data/lib/pry/pager.rb +13 -11
  26. data/lib/pry/pry_class.rb +10 -3
  27. data/lib/pry/terminal.rb +73 -0
  28. data/lib/pry/version.rb +1 -1
  29. data/lib/pry/wrapped_module.rb +51 -1
  30. data/spec/command_helpers_spec.rb +21 -1
  31. data/spec/commands/ls_spec.rb +4 -0
  32. data/spec/commands/show_doc_spec.rb +28 -28
  33. data/spec/commands/show_source_spec.rb +70 -22
  34. data/spec/commands/whereami_spec.rb +60 -11
  35. data/spec/documentation_helper_spec.rb +73 -0
  36. data/spec/fixtures/whereami_helper.rb +6 -0
  37. data/spec/helpers/table_spec.rb +19 -0
  38. data/spec/method_spec.rb +24 -7
  39. metadata +13 -7
  40. data/lib/pry/commands/deprecated_commands.rb +0 -2
  41. data/lib/pry/terminal_info.rb +0 -48
@@ -0,0 +1,186 @@
1
+ class Pry
2
+ class Method
3
+
4
+ # This class is responsible for locating the *real* `Pry::Method`
5
+ # object captured by a binding.
6
+ #
7
+ # Given a `Binding` from inside a method and a 'seed' Pry::Method object,
8
+ # there are primarily two situations where the seed method doesn't match
9
+ # the Binding:
10
+ # 1. The Pry::Method is from a subclass 2. The Pry::Method represents a method of the same name
11
+ # while the original was renamed to something else. For 1. we
12
+ # search vertically up the inheritance chain,
13
+ # and for 2. we search laterally along the object's method table.
14
+ #
15
+ # When we locate the method that matches the Binding we wrap it in
16
+ # Pry::Method and return it, or return nil if we fail.
17
+ class WeirdMethodLocator
18
+ class << self
19
+
20
+ # Whether the given method object matches the associated binding.
21
+ # If the method object does not match the binding, then it's
22
+ # most likely not the method captured by the binding, and we
23
+ # must commence a search.
24
+ #
25
+ # @param [Pry::Method] method
26
+ # @param [Binding] b
27
+ # @return [Boolean]
28
+ def normal_method?(method, b)
29
+ method && (method.source_file && method.source_range rescue false) &&
30
+ File.expand_path(method.source_file) == File.expand_path(b.eval('__FILE__')) &&
31
+ method.source_range.include?(b.eval('__LINE__'))
32
+ end
33
+
34
+ def weird_method?(method, b)
35
+ !normal_method?(method, b)
36
+ end
37
+ end
38
+
39
+ attr_accessor :method
40
+ attr_accessor :target
41
+
42
+ # @param [Pry::Method] method The seed method.
43
+ # @param [Binding] target The Binding that captures the method
44
+ # we want to locate.
45
+ def initialize(method, target)
46
+ @method, @target = method, target
47
+ end
48
+
49
+ # @return [Pry::Method, nil] The Pry::Method that matches the
50
+ # given binding.
51
+ def get_method
52
+ find_method_in_superclass || find_renamed_method
53
+ end
54
+
55
+ # @return [Boolean] Whether the Pry::Method is unrecoverable
56
+ # This usually happens when the method captured by the Binding
57
+ # has been subsequently deleted.
58
+ def lost_method?
59
+ !!(get_method.nil? && renamed_method_source_location)
60
+ end
61
+
62
+ private
63
+
64
+ def normal_method?(method)
65
+ self.class.normal_method?(method, target)
66
+ end
67
+
68
+ def target_self
69
+ target.eval('self')
70
+ end
71
+
72
+ def target_file
73
+ pry_file? ? target.eval('__FILE__') : File.expand_path(target.eval('__FILE__'))
74
+ end
75
+
76
+ def target_line
77
+ target.eval('__LINE__')
78
+ end
79
+
80
+ def pry_file?
81
+ Pry.eval_path == target.eval('__FILE__')
82
+ end
83
+
84
+ # it's possible in some cases that the method we find by this approach is a sub-method of
85
+ # the one we're currently in, consider:
86
+ #
87
+ # class A; def b; binding.pry; end; end
88
+ # class B < A; def b; super; end; end
89
+ #
90
+ # Given that we can normally find the source_range of methods, and that we know which
91
+ # __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
92
+ #
93
+ # This obviously won't work if the source is unavaiable for some reason, or if both
94
+ # methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
95
+ # is broken.
96
+ #
97
+ # @return [Pry::Method, nil] The Pry::Method representing the
98
+ # superclass method.
99
+ def find_method_in_superclass
100
+ guess = method
101
+
102
+ while guess
103
+ # needs rescue if this is a Disowned method or a C method or something...
104
+ # TODO: Fix up the exception handling so we don't need a bare rescue
105
+ if normal_method?(guess)
106
+ return guess
107
+ else
108
+ guess = guess.super
109
+ end
110
+ end
111
+
112
+ # Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
113
+ # This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
114
+ # or other unknown circumstances (TODO: we should warn the user when this happens)
115
+ nil
116
+ end
117
+
118
+ # This is the case where the name of a method has changed
119
+ # (via alias_method) so we locate the Method object for the
120
+ # renamed method.
121
+ #
122
+ # @return [Pry::Method, nil] The Pry::Method representing the
123
+ # renamed method
124
+ def find_renamed_method
125
+ return if !valid_file?(target_file)
126
+ alias_name = all_methods_for(target_self).find do |v|
127
+ expanded_source_location(target_self.method(v).source_location) == renamed_method_source_location
128
+ end
129
+
130
+ alias_name && Pry::Method(target_self.method(alias_name))
131
+ end
132
+
133
+ def expanded_source_location(sl)
134
+ return if !sl
135
+
136
+ if pry_file?
137
+ sl
138
+ else
139
+ [File.expand_path(sl.first), sl.last]
140
+ end
141
+ end
142
+
143
+ # Use static analysis to locate the start of the method definition.
144
+ # We have the `__FILE__` and `__LINE__` from the binding and the
145
+ # original name of the method so we search up until we find a
146
+ # def/define_method, etc defining a method of the appropriate name.
147
+ #
148
+ # @return [Array<String, Fixnum>] The `source_location` of the
149
+ # renamed method
150
+ def renamed_method_source_location
151
+ return @original_method_source_location if defined?(@original_method_source_location)
152
+
153
+ source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v|
154
+ Pry::Method.method_definition?(method.name, v)
155
+ end
156
+
157
+ @original_method_source_location = source_index &&
158
+ [target_file, index_to_line_number(source_index)]
159
+ end
160
+
161
+ def index_to_line_number(index)
162
+ # Pry.line_buffer is 0-indexed
163
+ pry_file? ? index : index + 1
164
+ end
165
+
166
+ def valid_file?(file)
167
+ File.exists?(file) || Pry.eval_path == file
168
+ end
169
+
170
+ def lines_for_file(file)
171
+ @lines_for_file ||= {}
172
+ @lines_for_file[file] ||= if Pry.eval_path == file
173
+ Pry.line_buffer
174
+ else
175
+ File.readlines(file)
176
+ end
177
+ end
178
+
179
+ def all_methods_for(obj)
180
+ obj.public_methods(false) +
181
+ obj.private_methods(false) +
182
+ obj.protected_methods(false)
183
+ end
184
+ end
185
+ end
186
+ end
@@ -52,8 +52,8 @@ class Pry
52
52
  # @return [String] The source for the candidate, i.e the
53
53
  # complete module/class definition.
54
54
  def source
55
+ return nil if file.nil?
55
56
  return @source if @source
56
- raise CommandError, "Could not locate source for #{wrapped}!" if file.nil?
57
57
 
58
58
  @source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line, number_of_lines_in_first_chunk))
59
59
  end
@@ -61,10 +61,10 @@ class Pry
61
61
  # @raise [Pry::CommandError] If documentation cannot be found.
62
62
  # @return [String] The documentation for the candidate.
63
63
  def doc
64
+ return nil if file.nil?
64
65
  return @doc if @doc
65
- raise CommandError, "Could not locate doc for #{wrapped}!" if file.nil?
66
66
 
67
- @doc = strip_leading_hash_and_whitespace_from_ruby_comments(Pry::Code.from_file(file).comment_describing(line))
67
+ @doc = get_comment_content(Pry::Code.from_file(file).comment_describing(line))
68
68
  end
69
69
 
70
70
  # @return [Array, nil] A `[String, Fixnum]` pair representing the
@@ -12,8 +12,11 @@ class Pry::Pager
12
12
  case pager
13
13
  when nil
14
14
  no_pager = !SystemPager.available?
15
- is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
16
- (is_jruby || no_pager) ? SimplePager.new(text).page : SystemPager.new(text).page
15
+ if no_pager || Pry::Helpers::BaseHelpers.jruby?
16
+ SimplePager.new(text).page
17
+ else
18
+ SystemPager.new(text).page
19
+ end
17
20
  when :simple
18
21
  SimplePager.new(text).page
19
22
  when :system
@@ -24,12 +27,8 @@ class Pry::Pager
24
27
  end
25
28
 
26
29
  def self.page_size
27
- @page_size ||= begin
28
- require 'io/console'
29
- $stdout.winsize.first
30
- rescue LoadError
31
- 27
32
- end
30
+ rows = Pry::Terminal.screen_size
31
+ @page_size ||= (rows && rows.first || 27)
33
32
  end
34
33
 
35
34
  def initialize(text)
@@ -38,11 +37,14 @@ class Pry::Pager
38
37
 
39
38
  class SimplePager < Pry::Pager
40
39
  def page
40
+ # The pager size minus the number of lines used by the simple pager info bar.
41
+ page_size = Pry::Pager.page_size - 3
41
42
  text_array = @text.lines.to_a
42
- text_array.each_slice(Pry::Pager.page_size) do |chunk|
43
+
44
+ text_array.each_slice(page_size) do |chunk|
43
45
  puts chunk.join
44
- break if chunk.size < Pry::Pager.page_size
45
- if text_array.size > Pry::Pager.page_size
46
+ break if chunk.size < page_size
47
+ if text_array.size > page_size
46
48
  puts "\n<page break> --- Press enter to continue ( q<enter> to break ) --- <page break>"
47
49
  break if $stdin.gets.chomp == "q"
48
50
  end
@@ -263,9 +263,16 @@ class Pry
263
263
 
264
264
  def self.auto_resize!
265
265
  trap :WINCH do
266
- size = `stty size`.split(/\s+/).map &:to_i
267
- Readline.set_screen_size *size
268
- Readline.refresh_line
266
+ begin
267
+ Readline.set_screen_size *Terminal.size!
268
+ rescue => e
269
+ warn "\nPry.auto_resize!'s Readline.set_screen_size failed: #{e}"
270
+ end
271
+ begin
272
+ Readline.refresh_line
273
+ rescue => e
274
+ warn "\nPry.auto_resize!'s Readline.refresh_line failed: #{e}"
275
+ end
269
276
  end
270
277
  end
271
278
 
@@ -0,0 +1,73 @@
1
+ class Pry::Terminal
2
+ class << self
3
+ # Return a pair of [rows, columns] which gives the size of the window.
4
+ #
5
+ # If the window size cannot be determined, return nil.
6
+ def screen_size
7
+ rows, cols = actual_screen_size
8
+ if rows && cols
9
+ [rows.to_i, cols.to_i]
10
+ else
11
+ nil
12
+ end
13
+ end
14
+
15
+ # Return a screen size or a default if that fails.
16
+ def size! default = [25, 80]
17
+ screen_size || default
18
+ end
19
+
20
+ # Return a screen width or the default if that fails.
21
+ def width! default = 80
22
+ size![1]
23
+ end
24
+
25
+ def actual_screen_size
26
+ # The best way, if possible (requires non-jruby ≥1.9 or io-console gem)
27
+ screen_size_according_to_io_console or
28
+ # Fall back to the old standby, though it might be stale:
29
+ screen_size_according_to_env or
30
+ # Fall further back, though this one is also out of date without something
31
+ # calling Readline.set_screen_size
32
+ screen_size_according_to_readline or
33
+ # Windows users can otherwise run ansicon and get a decent answer:
34
+ screen_size_according_to_ansicon_env
35
+ end
36
+
37
+ def screen_size_according_to_io_console
38
+ return if Pry::Helpers::BaseHelpers.jruby?
39
+ require 'io/console'
40
+ $stdout.winsize if $stdout.tty? and $stdout.respond_to?(:winsize)
41
+ rescue LoadError
42
+ # They're probably on 1.8 without the io-console gem. We'll keep trying.
43
+ end
44
+
45
+ def screen_size_according_to_env
46
+ size = [ENV['LINES'] || ENV['ROWS'], ENV['COLUMNS']]
47
+ size if nonzero_column?(size)
48
+ end
49
+
50
+ def screen_size_according_to_readline
51
+ if Readline.respond_to?(:get_screen_size)
52
+ size = Readline.get_screen_size
53
+ size if nonzero_column?(size)
54
+ end
55
+ rescue Java::JavaLang::NullPointerException
56
+ # This rescue won't happen on jrubies later than:
57
+ # https://github.com/jruby/jruby/pull/436
58
+ nil
59
+ end
60
+
61
+ def screen_size_according_to_ansicon_env
62
+ return unless ENV['ANSICON'] =~ /\((.*)x(.*)\)/
63
+ size = [$2, $1]
64
+ size if nonzero_column?(size)
65
+ end
66
+
67
+ private
68
+
69
+ def nonzero_column?(size)
70
+ size[1].to_i > 0
71
+ end
72
+ end
73
+ end
@@ -1,3 +1,3 @@
1
1
  class Pry
2
- VERSION = "0.9.11.4"
2
+ VERSION = "0.9.12pre1"
3
3
  end
@@ -65,6 +65,30 @@ class Pry
65
65
  @doc = nil
66
66
  end
67
67
 
68
+ # Returns an array of the names of the constants accessible in the wrapped
69
+ # module. This provides a consistent interface between 1.8 and 1.9 and also
70
+ # avoids the problem of accidentally calling the singleton method
71
+ # `Module.constants`.
72
+ # @param [Boolean] inherit (true) Include the names of constants from
73
+ # included modules?
74
+ def constants(inherit = true)
75
+ method = Module.instance_method(:constants).bind(@wrapped)
76
+
77
+ # If we're on 1.8, we have to manually remove ancestors' constants. If
78
+ # we're on 1.9, though, it's better to use the built-in `inherit` param,
79
+ # since it doesn't do things like incorrectly remove Pry::Config.
80
+ if method.arity == 0
81
+ consts = method.call
82
+ if !inherit
83
+ consts -= (@wrapped.ancestors - [@wrapped]).map(&:constants).flatten
84
+ end
85
+ else
86
+ consts = method.call(inherit)
87
+ end
88
+
89
+ consts
90
+ end
91
+
68
92
  # The prefix that would appear before methods defined on this class.
69
93
  #
70
94
  # i.e. the "String." or "String#" in String.new and String#initialize.
@@ -205,13 +229,27 @@ class Pry
205
229
  @memoized_candidates[rank] ||= Candidate.new(self, rank)
206
230
  end
207
231
 
208
-
209
232
  # @return [Fixnum] The number of candidate definitions for the
210
233
  # current module.
211
234
  def number_of_candidates
212
235
  method_candidates.count
213
236
  end
214
237
 
238
+ # @note On JRuby 1.9 and higher, in certain conditions, this method chucks
239
+ # away its ability to be quick (when there are lots of monkey patches,
240
+ # like in Rails). However, it should be efficient enough on other rubies.
241
+ # @see https://github.com/jruby/jruby/issues/525
242
+ # @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on
243
+ # other rubies returns Enumerator
244
+ def candidates
245
+ enum = generator.new do |y|
246
+ (0...number_of_candidates).each do |num|
247
+ y.yield candidate(num)
248
+ end
249
+ end
250
+ Pry::Helpers::BaseHelpers.jruby_19? ? enum.to_a : enum
251
+ end
252
+
215
253
  # @return [Boolean] Whether YARD docs are available for this module.
216
254
  def yard_docs?
217
255
  !!(defined?(YARD) && YARD::Registry.at(name))
@@ -237,6 +275,18 @@ class Pry
237
275
 
238
276
  private
239
277
 
278
+ # Ruby 1.8 doesn't support `Enumerator` (it's called Generator instead)
279
+ #
280
+ # @return [Object] Return the appropriate generator class.
281
+ def generator
282
+ @generator ||= if defined?(Enumerator)
283
+ Enumerator
284
+ else
285
+ require 'generator'
286
+ Generator
287
+ end
288
+ end
289
+
240
290
  # @return [Pry::WrappedModule::Candidate] The candidate of rank 0,
241
291
  # that is the 'monkey patch' of this module with the highest
242
292
  # number of methods. It is considered the 'canonical' definition
@@ -5,5 +5,25 @@ describe Pry::Helpers::CommandHelpers do
5
5
  @helper = Pry::Helpers::CommandHelpers
6
6
  end
7
7
 
8
- # FIXME: currently no tests
8
+ describe "unindent" do
9
+ it "should remove the same prefix from all lines" do
10
+ @helper.unindent(" one\n two\n").should == "one\ntwo\n"
11
+ end
12
+
13
+ it "should not be phased by empty lines" do
14
+ @helper.unindent(" one\n\n two\n").should == "one\n\ntwo\n"
15
+ end
16
+
17
+ it "should only remove a common prefix" do
18
+ @helper.unindent(" one\n two\n").should == " one\ntwo\n"
19
+ end
20
+
21
+ it "should also remove tabs if present" do
22
+ @helper.unindent("\tone\n\ttwo\n").should == "one\ntwo\n"
23
+ end
24
+
25
+ it "should ignore lines starting with --" do
26
+ @helper.unindent(" one\n--\n two\n").should == "one\n--\ntwo\n"
27
+ end
28
+ end
9
29
  end