method_source 0.3.4 → 0.3.5
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/README.markdown +63 -63
- data/Rakefile +59 -59
- data/lib/method_source.rb +174 -150
- data/lib/method_source/source_location.rb +60 -60
- data/lib/method_source/version.rb +3 -3
- data/test/test.rb +100 -100
- data/test/test_helper.rb +41 -41
- metadata +60 -36
data/README.markdown
CHANGED
@@ -1,63 +1,63 @@
|
|
1
|
-
method_source
|
2
|
-
=============
|
3
|
-
|
4
|
-
(C) John Mair (banisterfiend) 2011
|
5
|
-
|
6
|
-
_retrieve the sourcecode for a method_
|
7
|
-
|
8
|
-
*NOTE:* This simply utilizes `Method#source_location`; it
|
9
|
-
does not access the live AST.
|
10
|
-
|
11
|
-
`method_source` is a utility to return a method's sourcecode as a
|
12
|
-
Ruby string. Also returns `Proc` and `Lambda` sourcecode.
|
13
|
-
|
14
|
-
Method comments can also be extracted using the `comment` method.
|
15
|
-
|
16
|
-
It is written in pure Ruby (no C).
|
17
|
-
|
18
|
-
* Some Ruby 1.8 support now available.
|
19
|
-
|
20
|
-
`method_source` provides the `source` and `comment` methods to the `Method` and
|
21
|
-
`UnboundMethod` and `Proc` classes.
|
22
|
-
|
23
|
-
* Install the [gem](https://rubygems.org/gems/method_source): `gem install method_source`
|
24
|
-
* Read the [documentation](http://rdoc.info/github/banister/method_source/master/file/README.markdown)
|
25
|
-
* See the [source code](http://github.com/banister/method_source)
|
26
|
-
|
27
|
-
Example: display method source
|
28
|
-
------------------------------
|
29
|
-
|
30
|
-
Set.instance_method(:merge).source.display
|
31
|
-
# =>
|
32
|
-
def merge(enum)
|
33
|
-
if enum.instance_of?(self.class)
|
34
|
-
@hash.update(enum.instance_variable_get(:@hash))
|
35
|
-
else
|
36
|
-
do_with_enum(enum) { |o| add(o) }
|
37
|
-
end
|
38
|
-
|
39
|
-
self
|
40
|
-
end
|
41
|
-
|
42
|
-
Example: display method comments
|
43
|
-
--------------------------------
|
44
|
-
|
45
|
-
Set.instance_method(:merge).comment.display
|
46
|
-
# =>
|
47
|
-
# Merges the elements of the given enumerable object to the set and
|
48
|
-
# returns self.
|
49
|
-
|
50
|
-
Limitations:
|
51
|
-
------------
|
52
|
-
|
53
|
-
* Proc#source not available in Ruby 1.8
|
54
|
-
* Occasional strange behaviour in Ruby 1.8
|
55
|
-
* Cannot return source for C methods.
|
56
|
-
* Cannot return source for dynamically defined methods.
|
57
|
-
|
58
|
-
Special Thanks
|
59
|
-
--------------
|
60
|
-
|
61
|
-
[Adam Sanderson](https://github.com/adamsanderson) for `comment` functionality.
|
62
|
-
|
63
|
-
[Dmitry Elastic](https://github.com/dmitryelastic) for the brilliant Ruby 1.8 `source_location` hack.
|
1
|
+
method_source
|
2
|
+
=============
|
3
|
+
|
4
|
+
(C) John Mair (banisterfiend) 2011
|
5
|
+
|
6
|
+
_retrieve the sourcecode for a method_
|
7
|
+
|
8
|
+
*NOTE:* This simply utilizes `Method#source_location`; it
|
9
|
+
does not access the live AST.
|
10
|
+
|
11
|
+
`method_source` is a utility to return a method's sourcecode as a
|
12
|
+
Ruby string. Also returns `Proc` and `Lambda` sourcecode.
|
13
|
+
|
14
|
+
Method comments can also be extracted using the `comment` method.
|
15
|
+
|
16
|
+
It is written in pure Ruby (no C).
|
17
|
+
|
18
|
+
* Some Ruby 1.8 support now available.
|
19
|
+
|
20
|
+
`method_source` provides the `source` and `comment` methods to the `Method` and
|
21
|
+
`UnboundMethod` and `Proc` classes.
|
22
|
+
|
23
|
+
* Install the [gem](https://rubygems.org/gems/method_source): `gem install method_source`
|
24
|
+
* Read the [documentation](http://rdoc.info/github/banister/method_source/master/file/README.markdown)
|
25
|
+
* See the [source code](http://github.com/banister/method_source)
|
26
|
+
|
27
|
+
Example: display method source
|
28
|
+
------------------------------
|
29
|
+
|
30
|
+
Set.instance_method(:merge).source.display
|
31
|
+
# =>
|
32
|
+
def merge(enum)
|
33
|
+
if enum.instance_of?(self.class)
|
34
|
+
@hash.update(enum.instance_variable_get(:@hash))
|
35
|
+
else
|
36
|
+
do_with_enum(enum) { |o| add(o) }
|
37
|
+
end
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
Example: display method comments
|
43
|
+
--------------------------------
|
44
|
+
|
45
|
+
Set.instance_method(:merge).comment.display
|
46
|
+
# =>
|
47
|
+
# Merges the elements of the given enumerable object to the set and
|
48
|
+
# returns self.
|
49
|
+
|
50
|
+
Limitations:
|
51
|
+
------------
|
52
|
+
|
53
|
+
* Proc#source not available in Ruby 1.8
|
54
|
+
* Occasional strange behaviour in Ruby 1.8
|
55
|
+
* Cannot return source for C methods.
|
56
|
+
* Cannot return source for dynamically defined methods.
|
57
|
+
|
58
|
+
Special Thanks
|
59
|
+
--------------
|
60
|
+
|
61
|
+
[Adam Sanderson](https://github.com/adamsanderson) for `comment` functionality.
|
62
|
+
|
63
|
+
[Dmitry Elastic](https://github.com/dmitryelastic) for the brilliant Ruby 1.8 `source_location` hack.
|
data/Rakefile
CHANGED
@@ -1,59 +1,59 @@
|
|
1
|
-
dlext = Config::CONFIG['DLEXT']
|
2
|
-
direc = File.dirname(__FILE__)
|
3
|
-
|
4
|
-
require 'rake/clean'
|
5
|
-
require 'rake/gempackagetask'
|
6
|
-
require "#{direc}/lib/method_source/version"
|
7
|
-
|
8
|
-
CLOBBER.include("**/*.#{dlext}", "**/*~", "**/*#*", "**/*.log", "**/*.o")
|
9
|
-
CLEAN.include("ext/**/*.#{dlext}", "ext/**/*.log", "ext/**/*.o",
|
10
|
-
"ext/**/*~", "ext/**/*#*", "ext/**/*.obj",
|
11
|
-
"ext/**/*.def", "ext/**/*.pdb", "**/*_flymake*.*", "**/*_flymake")
|
12
|
-
|
13
|
-
def apply_spec_defaults(s)
|
14
|
-
s.name = "method_source"
|
15
|
-
s.summary = "retrieve the sourcecode for a method"
|
16
|
-
s.version = MethodSource::VERSION
|
17
|
-
s.date = Time.now.strftime '%Y-%m-%d'
|
18
|
-
s.author = "John Mair (banisterfiend)"
|
19
|
-
s.email = 'jrmair@gmail.com'
|
20
|
-
s.description = s.summary
|
21
|
-
s.require_path = 'lib'
|
22
|
-
s.add_dependency("ruby_parser",">=2.0.5")
|
23
|
-
s.add_development_dependency("bacon",">=1.1.0")
|
24
|
-
s.homepage = "http://banisterfiend.wordpress.com"
|
25
|
-
s.has_rdoc = 'yard'
|
26
|
-
s.files = Dir["ext/**/extconf.rb", "ext/**/*.h", "ext/**/*.c", "lib/**/*.rb",
|
27
|
-
"test/*.rb", "CHANGELOG", "README.markdown", "Rakefile", ".gemtest"]
|
28
|
-
end
|
29
|
-
|
30
|
-
task :test do
|
31
|
-
sh "bacon -k #{direc}/test/test.rb"
|
32
|
-
end
|
33
|
-
|
34
|
-
namespace :ruby do
|
35
|
-
spec = Gem::Specification.new do |s|
|
36
|
-
apply_spec_defaults(s)
|
37
|
-
s.platform = Gem::Platform::RUBY
|
38
|
-
end
|
39
|
-
|
40
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
41
|
-
pkg.need_zip = false
|
42
|
-
pkg.need_tar = false
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
desc "build all platform gems at once"
|
47
|
-
task :gems => [:rmgems, "ruby:gem"]
|
48
|
-
|
49
|
-
desc "remove all platform gems"
|
50
|
-
task :rmgems => ["ruby:clobber_package"]
|
51
|
-
|
52
|
-
desc "build and push latest gems"
|
53
|
-
task :pushgems => :gems do
|
54
|
-
chdir("#{direc}/pkg") do
|
55
|
-
Dir["*.gem"].each do |gemfile|
|
56
|
-
sh "gem push #{gemfile}"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
1
|
+
dlext = Config::CONFIG['DLEXT']
|
2
|
+
direc = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rake/clean'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require "#{direc}/lib/method_source/version"
|
7
|
+
|
8
|
+
CLOBBER.include("**/*.#{dlext}", "**/*~", "**/*#*", "**/*.log", "**/*.o")
|
9
|
+
CLEAN.include("ext/**/*.#{dlext}", "ext/**/*.log", "ext/**/*.o",
|
10
|
+
"ext/**/*~", "ext/**/*#*", "ext/**/*.obj",
|
11
|
+
"ext/**/*.def", "ext/**/*.pdb", "**/*_flymake*.*", "**/*_flymake")
|
12
|
+
|
13
|
+
def apply_spec_defaults(s)
|
14
|
+
s.name = "method_source"
|
15
|
+
s.summary = "retrieve the sourcecode for a method"
|
16
|
+
s.version = MethodSource::VERSION
|
17
|
+
s.date = Time.now.strftime '%Y-%m-%d'
|
18
|
+
s.author = "John Mair (banisterfiend)"
|
19
|
+
s.email = 'jrmair@gmail.com'
|
20
|
+
s.description = s.summary
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.add_dependency("ruby_parser",">=2.0.5")
|
23
|
+
s.add_development_dependency("bacon",">=1.1.0")
|
24
|
+
s.homepage = "http://banisterfiend.wordpress.com"
|
25
|
+
s.has_rdoc = 'yard'
|
26
|
+
s.files = Dir["ext/**/extconf.rb", "ext/**/*.h", "ext/**/*.c", "lib/**/*.rb",
|
27
|
+
"test/*.rb", "CHANGELOG", "README.markdown", "Rakefile", ".gemtest"]
|
28
|
+
end
|
29
|
+
|
30
|
+
task :test do
|
31
|
+
sh "bacon -k #{direc}/test/test.rb"
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace :ruby do
|
35
|
+
spec = Gem::Specification.new do |s|
|
36
|
+
apply_spec_defaults(s)
|
37
|
+
s.platform = Gem::Platform::RUBY
|
38
|
+
end
|
39
|
+
|
40
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
41
|
+
pkg.need_zip = false
|
42
|
+
pkg.need_tar = false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "build all platform gems at once"
|
47
|
+
task :gems => [:rmgems, "ruby:gem"]
|
48
|
+
|
49
|
+
desc "remove all platform gems"
|
50
|
+
task :rmgems => ["ruby:clobber_package"]
|
51
|
+
|
52
|
+
desc "build and push latest gems"
|
53
|
+
task :pushgems => :gems do
|
54
|
+
chdir("#{direc}/pkg") do
|
55
|
+
Dir["*.gem"].each do |gemfile|
|
56
|
+
sh "gem push #{gemfile}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/method_source.rb
CHANGED
@@ -1,150 +1,174 @@
|
|
1
|
-
# (C) John Mair (banisterfiend) 2010
|
2
|
-
# MIT License
|
3
|
-
|
4
|
-
direc = File.dirname(__FILE__)
|
5
|
-
|
6
|
-
require "#{direc}/method_source/version"
|
7
|
-
require "#{direc}/method_source/source_location"
|
8
|
-
|
9
|
-
module MethodSource
|
10
|
-
|
11
|
-
if RUBY_VERSION =~ /1.9/
|
12
|
-
require 'ripper'
|
13
|
-
|
14
|
-
# Determine if a string of code is a valid Ruby expression.
|
15
|
-
# Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
|
16
|
-
# @param [String] code The code to validate.
|
17
|
-
# @return [Boolean] Whether or not the code is a valid Ruby expression.
|
18
|
-
# @example
|
19
|
-
# valid_expression?("class Hello") #=> false
|
20
|
-
# valid_expression?("class Hello; end") #=> true
|
21
|
-
def self.valid_expression?(code)
|
22
|
-
!!Ripper::SexpBuilder.new(code).parse
|
23
|
-
end
|
24
|
-
|
25
|
-
else
|
26
|
-
require 'ruby_parser'
|
27
|
-
|
28
|
-
# Determine if a string of code is a valid Ruby expression.
|
29
|
-
# Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
|
30
|
-
# @param [String] code The code to validate.
|
31
|
-
# @return [Boolean] Whether or not the code is a valid Ruby expression.
|
32
|
-
# @example
|
33
|
-
# valid_expression?("class Hello") #=> false
|
34
|
-
# valid_expression?("class Hello; end") #=> true
|
35
|
-
def self.valid_expression?(code)
|
36
|
-
RubyParser.new.parse(code)
|
37
|
-
rescue Racc::ParseError, SyntaxError
|
38
|
-
false
|
39
|
-
else
|
40
|
-
true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Helper method responsible for extracting method body.
|
45
|
-
# Defined here to avoid polluting `Method` class.
|
46
|
-
# @param [Array] source_location The array returned by Method#source_location
|
47
|
-
# @return [File] The opened source file
|
48
|
-
def self.source_helper(source_location)
|
49
|
-
return nil if !source_location.is_a?(Array)
|
50
|
-
|
51
|
-
file_name, line = source_location
|
52
|
-
File.open(file_name) do |file|
|
53
|
-
(line - 1).times { file.readline }
|
54
|
-
|
55
|
-
code = ""
|
56
|
-
loop do
|
57
|
-
val = file.readline
|
58
|
-
code << val
|
59
|
-
|
60
|
-
return code if valid_expression?(code)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Helper method responsible for opening source file and buffering up
|
66
|
-
# the comments for a specified method. Defined here to avoid polluting
|
67
|
-
# `Method` class.
|
68
|
-
# @param [Array] source_location The array returned by Method#source_location
|
69
|
-
# @return [String] The comments up to the point of the method.
|
70
|
-
def self.comment_helper(source_location)
|
71
|
-
return nil if !source_location.is_a?(Array)
|
72
|
-
|
73
|
-
file_name, line = source_location
|
74
|
-
File.open(file_name) do |file|
|
75
|
-
buffer = ""
|
76
|
-
(line - 1).times do
|
77
|
-
line = file.readline
|
78
|
-
# Add any line that is a valid ruby comment,
|
79
|
-
# but clear as soon as we hit a non comment line.
|
80
|
-
if (line =~ /^\s*#/) || (line =~ /^\s*$/)
|
81
|
-
buffer << line.lstrip
|
82
|
-
else
|
83
|
-
buffer.replace("")
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
buffer
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# This module is to be included by `Method` and `UnboundMethod` and
|
92
|
-
# provides the `#source` functionality
|
93
|
-
module MethodExtensions
|
94
|
-
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
1
|
+
# (C) John Mair (banisterfiend) 2010
|
2
|
+
# MIT License
|
3
|
+
|
4
|
+
direc = File.dirname(__FILE__)
|
5
|
+
|
6
|
+
require "#{direc}/method_source/version"
|
7
|
+
require "#{direc}/method_source/source_location"
|
8
|
+
|
9
|
+
module MethodSource
|
10
|
+
|
11
|
+
if RUBY_VERSION =~ /1.9/
|
12
|
+
require 'ripper'
|
13
|
+
|
14
|
+
# Determine if a string of code is a valid Ruby expression.
|
15
|
+
# Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
|
16
|
+
# @param [String] code The code to validate.
|
17
|
+
# @return [Boolean] Whether or not the code is a valid Ruby expression.
|
18
|
+
# @example
|
19
|
+
# valid_expression?("class Hello") #=> false
|
20
|
+
# valid_expression?("class Hello; end") #=> true
|
21
|
+
def self.valid_expression?(code)
|
22
|
+
!!Ripper::SexpBuilder.new(code).parse
|
23
|
+
end
|
24
|
+
|
25
|
+
else
|
26
|
+
require 'ruby_parser'
|
27
|
+
|
28
|
+
# Determine if a string of code is a valid Ruby expression.
|
29
|
+
# Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
|
30
|
+
# @param [String] code The code to validate.
|
31
|
+
# @return [Boolean] Whether or not the code is a valid Ruby expression.
|
32
|
+
# @example
|
33
|
+
# valid_expression?("class Hello") #=> false
|
34
|
+
# valid_expression?("class Hello; end") #=> true
|
35
|
+
def self.valid_expression?(code)
|
36
|
+
RubyParser.new.parse(code)
|
37
|
+
rescue Racc::ParseError, SyntaxError
|
38
|
+
false
|
39
|
+
else
|
40
|
+
true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Helper method responsible for extracting method body.
|
45
|
+
# Defined here to avoid polluting `Method` class.
|
46
|
+
# @param [Array] source_location The array returned by Method#source_location
|
47
|
+
# @return [File] The opened source file
|
48
|
+
def self.source_helper(source_location)
|
49
|
+
return nil if !source_location.is_a?(Array)
|
50
|
+
|
51
|
+
file_name, line = source_location
|
52
|
+
File.open(file_name) do |file|
|
53
|
+
(line - 1).times { file.readline }
|
54
|
+
|
55
|
+
code = ""
|
56
|
+
loop do
|
57
|
+
val = file.readline
|
58
|
+
code << val
|
59
|
+
|
60
|
+
return code if valid_expression?(code)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Helper method responsible for opening source file and buffering up
|
66
|
+
# the comments for a specified method. Defined here to avoid polluting
|
67
|
+
# `Method` class.
|
68
|
+
# @param [Array] source_location The array returned by Method#source_location
|
69
|
+
# @return [String] The comments up to the point of the method.
|
70
|
+
def self.comment_helper(source_location)
|
71
|
+
return nil if !source_location.is_a?(Array)
|
72
|
+
|
73
|
+
file_name, line = source_location
|
74
|
+
File.open(file_name) do |file|
|
75
|
+
buffer = ""
|
76
|
+
(line - 1).times do
|
77
|
+
line = file.readline
|
78
|
+
# Add any line that is a valid ruby comment,
|
79
|
+
# but clear as soon as we hit a non comment line.
|
80
|
+
if (line =~ /^\s*#/) || (line =~ /^\s*$/)
|
81
|
+
buffer << line.lstrip
|
82
|
+
else
|
83
|
+
buffer.replace("")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
buffer
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# This module is to be included by `Method` and `UnboundMethod` and
|
92
|
+
# provides the `#source` functionality
|
93
|
+
module MethodExtensions
|
94
|
+
|
95
|
+
# We use the included hook to patch Method#source on rubinius.
|
96
|
+
# We need to use the included hook as Rubinius defines a `source`
|
97
|
+
# on Method so including a module will have no effect (as it's
|
98
|
+
# higher up the MRO).
|
99
|
+
# @param [Class] klass The class that includes the module.
|
100
|
+
def self.included(klass)
|
101
|
+
if klass.method_defined?(:source) && Object.const_defined?(:RUBY_ENGINE) &&
|
102
|
+
RUBY_ENGINE =~ /rbx/
|
103
|
+
|
104
|
+
klass.class_eval do
|
105
|
+
orig_source = instance_method(:source)
|
106
|
+
|
107
|
+
define_method(:source) do
|
108
|
+
begin
|
109
|
+
super
|
110
|
+
rescue
|
111
|
+
orig_source.bind(self).call
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return the sourcecode for the method as a string
|
120
|
+
# (This functionality is only supported in Ruby 1.9 and above)
|
121
|
+
# @return [String] The method sourcecode as a string
|
122
|
+
# @example
|
123
|
+
# Set.instance_method(:clear).source.display
|
124
|
+
# =>
|
125
|
+
# def clear
|
126
|
+
# @hash.clear
|
127
|
+
# self
|
128
|
+
# end
|
129
|
+
def source
|
130
|
+
if respond_to?(:source_location)
|
131
|
+
source = MethodSource.source_helper(source_location)
|
132
|
+
|
133
|
+
raise "Cannot locate source for this method: #{name}" if !source
|
134
|
+
else
|
135
|
+
raise "#{self.class}#source not supported by this Ruby version (#{RUBY_VERSION})"
|
136
|
+
end
|
137
|
+
|
138
|
+
source
|
139
|
+
end
|
140
|
+
|
141
|
+
# Return the comments associated with the method as a string.
|
142
|
+
# (This functionality is only supported in Ruby 1.9 and above)
|
143
|
+
# @return [String] The method's comments as a string
|
144
|
+
# @example
|
145
|
+
# Set.instance_method(:clear).comment.display
|
146
|
+
# =>
|
147
|
+
# # Removes all elements and returns self.
|
148
|
+
def comment
|
149
|
+
if respond_to?(:source_location)
|
150
|
+
comment = MethodSource.comment_helper(source_location)
|
151
|
+
|
152
|
+
raise "Cannot locate source for this method: #{name}" if !comment
|
153
|
+
else
|
154
|
+
raise "#{self.class}#comment not supported by this Ruby version (#{RUBY_VERSION})"
|
155
|
+
end
|
156
|
+
|
157
|
+
comment
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Method
|
163
|
+
include MethodSource::SourceLocation::MethodExtensions
|
164
|
+
include MethodSource::MethodExtensions
|
165
|
+
end
|
166
|
+
|
167
|
+
class UnboundMethod
|
168
|
+
include MethodSource::SourceLocation::UnboundMethodExtensions
|
169
|
+
include MethodSource::MethodExtensions
|
170
|
+
end
|
171
|
+
|
172
|
+
class Proc
|
173
|
+
include MethodSource::MethodExtensions
|
174
|
+
end
|
@@ -1,60 +1,60 @@
|
|
1
|
-
module MethodSource
|
2
|
-
module SourceLocation
|
3
|
-
module MethodExtensions
|
4
|
-
|
5
|
-
def trace_func(event, file, line, id, binding, classname)
|
6
|
-
return unless event == 'call'
|
7
|
-
set_trace_func nil
|
8
|
-
|
9
|
-
@file, @line = file, line
|
10
|
-
raise :found
|
11
|
-
end
|
12
|
-
|
13
|
-
private :trace_func
|
14
|
-
|
15
|
-
# Return the source location of a method for Ruby 1.8.
|
16
|
-
# @return [Array] A two element array. First element is the
|
17
|
-
# file, second element is the line in the file where the
|
18
|
-
# method definition is found.
|
19
|
-
def source_location
|
20
|
-
if @file.nil?
|
21
|
-
args =[*(1..(arity<-1 ? -arity-1 : arity ))]
|
22
|
-
|
23
|
-
set_trace_func method(:trace_func).to_proc
|
24
|
-
call *args rescue nil
|
25
|
-
set_trace_func nil
|
26
|
-
@file = File.expand_path(@file) if @file && File.exist?(File.expand_path(@file))
|
27
|
-
end
|
28
|
-
return [@file, @line] if File.exist?(@file.to_s)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
module UnboundMethodExtensions
|
33
|
-
|
34
|
-
# Return the source location of an instance method for Ruby 1.8.
|
35
|
-
# @return [Array] A two element array. First element is the
|
36
|
-
# file, second element is the line in the file where the
|
37
|
-
# method definition is found.
|
38
|
-
def source_location
|
39
|
-
klass = case owner
|
40
|
-
when Class
|
41
|
-
owner
|
42
|
-
when Module
|
43
|
-
method_owner = owner
|
44
|
-
Class.new { include(method_owner) }
|
45
|
-
end
|
46
|
-
|
47
|
-
begin
|
48
|
-
klass.allocate.method(name).source_location
|
49
|
-
rescue TypeError
|
50
|
-
|
51
|
-
# Assume we are dealing with a Singleton Class:
|
52
|
-
# 1. Get the instance object
|
53
|
-
# 2. Forward the source_location lookup to the instance
|
54
|
-
instance ||= ObjectSpace.each_object(owner).first
|
55
|
-
instance.method(name).source_location
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
1
|
+
module MethodSource
|
2
|
+
module SourceLocation
|
3
|
+
module MethodExtensions
|
4
|
+
|
5
|
+
def trace_func(event, file, line, id, binding, classname)
|
6
|
+
return unless event == 'call'
|
7
|
+
set_trace_func nil
|
8
|
+
|
9
|
+
@file, @line = file, line
|
10
|
+
raise :found
|
11
|
+
end
|
12
|
+
|
13
|
+
private :trace_func
|
14
|
+
|
15
|
+
# Return the source location of a method for Ruby 1.8.
|
16
|
+
# @return [Array] A two element array. First element is the
|
17
|
+
# file, second element is the line in the file where the
|
18
|
+
# method definition is found.
|
19
|
+
def source_location
|
20
|
+
if @file.nil?
|
21
|
+
args =[*(1..(arity<-1 ? -arity-1 : arity ))]
|
22
|
+
|
23
|
+
set_trace_func method(:trace_func).to_proc
|
24
|
+
call *args rescue nil
|
25
|
+
set_trace_func nil
|
26
|
+
@file = File.expand_path(@file) if @file && File.exist?(File.expand_path(@file))
|
27
|
+
end
|
28
|
+
return [@file, @line] if File.exist?(@file.to_s)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module UnboundMethodExtensions
|
33
|
+
|
34
|
+
# Return the source location of an instance method for Ruby 1.8.
|
35
|
+
# @return [Array] A two element array. First element is the
|
36
|
+
# file, second element is the line in the file where the
|
37
|
+
# method definition is found.
|
38
|
+
def source_location
|
39
|
+
klass = case owner
|
40
|
+
when Class
|
41
|
+
owner
|
42
|
+
when Module
|
43
|
+
method_owner = owner
|
44
|
+
Class.new { include(method_owner) }
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
klass.allocate.method(name).source_location
|
49
|
+
rescue TypeError
|
50
|
+
|
51
|
+
# Assume we are dealing with a Singleton Class:
|
52
|
+
# 1. Get the instance object
|
53
|
+
# 2. Forward the source_location lookup to the instance
|
54
|
+
instance ||= ObjectSpace.each_object(owner).first
|
55
|
+
instance.method(name).source_location
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
module MethodSource
|
2
|
-
VERSION = "0.3.
|
3
|
-
end
|
1
|
+
module MethodSource
|
2
|
+
VERSION = "0.3.5"
|
3
|
+
end
|
data/test/test.rb
CHANGED
@@ -1,100 +1,100 @@
|
|
1
|
-
direc = File.dirname(__FILE__)
|
2
|
-
|
3
|
-
require 'bacon'
|
4
|
-
require "#{direc}/../lib/method_source"
|
5
|
-
require "#{direc}/test_helper"
|
6
|
-
|
7
|
-
describe MethodSource do
|
8
|
-
|
9
|
-
before do
|
10
|
-
@hello_module_source = " def hello; :hello_module; end\n"
|
11
|
-
@hello_singleton_source = "def $o.hello; :hello_singleton; end\n"
|
12
|
-
@hello_source = "def hello; :hello; end\n"
|
13
|
-
@hello_comment = "# A comment for hello\n# It spans two lines and is indented by 2 spaces\n"
|
14
|
-
@lambda_comment = "# This is a comment for MyLambda\n"
|
15
|
-
@lambda_source = "MyLambda = lambda { :lambda }\n"
|
16
|
-
@proc_source = "MyProc = Proc.new { :proc }\n"
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should define methods on Method and UnboundMethod and Proc' do
|
20
|
-
Method.method_defined?(:source).should == true
|
21
|
-
UnboundMethod.method_defined?(:source).should == true
|
22
|
-
Proc.method_defined?(:source).should == true
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "Methods" do
|
26
|
-
it 'should return source for method' do
|
27
|
-
method(:hello).source.should == @hello_source
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'should return source for a method defined in a module' do
|
31
|
-
M.instance_method(:hello).source.should == @hello_module_source
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'should return source for a singleton method as an instance method' do
|
35
|
-
class << $o; self; end.instance_method(:hello).source.should == @hello_singleton_source
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should return source for a singleton method' do
|
39
|
-
$o.method(:hello).source.should == @hello_singleton_source
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
it 'should return a comment for method' do
|
44
|
-
method(:hello).comment.should == @hello_comment
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'should raise for C methods' do
|
48
|
-
lambda { method(:puts).source }.should.raise RuntimeError
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
if RUBY_VERSION =~ /1.9/
|
53
|
-
describe "Lambdas and Procs" do
|
54
|
-
it 'should return source for proc' do
|
55
|
-
MyProc.source.should == @proc_source
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'should return an empty string if there is no comment' do
|
59
|
-
MyProc.comment.should == ''
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'should return source for lambda' do
|
63
|
-
MyLambda.source.should == @lambda_source
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'should return comment for lambda' do
|
67
|
-
MyLambda.comment.should == @lambda_comment
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
describe "Comment tests" do
|
72
|
-
before do
|
73
|
-
@comment1 = "# a\n# b\n"
|
74
|
-
@comment2 = "# a\n# b\n"
|
75
|
-
@comment3 = "# a\n#\n# b\n"
|
76
|
-
@comment4 = "# a\n# b\n"
|
77
|
-
@comment5 = "# a\n# b\n# c\n# d\n"
|
78
|
-
end
|
79
|
-
|
80
|
-
it "should correctly extract multi-line comments" do
|
81
|
-
method(:comment_test1).comment.should == @comment1
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should correctly strip leading whitespace before comments" do
|
85
|
-
method(:comment_test2).comment.should == @comment2
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should keep empty comment lines" do
|
89
|
-
method(:comment_test3).comment.should == @comment3
|
90
|
-
end
|
91
|
-
|
92
|
-
it "should ignore blank lines between comments" do
|
93
|
-
method(:comment_test4).comment.should == @comment4
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should align all comments to same indent level" do
|
97
|
-
method(:comment_test5).comment.should == @comment5
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
1
|
+
direc = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'bacon'
|
4
|
+
require "#{direc}/../lib/method_source"
|
5
|
+
require "#{direc}/test_helper"
|
6
|
+
|
7
|
+
describe MethodSource do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@hello_module_source = " def hello; :hello_module; end\n"
|
11
|
+
@hello_singleton_source = "def $o.hello; :hello_singleton; end\n"
|
12
|
+
@hello_source = "def hello; :hello; end\n"
|
13
|
+
@hello_comment = "# A comment for hello\n# It spans two lines and is indented by 2 spaces\n"
|
14
|
+
@lambda_comment = "# This is a comment for MyLambda\n"
|
15
|
+
@lambda_source = "MyLambda = lambda { :lambda }\n"
|
16
|
+
@proc_source = "MyProc = Proc.new { :proc }\n"
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should define methods on Method and UnboundMethod and Proc' do
|
20
|
+
Method.method_defined?(:source).should == true
|
21
|
+
UnboundMethod.method_defined?(:source).should == true
|
22
|
+
Proc.method_defined?(:source).should == true
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "Methods" do
|
26
|
+
it 'should return source for method' do
|
27
|
+
method(:hello).source.should == @hello_source
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return source for a method defined in a module' do
|
31
|
+
M.instance_method(:hello).source.should == @hello_module_source
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return source for a singleton method as an instance method' do
|
35
|
+
class << $o; self; end.instance_method(:hello).source.should == @hello_singleton_source
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return source for a singleton method' do
|
39
|
+
$o.method(:hello).source.should == @hello_singleton_source
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
it 'should return a comment for method' do
|
44
|
+
method(:hello).comment.should == @hello_comment
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise for C methods' do
|
48
|
+
lambda { method(:puts).source }.should.raise RuntimeError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if RUBY_VERSION =~ /1.9/
|
53
|
+
describe "Lambdas and Procs" do
|
54
|
+
it 'should return source for proc' do
|
55
|
+
MyProc.source.should == @proc_source
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return an empty string if there is no comment' do
|
59
|
+
MyProc.comment.should == ''
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should return source for lambda' do
|
63
|
+
MyLambda.source.should == @lambda_source
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should return comment for lambda' do
|
67
|
+
MyLambda.comment.should == @lambda_comment
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
describe "Comment tests" do
|
72
|
+
before do
|
73
|
+
@comment1 = "# a\n# b\n"
|
74
|
+
@comment2 = "# a\n# b\n"
|
75
|
+
@comment3 = "# a\n#\n# b\n"
|
76
|
+
@comment4 = "# a\n# b\n"
|
77
|
+
@comment5 = "# a\n# b\n# c\n# d\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should correctly extract multi-line comments" do
|
81
|
+
method(:comment_test1).comment.should == @comment1
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should correctly strip leading whitespace before comments" do
|
85
|
+
method(:comment_test2).comment.should == @comment2
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should keep empty comment lines" do
|
89
|
+
method(:comment_test3).comment.should == @comment3
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should ignore blank lines between comments" do
|
93
|
+
method(:comment_test4).comment.should == @comment4
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should align all comments to same indent level" do
|
97
|
+
method(:comment_test5).comment.should == @comment5
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
|
-
module M
|
2
|
-
def hello; :hello_module; end
|
3
|
-
end
|
4
|
-
|
5
|
-
$o = Object.new
|
6
|
-
def $o.hello; :hello_singleton; end
|
7
|
-
|
8
|
-
# A comment for hello
|
9
|
-
|
10
|
-
# It spans two lines and is indented by 2 spaces
|
11
|
-
def hello; :hello; end
|
12
|
-
|
13
|
-
# a
|
14
|
-
# b
|
15
|
-
def comment_test1; end
|
16
|
-
|
17
|
-
# a
|
18
|
-
# b
|
19
|
-
def comment_test2; end
|
20
|
-
|
21
|
-
# a
|
22
|
-
#
|
23
|
-
# b
|
24
|
-
def comment_test3; end
|
25
|
-
|
26
|
-
# a
|
27
|
-
|
28
|
-
# b
|
29
|
-
def comment_test4; end
|
30
|
-
|
31
|
-
|
32
|
-
# a
|
33
|
-
# b
|
34
|
-
# c
|
35
|
-
# d
|
36
|
-
def comment_test5; end
|
37
|
-
|
38
|
-
# This is a comment for MyLambda
|
39
|
-
MyLambda = lambda { :lambda }
|
40
|
-
MyProc = Proc.new { :proc }
|
41
|
-
|
1
|
+
module M
|
2
|
+
def hello; :hello_module; end
|
3
|
+
end
|
4
|
+
|
5
|
+
$o = Object.new
|
6
|
+
def $o.hello; :hello_singleton; end
|
7
|
+
|
8
|
+
# A comment for hello
|
9
|
+
|
10
|
+
# It spans two lines and is indented by 2 spaces
|
11
|
+
def hello; :hello; end
|
12
|
+
|
13
|
+
# a
|
14
|
+
# b
|
15
|
+
def comment_test1; end
|
16
|
+
|
17
|
+
# a
|
18
|
+
# b
|
19
|
+
def comment_test2; end
|
20
|
+
|
21
|
+
# a
|
22
|
+
#
|
23
|
+
# b
|
24
|
+
def comment_test3; end
|
25
|
+
|
26
|
+
# a
|
27
|
+
|
28
|
+
# b
|
29
|
+
def comment_test4; end
|
30
|
+
|
31
|
+
|
32
|
+
# a
|
33
|
+
# b
|
34
|
+
# c
|
35
|
+
# d
|
36
|
+
def comment_test5; end
|
37
|
+
|
38
|
+
# This is a comment for MyLambda
|
39
|
+
MyLambda = lambda { :lambda }
|
40
|
+
MyProc = Proc.new { :proc }
|
41
|
+
|
metadata
CHANGED
@@ -1,48 +1,64 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_source
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 3
|
8
|
+
- 5
|
9
|
+
version: 0.3.5
|
6
10
|
platform: ruby
|
7
|
-
authors:
|
11
|
+
authors:
|
8
12
|
- John Mair (banisterfiend)
|
9
13
|
autorequire:
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
|
-
|
16
|
+
|
17
|
+
date: 2011-03-05 00:00:00 +13:00
|
13
18
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
16
21
|
name: ruby_parser
|
17
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
24
|
none: false
|
19
|
-
requirements:
|
20
|
-
- -
|
21
|
-
- !ruby/object:Gem::Version
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 0
|
31
|
+
- 5
|
22
32
|
version: 2.0.5
|
23
33
|
type: :runtime
|
24
|
-
|
25
|
-
|
26
|
-
- !ruby/object:Gem::Dependency
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
27
36
|
name: bacon
|
28
|
-
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
39
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 1
|
46
|
+
- 0
|
33
47
|
version: 1.1.0
|
34
48
|
type: :development
|
35
|
-
|
36
|
-
version_requirements: *17300544
|
49
|
+
version_requirements: *id002
|
37
50
|
description: retrieve the sourcecode for a method
|
38
51
|
email: jrmair@gmail.com
|
39
52
|
executables: []
|
53
|
+
|
40
54
|
extensions: []
|
55
|
+
|
41
56
|
extra_rdoc_files: []
|
42
|
-
|
43
|
-
|
44
|
-
- lib/method_source/version.rb
|
57
|
+
|
58
|
+
files:
|
45
59
|
- lib/method_source.rb
|
60
|
+
- lib/method_source/version.rb
|
61
|
+
- lib/method_source/source_location.rb
|
46
62
|
- test/test.rb
|
47
63
|
- test/test_helper.rb
|
48
64
|
- README.markdown
|
@@ -51,26 +67,34 @@ files:
|
|
51
67
|
has_rdoc: true
|
52
68
|
homepage: http://banisterfiend.wordpress.com
|
53
69
|
licenses: []
|
70
|
+
|
54
71
|
post_install_message:
|
55
72
|
rdoc_options: []
|
56
|
-
|
73
|
+
|
74
|
+
require_paths:
|
57
75
|
- lib
|
58
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
77
|
none: false
|
60
|
-
requirements:
|
61
|
-
- -
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
|
64
|
-
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
85
|
none: false
|
66
|
-
requirements:
|
67
|
-
- -
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
70
92
|
requirements: []
|
93
|
+
|
71
94
|
rubyforge_project:
|
72
|
-
rubygems_version: 1.
|
95
|
+
rubygems_version: 1.3.7
|
73
96
|
signing_key:
|
74
97
|
specification_version: 3
|
75
98
|
summary: retrieve the sourcecode for a method
|
76
99
|
test_files: []
|
100
|
+
|