method_source 0.1.0
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 +48 -0
- data/Rakefile +57 -0
- data/lib/method_source.rb +94 -0
- data/lib/method_source/version.rb +3 -0
- data/test/test.rb +32 -0
- metadata +71 -0
data/README.markdown
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
method_source
|
2
|
+
=============
|
3
|
+
|
4
|
+
(C) John Mair (banisterfiend) 2010
|
5
|
+
|
6
|
+
_retrieve the sourcecode for a method_
|
7
|
+
|
8
|
+
`method_source` is a utility to return a method's sourcecode as a
|
9
|
+
Ruby string.
|
10
|
+
|
11
|
+
It is written in pure Ruby (no C).
|
12
|
+
|
13
|
+
`method_source` provides the `source` method to the `Method` and
|
14
|
+
`UnboundMethod` classes.
|
15
|
+
|
16
|
+
* Install the [gem](https://rubygems.org/gems/method_source): `gem install method_source`
|
17
|
+
* Read the [documentation](http://rdoc.info/github/banister/method_source/master/file/README.markdown)
|
18
|
+
* See the [source code](http://github.com/banister/method_source)
|
19
|
+
|
20
|
+
example:
|
21
|
+
---------
|
22
|
+
|
23
|
+
Set.instance_method(:merge).source.display
|
24
|
+
# =>
|
25
|
+
def merge(enum)
|
26
|
+
if enum.instance_of?(self.class)
|
27
|
+
@hash.update(enum.instance_variable_get(:@hash))
|
28
|
+
else
|
29
|
+
do_with_enum(enum) { |o| add(o) }
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
Limitations:
|
36
|
+
------------
|
37
|
+
|
38
|
+
* Only works with Ruby 1.9+
|
39
|
+
* Cannot return source for C methods.
|
40
|
+
* Cannot return source for dynamically defined methods.
|
41
|
+
|
42
|
+
Possible Applications:
|
43
|
+
----------------------
|
44
|
+
|
45
|
+
* Combine with [RubyParser](https://github.com/seattlerb/ruby_parser)
|
46
|
+
for extra fun.
|
47
|
+
|
48
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
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.homepage = "http://banisterfiend.wordpress.com"
|
23
|
+
s.has_rdoc = 'yard'
|
24
|
+
s.files = Dir["ext/**/extconf.rb", "ext/**/*.h", "ext/**/*.c", "lib/**/*.rb",
|
25
|
+
"test/*.rb", "CHANGELOG", "README.markdown", "Rakefile"]
|
26
|
+
end
|
27
|
+
|
28
|
+
task :test do
|
29
|
+
sh "bacon -k #{direc}/test/test.rb"
|
30
|
+
end
|
31
|
+
|
32
|
+
namespace :ruby do
|
33
|
+
spec = Gem::Specification.new do |s|
|
34
|
+
apply_spec_defaults(s)
|
35
|
+
s.platform = Gem::Platform::RUBY
|
36
|
+
end
|
37
|
+
|
38
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
39
|
+
pkg.need_zip = false
|
40
|
+
pkg.need_tar = false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "build all platform gems at once"
|
45
|
+
task :gems => [:rmgems, "ruby:gem"]
|
46
|
+
|
47
|
+
desc "remove all platform gems"
|
48
|
+
task :rmgems => ["ruby:clobber_package"]
|
49
|
+
|
50
|
+
desc "build and push latest gems"
|
51
|
+
task :pushgems => :gems do
|
52
|
+
chdir("#{direc}/pkg") do
|
53
|
+
Dir["*.gem"].each do |gemfile|
|
54
|
+
sh "gem push #{gemfile}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# (C) John Mair (banisterfiend) 2010
|
2
|
+
# MIT License
|
3
|
+
|
4
|
+
direc = File.dirname(__FILE__)
|
5
|
+
|
6
|
+
require 'stringio'
|
7
|
+
require "#{direc}/method_source/version"
|
8
|
+
|
9
|
+
module MethodSource
|
10
|
+
|
11
|
+
# Helper method used to find end of method body
|
12
|
+
# @param [String] code The string of Ruby code to check for
|
13
|
+
# correctness
|
14
|
+
# @return [Boolean]
|
15
|
+
def self.valid_expression?(code)
|
16
|
+
suppress_stderr do
|
17
|
+
RubyVM::InstructionSequence.new(code)
|
18
|
+
end
|
19
|
+
rescue Exception
|
20
|
+
false
|
21
|
+
else
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
# Helper method used to suppress stderr output by the
|
26
|
+
# `RubyVM::InstructionSequence` method
|
27
|
+
# @yield The block where stderr is suppressed
|
28
|
+
def self.suppress_stderr
|
29
|
+
real_stderr, $stderr = $stderr, StringIO.new
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
$stderr = real_stderr
|
33
|
+
end
|
34
|
+
|
35
|
+
# Helper method responsible for opening source file and advancing to
|
36
|
+
# the correct linenumber. Defined here to avoid polluting `Method`
|
37
|
+
# class.
|
38
|
+
# @param [Array] source_location The array returned by Method#source_location
|
39
|
+
# @return [File] The opened source file
|
40
|
+
def self.source_helper(source_location)
|
41
|
+
return nil if !source_location.is_a?(Array)
|
42
|
+
|
43
|
+
file_name, line = source_location
|
44
|
+
file = File.open(file_name)
|
45
|
+
(line - 1).times { file.readline }
|
46
|
+
file
|
47
|
+
end
|
48
|
+
|
49
|
+
# This module is to be included by `Method` and `UnboundMethod` and
|
50
|
+
# provides the `#source` functionality
|
51
|
+
module MethodExtensions
|
52
|
+
|
53
|
+
# Return the sourcecode for the method as a string
|
54
|
+
# (This functionality is only supported in Ruby 1.9 and above)
|
55
|
+
# @return [String] The method sourcecode as a string
|
56
|
+
# @example
|
57
|
+
# Set.instance_method(:clear).source.display
|
58
|
+
# =>
|
59
|
+
# def clear
|
60
|
+
# @hash.clear
|
61
|
+
# self
|
62
|
+
# end
|
63
|
+
def source
|
64
|
+
file = nil
|
65
|
+
|
66
|
+
if respond_to?(:source_location)
|
67
|
+
file = MethodSource.source_helper(source_location)
|
68
|
+
|
69
|
+
raise "Cannot locate source for this method: #{name}" if !file
|
70
|
+
else
|
71
|
+
raise "Method#source not supported by this Ruby version (#{RUBY_VERSION})"
|
72
|
+
end
|
73
|
+
|
74
|
+
code = ""
|
75
|
+
loop do
|
76
|
+
val = file.readline
|
77
|
+
code += val
|
78
|
+
|
79
|
+
return code if MethodSource.valid_expression?(code)
|
80
|
+
end
|
81
|
+
|
82
|
+
ensure
|
83
|
+
file.close if file
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Method
|
89
|
+
include MethodSource::MethodExtensions
|
90
|
+
end
|
91
|
+
|
92
|
+
class UnboundMethod
|
93
|
+
include MethodSource::MethodExtensions
|
94
|
+
end
|
data/test/test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
direc = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'bacon'
|
4
|
+
require "#{direc}/../lib/method_source"
|
5
|
+
|
6
|
+
hello_source = "def hello; :hello; end\n"
|
7
|
+
|
8
|
+
def hello; :hello; end
|
9
|
+
|
10
|
+
describe MethodSource do
|
11
|
+
|
12
|
+
it 'should define methods on both Method and UnboundMethod' do
|
13
|
+
Method.method_defined?(:source).should == true
|
14
|
+
UnboundMethod.method_defined?(:source).should == true
|
15
|
+
end
|
16
|
+
|
17
|
+
if RUBY_VERSION =~ /1.9/
|
18
|
+
it 'should return source for method' do
|
19
|
+
method(:hello).source.should == hello_source
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should raise for C methods' do
|
23
|
+
lambda { method(:puts).source }.should.raise RuntimeError
|
24
|
+
end
|
25
|
+
|
26
|
+
else
|
27
|
+
it 'should raise on #source' do
|
28
|
+
lambda { method(:hello).source }.should.raise RuntimeError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: method_source
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- John Mair (banisterfiend)
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-17 00:00:00 +13:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: retrieve the sourcecode for a method
|
23
|
+
email: jrmair@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/method_source/version.rb
|
32
|
+
- lib/method_source.rb
|
33
|
+
- test/test.rb
|
34
|
+
- README.markdown
|
35
|
+
- Rakefile
|
36
|
+
has_rdoc: yard
|
37
|
+
homepage: http://banisterfiend.wordpress.com
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
hash: 3
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.7
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: retrieve the sourcecode for a method
|
70
|
+
test_files: []
|
71
|
+
|