pry-doc 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGELOG.md +7 -1
  4. data/README.md +2 -8
  5. data/Rakefile +11 -9
  6. data/lib/pry-doc.rb +11 -227
  7. data/lib/pry-doc/{core_docs_19 → docs/19}/checksums +0 -0
  8. data/lib/pry-doc/{core_docs_19 → docs/19}/object_types +0 -0
  9. data/lib/pry-doc/{core_docs_19 → docs/19}/objects/root.dat +0 -0
  10. data/lib/pry-doc/{core_docs_19 → docs/19}/proxy_types +0 -0
  11. data/lib/pry-doc/{core_docs_20 → docs/20}/checksums +0 -0
  12. data/lib/pry-doc/{core_docs_20 → docs/20}/object_types +0 -0
  13. data/lib/pry-doc/{core_docs_20 → docs/20}/objects/root.dat +0 -0
  14. data/lib/pry-doc/{core_docs_20 → docs/20}/proxy_types +0 -0
  15. data/lib/pry-doc/{core_docs_21 → docs/21}/checksums +0 -0
  16. data/lib/pry-doc/{core_docs_21 → docs/21}/object_types +0 -0
  17. data/lib/pry-doc/{core_docs_21 → docs/21}/objects/root.dat +0 -0
  18. data/lib/pry-doc/{core_docs_21 → docs/21}/proxy_types +0 -0
  19. data/lib/pry-doc/{core_docs_22 → docs/22}/checksums +0 -0
  20. data/lib/pry-doc/{core_docs_22 → docs/22}/object_types +0 -0
  21. data/lib/pry-doc/{core_docs_22 → docs/22}/objects/root.dat +0 -0
  22. data/lib/pry-doc/{core_docs_22 → docs/22}/proxy_types +0 -0
  23. data/lib/pry-doc/{core_docs_23 → docs/23}/checksums +0 -0
  24. data/lib/pry-doc/{core_docs_23 → docs/23}/object_types +0 -0
  25. data/lib/pry-doc/{core_docs_23 → docs/23}/objects/root.dat +0 -0
  26. data/lib/pry-doc/{core_docs_23 → docs/23}/proxy_types +0 -0
  27. data/lib/pry-doc/docs/24/checksums +486 -0
  28. data/lib/pry-doc/docs/24/complete +0 -0
  29. data/lib/pry-doc/docs/24/object_types +0 -0
  30. data/lib/pry-doc/docs/24/objects/root.dat +0 -0
  31. data/lib/pry-doc/docs/24/proxy_types +0 -0
  32. data/lib/pry-doc/pry_ext/method_info.rb +188 -0
  33. data/lib/pry-doc/version.rb +1 -1
  34. data/pry-doc.gemspec +3 -3
  35. data/spec/helper.rb +8 -1
  36. data/spec/pry-doc_spec.rb +36 -39
  37. metadata +34 -28
File without changes
Binary file
Binary file
@@ -0,0 +1,188 @@
1
+ class Pry
2
+ module MethodInfo
3
+ class << self
4
+ ##
5
+ # Retrieve the YARD object that contains the method data.
6
+ #
7
+ # @param [Method, UnboundMethod] meth The method object
8
+ # @return [YARD::CodeObjects::MethodObject] the YARD data for the method
9
+ def info_for(meth)
10
+ cache(meth)
11
+ registry_lookup(meth)
12
+ end
13
+
14
+ ##
15
+ # Retrieves aliases of the given method.
16
+ #
17
+ # @param [Method, UnboundMethod] meth The method object
18
+ # @return [Array<UnboundMethod>] the aliases of the given method if they
19
+ # exist, otherwise an empty array
20
+ def aliases(meth)
21
+ owner = meth.owner
22
+ name = meth.name
23
+
24
+ (owner.instance_methods + owner.private_instance_methods).uniq.map do |m|
25
+ aliased_method = owner.__send__(:instance_method, m)
26
+
27
+ next unless aliased_method == owner.__send__(:instance_method, name)
28
+ next if m == name
29
+ aliased_method
30
+ end.compact!
31
+ end
32
+
33
+ ##
34
+ # FIXME: this is unnecessarily limited to ext/ and lib/ directories.
35
+ #
36
+ # @return [String] the root directory of a given gem directory
37
+ def gem_root(dir)
38
+ return unless (index = dir.rindex(%r(/(?:lib|ext)(?:/|$))))
39
+ dir[0..index-1]
40
+ end
41
+
42
+ private
43
+
44
+ ##
45
+ # Convert a method object into the `Class#method` string notation.
46
+ #
47
+ # @param [Method, UnboundMethod] meth
48
+ # @return [String] the method in string receiver notation
49
+ # @note This mess is needed to support all the modern Rubies. Somebody has
50
+ # to figure out a better way to distinguish between class methods and
51
+ # instance methods.
52
+ def receiver_notation_for(meth)
53
+ match = meth.inspect.match(/\A#<(?:Unbound)?Method: (.+)([#\.].+?)(?:\(.+\))?>\z/)
54
+ owner = meth.owner.to_s.sub(/#<.+?:(.+?)>/, '\1')
55
+ name = match[2]
56
+ name.sub!('#', '.') if match[1] =~ /\A#<Class:/
57
+ owner + name
58
+ end
59
+
60
+ ##
61
+ # Checks whether `meth` is a class method.
62
+ #
63
+ # @param [Method, UnboundMethod] meth The method to check
64
+ # @return [Boolean] true if singleton, otherwise false
65
+ def is_singleton?(meth)
66
+ receiver_notation_for(meth).include?('.')
67
+ end
68
+
69
+ def registry_lookup(meth)
70
+ if (obj = YARD::Registry.at(receiver_notation_for(meth)))
71
+ return obj
72
+ end
73
+
74
+ if (aliases = aliases(meth)).any?
75
+ YARD::Registry.at(receiver_notation_for(aliases.first))
76
+ elsif meth.owner == Kernel
77
+ # YARD thinks that some methods are on Object when
78
+ # they're actually on Kernel; so try again on Object if Kernel fails.
79
+ YARD::Registry.at("Object##{meth.name}")
80
+ end
81
+ end
82
+
83
+ ##
84
+ # Attempts to find the C source files if method belongs to a gem and use
85
+ # YARD to parse and cache the source files for display.
86
+ #
87
+ # @param [Method, UnboundMethod] meth The method object
88
+ def parse_and_cache_if_gem_cext(meth)
89
+ return unless (gem_dir = find_gem_dir(meth))
90
+
91
+ path = "#{gem_dir}/**/*.c"
92
+ return if Dir.glob(path).none?
93
+
94
+ puts "Scanning and caching *.c files..."
95
+ YARD.parse(path)
96
+ end
97
+
98
+ ##
99
+ # @return [Object] the host of the method (receiver or owner)
100
+ def method_host(meth)
101
+ is_singleton?(meth) && Module === meth.receiver ? meth.receiver : meth.owner
102
+ end
103
+
104
+ ##
105
+ # @param [Method, UnboundMethod] meth The method object
106
+ # @return [String, nil] root directory path of gem that method belongs to
107
+ # or nil if could not be found
108
+ def find_gem_dir(meth)
109
+ host = method_host(meth)
110
+
111
+ begin
112
+ host_source_location, _ = WrappedModule.new(host).source_location
113
+ break if host_source_location != nil
114
+ return unless host.name
115
+ host = eval(host.namespace_name)
116
+ end while host
117
+
118
+ # We want to exclude all source_locations that aren't gems (i.e
119
+ # stdlib).
120
+ if host_source_location && host_source_location =~ %r{/gems/}
121
+ gem_root(host_source_location)
122
+ else
123
+ # The WrappedModule approach failed, so try our backup approach.
124
+ gem_dir_from_method(meth)
125
+ end
126
+ end
127
+
128
+ ##
129
+ # Try to guess what the gem name will be based on the name of the module.
130
+ #
131
+ # @param [String] name The name of the module
132
+ # @return [Enumerator] the enumerator which enumerates on possible names
133
+ # we try to guess
134
+ def guess_gem_name(name)
135
+ scanned_name = name.scan(/[A-z]+/).map(&:downcase)
136
+
137
+ Enumerator.new do |y|
138
+ y << name.downcase
139
+ y << scanned_name.join('_')
140
+ y << scanned_name.join('_').sub('_', '-')
141
+ y << scanned_name.join('-')
142
+ y << name
143
+ end
144
+ end
145
+
146
+ ##
147
+ # Try to recover the gem directory of a gem based on a method object.
148
+ #
149
+ # @param [Method, UnboundMethod] meth The method object
150
+ # @return [String, nil] the located gem directory
151
+ def gem_dir_from_method(meth)
152
+ return unless (host = method_host(meth)).name
153
+
154
+ guess_gem_name(host.name.split('::').first).each do |guess|
155
+ matches = $LOAD_PATH.grep(%r(/gems/#{guess}))
156
+ return gem_root(matches.first) if matches.any?
157
+ end
158
+
159
+ nil
160
+ end
161
+
162
+ ##
163
+ # Caches the file that holds the method.
164
+ #
165
+ # Cannot cache C stdlib and eval methods.
166
+ #
167
+ # @param [Method, UnboundMethod] meth The method object.
168
+ def cache(meth)
169
+ file, _ = meth.source_location
170
+
171
+ # Eval methods can't be cached.
172
+ return if file =~ /(\(.*\))|<.*>/
173
+
174
+ # No need to cache already cached methods.
175
+ return if registry_lookup(meth)
176
+
177
+ unless file
178
+ parse_and_cache_if_gem_cext(meth)
179
+ return
180
+ end
181
+
182
+ log.enter_level(Logger::FATAL) do
183
+ YARD.parse(file)
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -1,3 +1,3 @@
1
1
  module PryDoc
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
data/pry-doc.gemspec CHANGED
@@ -20,9 +20,9 @@ DESCR
20
20
  s.require_paths = ["lib"]
21
21
  s.files = `git ls-files`.split("\n")
22
22
 
23
- s.add_dependency 'yard', "~> 0.8"
23
+ s.add_dependency 'yard', "~> 0.9"
24
24
  s.add_dependency 'pry', "~> 0.9"
25
- s.add_development_dependency 'latest_ruby', "~> 0.0"
26
- s.add_development_dependency 'bacon', "~> 1.1"
25
+ s.add_development_dependency 'latest_ruby', '~> 0.0'
26
+ s.add_development_dependency 'rspec', '~> 3.5'
27
27
  s.add_development_dependency 'rake', "~> 10.0"
28
28
  end
data/spec/helper.rb CHANGED
@@ -1,3 +1,11 @@
1
+ require 'pry-doc'
2
+
3
+ RSpec.configure do |c|
4
+ c.order = 'random'
5
+ c.color = true
6
+ c.disable_monkey_patching!
7
+ end
8
+
1
9
  direc = File.dirname(__FILE__)
2
10
 
3
11
  class C
@@ -8,4 +16,3 @@ puts
8
16
  puts "Building Sample Gem with C Extensions for testing.."
9
17
  system("cd #{direc}/gem_with_cext/gems/ext/ && ruby extconf.rb && make")
10
18
  puts
11
-
data/spec/pry-doc_spec.rb CHANGED
@@ -1,11 +1,9 @@
1
1
  direc = File.dirname(__FILE__)
2
2
 
3
- require 'rubygems'
4
3
  require 'pry'
5
4
  require "#{direc}/../lib/pry-doc"
6
5
  require "#{direc}/helper"
7
6
  require "#{direc}/gem_with_cext/gems/sample"
8
- require 'bacon'
9
7
  require 'set'
10
8
  require 'fileutils'
11
9
  require 'readline'
@@ -13,33 +11,33 @@ require 'readline'
13
11
  puts "Testing pry-doc version #{PryDoc::VERSION}..."
14
12
  puts "Ruby version: #{RUBY_VERSION}"
15
13
 
16
- describe PryDoc do
14
+ RSpec.describe PryDoc do
17
15
 
18
16
  describe "core C methods" do
19
17
  it 'should look up core (C) methods' do
20
18
  obj = Pry::MethodInfo.info_for(method(:puts))
21
- obj.source.should.not == nil
19
+ expect(obj.source).not_to be_nil
22
20
  end
23
21
 
24
22
  it 'should look up core (C) instance methods' do
25
- Module.module_eval do
26
- obj = Pry::MethodInfo.info_for(instance_method(:include))
27
- obj.source.should.not == nil
23
+ obj = Module.module_eval do
24
+ Pry::MethodInfo.info_for(instance_method(:include))
28
25
  end
26
+ expect(obj.source).not_to be_nil
29
27
  end
30
28
 
31
29
  it 'should look up core (C) class method (by Method object)' do
32
- Module.module_eval do
33
- obj = Pry::MethodInfo.info_for(Dir.method(:glob))
34
- obj.source.should.not == nil
30
+ obj = Module.module_eval do
31
+ Pry::MethodInfo.info_for(Dir.method(:glob))
35
32
  end
33
+ expect(obj.source).not_to be_nil
36
34
  end
37
35
 
38
36
  it 'should look up core (C) class method (by UnboundMethod object)' do
39
- Module.module_eval do
40
- obj = Pry::MethodInfo.info_for(class << Dir; instance_method(:glob); end)
41
- obj.source.should.not == nil
37
+ obj = Module.module_eval do
38
+ Pry::MethodInfo.info_for(class << Dir; instance_method(:glob); end)
42
39
  end
40
+ expect(obj.source).not_to be_nil
43
41
  end
44
42
  end
45
43
 
@@ -47,26 +45,26 @@ describe PryDoc do
47
45
  it 'should return nil for eval methods' do
48
46
  TOPLEVEL_BINDING.eval("def hello; end")
49
47
  obj = Pry::MethodInfo.info_for(method(:hello))
50
- obj.should == nil
48
+ expect(obj).to be_nil
51
49
  end
52
50
  end
53
51
 
54
52
  describe "pure ruby methods" do
55
53
  it 'should look up ruby methods' do
56
54
  obj = Pry::MethodInfo.info_for(C.new.method(:message))
57
- obj.should.not == nil
55
+ expect(obj).not_to be_nil
58
56
  end
59
57
 
60
58
  it 'should look up ruby instance methods' do
61
59
  obj = Pry::MethodInfo.info_for(C.instance_method(:message))
62
- obj.should.not == nil
60
+ expect(obj).not_to be_nil
63
61
  end
64
62
  end
65
63
 
66
64
  describe "Ruby stdlib methods" do
67
65
  it "should look up ruby stdlib method" do
68
66
  obj = Pry::MethodInfo.info_for(Set.instance_method(:union))
69
- obj.should.not == nil
67
+ expect(obj).not_to be_nil
70
68
  end
71
69
  end
72
70
 
@@ -74,42 +72,42 @@ describe PryDoc do
74
72
 
75
73
  it "should lookup C ext methods" do
76
74
  obj = Pry::MethodInfo.info_for(Sample.instance_method(:gleezor))
77
- obj.should.not == nil
75
+ expect(obj).not_to be_nil
78
76
  end
79
77
 
80
78
  it "should lookup aliased C ext methods" do
81
79
  obj = Pry::MethodInfo.info_for(Sample.instance_method(:remove))
82
- obj.should.not == nil
80
+ expect(obj).not_to be_nil
83
81
  end
84
82
 
85
83
  it "should lookup C ext instance methods even when its owners don't have any ruby methods" do
86
84
  obj = Pry::MethodInfo.info_for(Sample::A::B.instance_method(:gleezor))
87
- obj.should.not == nil
85
+ expect(obj).not_to be_nil
88
86
  end
89
87
 
90
88
  it "should lookup C ext class methods even when its owners don't have any ruby methods" do
91
89
  obj = Pry::MethodInfo.info_for(Sample::A::B.method(:gleezor))
92
- obj.should.not == nil
90
+ expect(obj).not_to be_nil
93
91
  end
94
92
  end
95
93
 
96
94
  describe "C stdlib methods" do
97
95
  it "finds them" do
98
96
  obj = Pry::MethodInfo.info_for(Readline.method(:readline))
99
- obj.should.not == nil
97
+ expect(obj).not_to be_nil
100
98
  end
101
99
 
102
100
  it "finds well hidden docs like BigDecimal docs" do
103
101
  require 'bigdecimal'
104
102
  obj = Pry::MethodInfo.info_for(BigDecimal.instance_method(:finite?))
105
- obj.should.not == nil
103
+ expect(obj).not_to be_nil
106
104
  end
107
105
  end
108
106
 
109
107
  describe ".aliases" do
110
108
  it "should return empty array if method does not have any alias" do
111
109
  aliases = Pry::MethodInfo.aliases(Sample.instance_method(:some_meth))
112
- aliases.should == []
110
+ expect(aliases).to be_empty
113
111
  end
114
112
 
115
113
  it "should return aliases of a (C) method" do
@@ -117,10 +115,10 @@ describe PryDoc do
117
115
  copy = Sample.instance_method(:remove)
118
116
 
119
117
  aliases = Pry::MethodInfo.aliases(orig)
120
- aliases.should == [copy]
118
+ expect(aliases).to eq([copy])
121
119
 
122
120
  aliases = Pry::MethodInfo.aliases(copy)
123
- aliases.should == [orig]
121
+ expect(aliases).to eq([orig])
124
122
  end
125
123
 
126
124
  it "should return aliases of a ruby method" do
@@ -130,10 +128,10 @@ describe PryDoc do
130
128
  copy = C.instance_method(:msg)
131
129
 
132
130
  aliases = Pry::MethodInfo.aliases(orig)
133
- aliases.should == [copy]
131
+ expect(aliases).to eq([copy])
134
132
 
135
133
  aliases = Pry::MethodInfo.aliases(copy)
136
- aliases.should == [orig]
134
+ expect(aliases).to eq([orig])
137
135
  end
138
136
 
139
137
  it "should return aliases of protected method" do
@@ -141,7 +139,7 @@ describe PryDoc do
141
139
  copy = Sample.instance_method(:remove_1)
142
140
 
143
141
  aliases = Pry::MethodInfo.aliases(orig)
144
- aliases.should == [copy]
142
+ expect(aliases).to eq([copy])
145
143
  end
146
144
 
147
145
  it "should return aliases of private method" do
@@ -149,7 +147,7 @@ describe PryDoc do
149
147
  copy = Sample.instance_method(:remove_2)
150
148
 
151
149
  aliases = Pry::MethodInfo.aliases(orig)
152
- aliases.should == [copy]
150
+ expect(aliases).to eq([copy])
153
151
  end
154
152
 
155
153
  it 'does not error when given a singleton method' do
@@ -157,16 +155,15 @@ describe PryDoc do
157
155
  def self.my_method; end
158
156
  end
159
157
 
160
- lambda { Pry::MethodInfo.aliases(c.method(:my_method)) }.should.not.raise NameError
158
+ expect { Pry::MethodInfo.aliases(c.method(:my_method)) }.not_to raise_error
161
159
  end
162
160
  end
163
161
 
164
162
  describe ".gem_root" do
165
163
  it "should return the path to the gem" do
166
164
  path = Pry::WrappedModule.new(Sample).source_location[0]
167
-
168
- Pry::MethodInfo.gem_root(path).should ==
169
- File.expand_path("gem_with_cext/gems", direc)
165
+ expect(Pry::MethodInfo.gem_root(path)).
166
+ to eq(File.expand_path("gem_with_cext/gems", direc))
170
167
  end
171
168
 
172
169
  it "should not be fooled by a parent 'lib' or 'ext' dir" do
@@ -174,16 +171,16 @@ describe PryDoc do
174
171
  "1.9.1/gems/activesupport-4.0.2/lib/active_support/"\
175
172
  "core_ext/kernel/reporting.rb"
176
173
 
177
- Pry::MethodInfo.gem_root(path).should ==
178
- "/foo/.rbenv/versions/1.9.3-p429/lib/ruby/"\
179
- "gems/1.9.1/gems/activesupport-4.0.2"
174
+ expect(Pry::MethodInfo.gem_root(path))
175
+ .to eq('/foo/.rbenv/versions/1.9.3-p429/lib/ruby/' \
176
+ 'gems/1.9.1/gems/activesupport-4.0.2')
180
177
  end
181
178
  end
182
179
 
183
180
  describe "1.9 and higher specific docs" do
184
181
  it "finds Kernel#require_relative" do
185
182
  obj = Pry::MethodInfo.info_for(Kernel.instance_method(:require_relative))
186
- obj.should.not == nil
183
+ expect(obj).not_to be_nil
187
184
  end
188
185
  end
189
186
 
@@ -192,7 +189,7 @@ describe PryDoc do
192
189
  describe "2.0 specific docs" do
193
190
  it "finds Module#refine" do
194
191
  obj = Pry::MethodInfo.info_for(Module.instance_method(:refine))
195
- obj.should.not == nil
192
+ expect(obj).not_to be_nil
196
193
  end
197
194
  end
198
195
  end