summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJohn Mair <jrmair@gmail.com>2010-12-17 02:58:01 +1300
committerJohn Mair <jrmair@gmail.com>2010-12-17 02:58:01 +1300
commit2895bb245fd216be96da6e4d6b9e361248934450 (patch)
treebd0a0480b25a3eb11f1aba0ef9fc2825a4a16d3b /lib
downloadmethod_source-2895bb245fd216be96da6e4d6b9e361248934450.tar.gz
first commit
Diffstat (limited to 'lib')
-rw-r--r--lib/method_source.rb94
-rw-r--r--lib/method_source/version.rb3
2 files changed, 97 insertions, 0 deletions
diff --git a/lib/method_source.rb b/lib/method_source.rb
new file mode 100644
index 0000000..bdb00ed
--- /dev/null
+++ b/lib/method_source.rb
@@ -0,0 +1,94 @@
+# (C) John Mair (banisterfiend) 2010
+# MIT License
+
+direc = File.dirname(__FILE__)
+
+require 'stringio'
+require "#{direc}/method_source/version"
+
+module MethodSource
+
+ # Helper method used to find end of method body
+ # @param [String] code The string of Ruby code to check for
+ # correctness
+ # @return [Boolean]
+ def self.valid_expression?(code)
+ suppress_stderr do
+ RubyVM::InstructionSequence.new(code)
+ end
+ rescue Exception
+ false
+ else
+ true
+ end
+
+ # Helper method used to suppress stderr output by the
+ # `RubyVM::InstructionSequence` method
+ # @yield The block where stderr is suppressed
+ def self.suppress_stderr
+ real_stderr, $stderr = $stderr, StringIO.new
+ yield
+ ensure
+ $stderr = real_stderr
+ end
+
+ # Helper method responsible for opening source file and advancing to
+ # the correct linenumber. Defined here to avoid polluting `Method`
+ # class.
+ # @param [Array] source_location The array returned by Method#source_location
+ # @return [File] The opened source file
+ def self.source_helper(source_location)
+ return nil if !source_location.is_a?(Array)
+
+ file_name, line = source_location
+ file = File.open(file_name)
+ (line - 1).times { file.readline }
+ file
+ end
+
+ # This module is to be included by `Method` and `UnboundMethod` and
+ # provides the `#source` functionality
+ module MethodExtensions
+
+ # Return the sourcecode for the method as a string
+ # (This functionality is only supported in Ruby 1.9 and above)
+ # @return [String] The method sourcecode as a string
+ # @example
+ # Set.instance_method(:clear).source.display
+ # =>
+ # def clear
+ # @hash.clear
+ # self
+ # end
+ def source
+ file = nil
+
+ if respond_to?(:source_location)
+ file = MethodSource.source_helper(source_location)
+
+ raise "Cannot locate source for this method: #{name}" if !file
+ else
+ raise "Method#source not supported by this Ruby version (#{RUBY_VERSION})"
+ end
+
+ code = ""
+ loop do
+ val = file.readline
+ code += val
+
+ return code if MethodSource.valid_expression?(code)
+ end
+
+ ensure
+ file.close if file
+ end
+ end
+end
+
+class Method
+ include MethodSource::MethodExtensions
+end
+
+class UnboundMethod
+ include MethodSource::MethodExtensions
+end
diff --git a/lib/method_source/version.rb b/lib/method_source/version.rb
new file mode 100644
index 0000000..30c0683
--- /dev/null
+++ b/lib/method_source/version.rb
@@ -0,0 +1,3 @@
+module MethodSource
+ VERSION = "0.1.0"
+end