desc_method 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README +101 -0
- data/lib/desc_method.rb +3 -0
- data/lib/method_describer/class_desc.rb +56 -0
- data/lib/method_describer/kernel_new_methods_list.rb +20 -0
- data/lib/method_describer/method_desc.rb +186 -0
- metadata +119 -0
data/README
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
A "run time RI for ruby methods", this gem allows you to query for information about objects' methods at runtime--for example within irb or ruby-debug. It reveals everything known about the method. This includes source, ri, arity, rdoc comments (1.9 only), etc.
|
2
|
+
|
3
|
+
For me it has proved quite useful, and I wouldn't do a ruby-debug session without it--you might really like it.
|
4
|
+
|
5
|
+
Examples:
|
6
|
+
|
7
|
+
>> class A;
|
8
|
+
def go(a); end;
|
9
|
+
end
|
10
|
+
|
11
|
+
>> A.desc_method :go
|
12
|
+
#<UnboundMethod: A#go> arity: 1
|
13
|
+
ri for A#go
|
14
|
+
Nothing known about A
|
15
|
+
(end ri)
|
16
|
+
def go(a)
|
17
|
+
# do nothing
|
18
|
+
end
|
19
|
+
Parameters: go(a)
|
20
|
+
|
21
|
+
>> File.desc_method :delete
|
22
|
+
ri for File.delete
|
23
|
+
----------------------------------------------------------- File::delete
|
24
|
+
File.delete(file_name, ...) => integer
|
25
|
+
File.unlink(file_name, ...) => integer
|
26
|
+
|
27
|
+
From Ruby 1.9.1
|
28
|
+
------------------------------------------------------------------------
|
29
|
+
Deletes the named files, returning the number of names passed as
|
30
|
+
arguments. Raises an exception on any error. See also +Dir::rmdir+.
|
31
|
+
|
32
|
+
(end ri)
|
33
|
+
#<Method: File.delete> arity: -1
|
34
|
+
appears to be a c method
|
35
|
+
#parameters signature: delete( [[:rest]] )
|
36
|
+
|
37
|
+
Or (my favorite) a debug session:
|
38
|
+
|
39
|
+
(rdb:1) l=
|
40
|
+
...
|
41
|
+
74 assert(assigns['order'].order_line_items.map(:unit_price).min >= -5)
|
42
|
+
...
|
43
|
+
(rdb:1) desc_method :assert
|
44
|
+
#<Method: StoreControllerTest(Test::Unit::Assertions)#assert> arity: -2
|
45
|
+
ri for Test::Unit::Assertions#assert
|
46
|
+
------------------------------------------ Test::Unit::Assertions#assert
|
47
|
+
assert(boolean, message=nil)
|
48
|
+
|
49
|
+
From gem test-unit-2.0.1
|
50
|
+
------------------------------------------------------------------------
|
51
|
+
Asserts that +boolean+ is not false or nil.
|
52
|
+
|
53
|
+
Example:
|
54
|
+
|
55
|
+
assert [1, 2].include?(5)
|
56
|
+
|
57
|
+
(end ri)
|
58
|
+
def assert(boolean, message = nil)
|
59
|
+
_wrap_assertion do
|
60
|
+
assert_block("assert should not be called with a block.") do
|
61
|
+
(not block_given?)
|
62
|
+
end
|
63
|
+
assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
Parameters: assert(boolean, message = nil)
|
67
|
+
|
68
|
+
Thus, you can look at methods' source/rdocs without having to step into them. Deftly convenient.
|
69
|
+
|
70
|
+
========= How to Install=====
|
71
|
+
$ gem install rogerdpack-desc_method
|
72
|
+
or
|
73
|
+
$ gem install rogerdpack-desc_method --source http://gems.github.com
|
74
|
+
if you don't have gems.github.com as a gem source.
|
75
|
+
|
76
|
+
Usage:
|
77
|
+
>> require 'desc_method'
|
78
|
+
>> Class.desc_method :method_name # class or instance method name
|
79
|
+
...
|
80
|
+
>> some_instance.desc_method :method_name
|
81
|
+
|
82
|
+
Other goodies included:
|
83
|
+
|
84
|
+
Class#desc_class method
|
85
|
+
|
86
|
+
An example:
|
87
|
+
>> Object.desc_class
|
88
|
+
# outputs descriptive info about that class--RI, method list, etc.
|
89
|
+
>> Object.desc_class :verbose => true
|
90
|
+
# outputs RI, method lists including inherited methods, ancestors, all constants etc.
|
91
|
+
|
92
|
+
Kernel#methods has been monkey patched to output a "separator" between its inherited and non inherited methods--i.e.
|
93
|
+
>> instance.methods
|
94
|
+
=> [:first, :second, :after_this_are_inherited>>>>>, :some_inherited_method, :another_inherited_method] # adds in that separator
|
95
|
+
|
96
|
+
I'll probably remove that in a future release since it turns out you can just run Kernel#methods(false) for about much.
|
97
|
+
|
98
|
+
=== Thanks ===
|
99
|
+
This gem wraps for convenience the functionality of Method#source_location, ruby2ruby, et al, and also contains some code inspiration from manvenu, SourceRef (MBARI), and Python's Method#desc. It also wouldn't be useful without irb and the ruby-debug folks. Thanks!
|
100
|
+
|
101
|
+
Comments/suggestions welcome rogerdpack on gmail or github
|
data/lib/desc_method.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require_rel 'method_desc'
|
2
|
+
class Class
|
3
|
+
# just runs ri against the class, outputs a big
|
4
|
+
def desc_class options = {}
|
5
|
+
# want_output = false, verbose = false
|
6
|
+
begin
|
7
|
+
puts "begin RI"
|
8
|
+
RDoc::RI::Driver.run [to_s, '--no-pager']
|
9
|
+
puts 'end ri'
|
10
|
+
rescue SystemExit
|
11
|
+
# not found
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods = methods(false)
|
15
|
+
for ancestor in ancestors[1..-1] # skip the first one, which is yourself
|
16
|
+
class_methods -= ancestor.methods(false)
|
17
|
+
end
|
18
|
+
|
19
|
+
doc = []
|
20
|
+
doc << to_s
|
21
|
+
doc += ["non inherited methods:", instance_methods(false).sort.join(", ")]
|
22
|
+
doc += ['non inherited class methods:', class_methods.sort.join(', ')]
|
23
|
+
doc += ["ancestors:", ancestors.join(', ')] if options[:verbose]
|
24
|
+
doc += ["Constants (possible sub classes):", constants.join(', ')] if constants.length > 0 && options[:verbose]
|
25
|
+
puts doc
|
26
|
+
doc if options[:want_output]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
=begin
|
32
|
+
doctest:
|
33
|
+
|
34
|
+
it should return true if you run it against a "real" class
|
35
|
+
>> String.desc_class(:want_output => true).length > 1
|
36
|
+
=> true
|
37
|
+
>> class A; end
|
38
|
+
>> A.desc_class(:want_output => true).length > 1
|
39
|
+
=> true
|
40
|
+
|
41
|
+
it shouldn't report itself as an ancestor of itself
|
42
|
+
>> A.desc_class(:want_output => true).grep(/ancestors/).include? '[A]'
|
43
|
+
=> false
|
44
|
+
|
45
|
+
also lists constants
|
46
|
+
>> A.desc_class(:want_output => true, :verbose => true).grep(/constants/i)
|
47
|
+
=> [] # should be none since we didn't add any constants to A
|
48
|
+
>> class A; B = 3; end
|
49
|
+
>> A.desc_class(:want_output => true, :verbose => true).grep(/constants/i).length
|
50
|
+
=> 1 # should be none since we didn't add any constants to A
|
51
|
+
|
52
|
+
should work with sub methods
|
53
|
+
>> String.desc_method(:strip)
|
54
|
+
|
55
|
+
doctest_require: '../method_desc'
|
56
|
+
=end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Supplement [monkey patch] Kernel#methods so that it returns lists that are split into two kind of [adds a marker where the inherited methods begin].
|
3
|
+
=end
|
4
|
+
module Kernel
|
5
|
+
alias :methods_old :methods
|
6
|
+
def methods all = true
|
7
|
+
if all
|
8
|
+
# give some marker designating when the inherited methods start
|
9
|
+
(public_methods(false) << :"inherited methods after this point >>") + (public_methods(true) - public_methods(false))
|
10
|
+
else
|
11
|
+
public_methods(false)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
if $0 == __FILE__
|
17
|
+
class A; end
|
18
|
+
puts 'A.methods', A.methods(true).inspect, A.methods(false).inspect
|
19
|
+
puts 'A.new.methods', A.new.methods.inspect
|
20
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# originally gleaned from http://p.ramaze.net/17901
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rdoc'
|
4
|
+
require 'rdoc/ri/driver'
|
5
|
+
require 'sane'
|
6
|
+
begin
|
7
|
+
gem 'arguments' # TODO why is this necessary?
|
8
|
+
require 'arguments' # rogerdpack-arguments
|
9
|
+
rescue LoadError
|
10
|
+
require 'arguments' # 1.9
|
11
|
+
end
|
12
|
+
require 'ruby2ruby'
|
13
|
+
|
14
|
+
module SourceLocationDesc
|
15
|
+
|
16
|
+
# add a Method#desc which spits out all it knows about that method
|
17
|
+
# ri, location, local ri, etc.
|
18
|
+
# TODO does this work with class methods?
|
19
|
+
def desc want_just_summary = false, want_the_description_returned = false
|
20
|
+
doc = []
|
21
|
+
#_dbg
|
22
|
+
# to_s is something like "#<Method: String#strip>"
|
23
|
+
# or #<Method: GiftCertsControllerTest(Test::Unit::TestCase)#get>
|
24
|
+
# or "#<Method: A.go>"
|
25
|
+
# or "#<Method: Order(id: integer, order_number: integer).get_cc_processor>"
|
26
|
+
# or "#<Method: Order(id: integer, order_number: integer)(ActiveRecord::Base).get_cc_processor>"
|
27
|
+
|
28
|
+
string = to_s
|
29
|
+
|
30
|
+
# derive class_name
|
31
|
+
parenthese_count = string.count '('
|
32
|
+
|
33
|
+
if parenthese_count== 1
|
34
|
+
# case #<Method: GiftCertsControllerTest(Test::Unit::TestCase)#get>
|
35
|
+
# case #<Method: Order(id: integer, order_number: integer).get_cc_processor>
|
36
|
+
if string.include? "id: " # TODO huh?
|
37
|
+
string =~ /Method: (.+)\(/
|
38
|
+
else
|
39
|
+
string =~ /\(([^\(]+)\)[\.#]/ # extract out what is between last parentheses
|
40
|
+
end
|
41
|
+
class_name = $1
|
42
|
+
elsif parenthese_count == 0
|
43
|
+
# case "#<Method: A.go>"
|
44
|
+
string =~ /Method: ([^#\.]+)/
|
45
|
+
class_name = $1
|
46
|
+
elsif parenthese_count == 2
|
47
|
+
# case "#<Method: Order(id: integer, order_number: integer)(ActiveRecord::Base).get_cc_processor>"
|
48
|
+
string =~ /\(([^\(]+)\)[\.#]/
|
49
|
+
class_name = $1
|
50
|
+
else
|
51
|
+
raise 'bad ' + string
|
52
|
+
end
|
53
|
+
|
54
|
+
# now get method name, type
|
55
|
+
string =~ /Method: .*([#\.])(.*)>/ # include the # or .
|
56
|
+
joiner = $1
|
57
|
+
method_name = $2
|
58
|
+
full_name = "#{class_name}#{joiner}#{method_name}"
|
59
|
+
puts "sig: #{to_s} arity: #{arity}"
|
60
|
+
# TODO add to doc, I want it before ri for now though, and only once, so not there yet :)
|
61
|
+
|
62
|
+
# now run default RI for it
|
63
|
+
begin
|
64
|
+
puts 'searching ri for ' + full_name + "..."
|
65
|
+
RDoc::RI::Driver.run [full_name, '--no-pager'] unless want_just_summary
|
66
|
+
rescue *[StandardError, SystemExit]
|
67
|
+
# not found
|
68
|
+
end
|
69
|
+
puts '(end ri)'
|
70
|
+
|
71
|
+
# now gather up any other information we now about it, in case there are no rdocs
|
72
|
+
|
73
|
+
if !(respond_to? :source_location)
|
74
|
+
# pull out names for 1.8
|
75
|
+
begin
|
76
|
+
klass = eval(class_name)
|
77
|
+
# we don't call to_ruby to overcome ruby2ruby bug http://rubyforge.org/tracker/index.php?func=detail&aid=26891&group_id=1513&atid=5921
|
78
|
+
if joiner == '#'
|
79
|
+
doc << RubyToRuby.new.process(ParseTree.translate(klass, method_name))
|
80
|
+
else
|
81
|
+
doc << RubyToRuby.new.process(ParseTree.translate(klass.singleton_class, method_name))
|
82
|
+
end
|
83
|
+
args = Arguments.names( klass, method_name) rescue Arguments.names(klass.singleton_class, method_name)
|
84
|
+
out = []
|
85
|
+
args.each{|arg_pair|
|
86
|
+
out << arg_pair.join(' = ')
|
87
|
+
} if args
|
88
|
+
out = out.join(', ')
|
89
|
+
return out if want_just_summary
|
90
|
+
|
91
|
+
param_string = "Parameters: #{method_name}(" + out + ")"
|
92
|
+
doc << param_string unless want_the_description_returned
|
93
|
+
rescue Exception => e
|
94
|
+
|
95
|
+
puts "fail to parse tree: #{class_name} #{e} #{e.backtrace}" if $VERBOSE
|
96
|
+
end
|
97
|
+
else
|
98
|
+
# 1.9.x
|
99
|
+
file, line = source_location
|
100
|
+
if file
|
101
|
+
# then it's a pure ruby method
|
102
|
+
doc << "at #{file}:#{line}"
|
103
|
+
all_lines = File.readlines(file)
|
104
|
+
head_and_sig = all_lines[0...line]
|
105
|
+
sig = head_and_sig[-1]
|
106
|
+
head = head_and_sig[0..-2]
|
107
|
+
|
108
|
+
doc << sig
|
109
|
+
head.reverse_each do |line|
|
110
|
+
break unless line =~ /^\s*#(.*)/
|
111
|
+
doc.unshift " " + $1.strip
|
112
|
+
end
|
113
|
+
|
114
|
+
# now the real code will end with 'end' same whitespace as the first
|
115
|
+
sig_white_space = sig.scan(/\W+/)[0]
|
116
|
+
body = all_lines[line..-1]
|
117
|
+
body.each{|line|
|
118
|
+
doc << line
|
119
|
+
if line.start_with?(sig_white_space + "end")
|
120
|
+
break
|
121
|
+
end
|
122
|
+
}
|
123
|
+
# how do I get the rest now?
|
124
|
+
return sig + "\n" + head[0] if want_just_summary
|
125
|
+
else
|
126
|
+
doc << 'appears to be a c method'
|
127
|
+
end
|
128
|
+
param_string = to_s
|
129
|
+
if respond_to? :parameters
|
130
|
+
doc << "Original code signature: %s" % sig.to_s.strip if sig
|
131
|
+
doc << "#parameters signature: %s( %p )" % [name, parameters]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
puts doc # always output it since RI does currently [todo make optional I suppose, and non out-putty]
|
136
|
+
|
137
|
+
if want_the_description_returned # give them something they can examine
|
138
|
+
doc
|
139
|
+
else
|
140
|
+
param_string
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
named_args_for :desc # just for fun, tests use it too, plus it should actually wurk without interfering...I think
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
class Method; include SourceLocationDesc; end
|
149
|
+
class UnboundMethod; include SourceLocationDesc; end
|
150
|
+
|
151
|
+
# TODO mixin from a separate module
|
152
|
+
class Object
|
153
|
+
# currently rather verbose, but will attempt to describe all it knows about a method
|
154
|
+
def desc_method name, options = {}
|
155
|
+
if self.is_a? Class
|
156
|
+
# i.e. String.strip
|
157
|
+
instance_method(name).desc(options) rescue method(name).desc(options) # rescue allows for Class.instance_method_name
|
158
|
+
else
|
159
|
+
method(name).desc(options)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
=begin
|
166
|
+
doctest:
|
167
|
+
>> require 'pathname'
|
168
|
+
it should display the name
|
169
|
+
>> Pathname.instance_method(:children).desc(:want_the_description_returned => true).grep(/children/).size > 0
|
170
|
+
=> true # ["#<UnboundMethod: Pathname#children>"]
|
171
|
+
|
172
|
+
and arity
|
173
|
+
>> Pathname.instance_method(:children).desc(:want_the_description_returned => true).grep(/arity/)
|
174
|
+
=> ["#<UnboundMethod: Pathname#children> arity: -1"]
|
175
|
+
|
176
|
+
# todo: one that is guaranteed to exit you early [no docs at all ever]
|
177
|
+
|
178
|
+
wurx with class methods
|
179
|
+
>> class A; def self.go(a = 3); a=5; end; end
|
180
|
+
>> class A; def go2(a=4) a =7; end; end
|
181
|
+
>> A.desc_method(:go)
|
182
|
+
>> A.desc_method(:go2)
|
183
|
+
|
184
|
+
>> File.desc_method :delete
|
185
|
+
|
186
|
+
=end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: desc_method
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roger Pack
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-29 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rdoc
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "2.3"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: require_all
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "1.1"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rogerdpack-arguments
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: rogerdpack-sane
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.1.2
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: ParseTree
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: ruby2ruby
|
67
|
+
type: :runtime
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
description: ruby method describer to make it possible to inspect methods [rdoc, signature, etc.] at runtime, for example while debugging.
|
76
|
+
email:
|
77
|
+
- rogerdpack@gmail.comm
|
78
|
+
executables: []
|
79
|
+
|
80
|
+
extensions: []
|
81
|
+
|
82
|
+
extra_rdoc_files: []
|
83
|
+
|
84
|
+
files:
|
85
|
+
- README
|
86
|
+
- lib/desc_method.rb
|
87
|
+
- lib/method_describer/class_desc.rb
|
88
|
+
- lib/method_describer/kernel_new_methods_list.rb
|
89
|
+
- lib/method_describer/method_desc.rb
|
90
|
+
has_rdoc: true
|
91
|
+
homepage: http://github.com/rogerdpack/method_describer
|
92
|
+
licenses: []
|
93
|
+
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
version:
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.3.5
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: ruby method describer to make it possible to inspect methods [rdoc, signature, etc.] at runtime, for example while debugging.
|
118
|
+
test_files: []
|
119
|
+
|