summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/method_source.rb48
-rw-r--r--lib/method_source/source_location.rb56
-rw-r--r--lib/method_source/version.rb2
3 files changed, 93 insertions, 13 deletions
diff --git a/lib/method_source.rb b/lib/method_source.rb
index d6a6e29..43651b9 100644
--- a/lib/method_source.rb
+++ b/lib/method_source.rb
@@ -4,19 +4,41 @@
direc = File.dirname(__FILE__)
require "#{direc}/method_source/version"
-
-if RUBY_VERSION =~ /1.9/
- require 'ripper'
-end
+require "#{direc}/method_source/source_location"
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)
- !!Ripper::SexpBuilder.new(code).parse
+ if RUBY_VERSION =~ /1.9/
+ require 'ripper'
+
+ # Determine if a string of code is a valid Ruby expression.
+ # Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
+ # @param [String] code The code to validate.
+ # @return [Boolean] Whether or not the code is a valid Ruby expression.
+ # @example
+ # valid_expression?("class Hello") #=> false
+ # valid_expression?("class Hello; end") #=> true
+ def self.valid_expression?(code)
+ !!Ripper::SexpBuilder.new(code).parse
+ end
+
+ else
+ require 'ruby_parser'
+
+ # Determine if a string of code is a valid Ruby expression.
+ # Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
+ # @param [String] code The code to validate.
+ # @return [Boolean] Whether or not the code is a valid Ruby expression.
+ # @example
+ # valid_expression?("class Hello") #=> false
+ # valid_expression?("class Hello; end") #=> true
+ def self.valid_expression?(code)
+ RubyParser.new.parse(code)
+ rescue Racc::ParseError, SyntaxError
+ false
+ else
+ true
+ end
end
# Helper method responsible for extracting method body.
@@ -86,7 +108,7 @@ module MethodSource
raise "Cannot locate source for this method: #{name}" if !source
else
- raise "Method#source not supported by this Ruby version (#{RUBY_VERSION})"
+ raise "#{self.class}#source not supported by this Ruby version (#{RUBY_VERSION})"
end
source
@@ -105,7 +127,7 @@ module MethodSource
raise "Cannot locate source for this method: #{name}" if !comment
else
- raise "Method#comment not supported by this Ruby version (#{RUBY_VERSION})"
+ raise "#{self.class}#comment not supported by this Ruby version (#{RUBY_VERSION})"
end
comment
@@ -114,10 +136,12 @@ module MethodSource
end
class Method
+ include MethodSource::SourceLocation::MethodExtensions
include MethodSource::MethodExtensions
end
class UnboundMethod
+ include MethodSource::SourceLocation::UnboundMethodExtensions
include MethodSource::MethodExtensions
end
diff --git a/lib/method_source/source_location.rb b/lib/method_source/source_location.rb
new file mode 100644
index 0000000..1878b18
--- /dev/null
+++ b/lib/method_source/source_location.rb
@@ -0,0 +1,56 @@
+module MethodSource
+ module SourceLocation
+ module MethodExtensions
+ def trace_func(event, file, line, id, binding, classname)
+ return unless event == 'call'
+ set_trace_func nil
+
+ @file, @line = file, line
+ raise :found
+ end
+
+ # Return the source location of a method for Ruby 1.8.
+ # @return [Array] A two element array. First element is the
+ # file, second element is the line in the file where the
+ # method definition is found.
+ def source_location
+ if @file.nil?
+ args =[*(1..(arity<-1 ? -arity-1 : arity ))]
+
+ set_trace_func method(:trace_func).to_proc
+ call *args rescue nil
+ set_trace_func nil
+ @file = File.expand_path(@file) if @file && File.exist?(File.expand_path(@file))
+ end
+ return [@file, @line] if File.exist?(@file.to_s)
+ end
+ end
+
+ module UnboundMethodExtensions
+
+ # Return the source location of an instance method for Ruby 1.8.
+ # @return [Array] A two element array. First element is the
+ # file, second element is the line in the file where the
+ # method definition is found.
+ def source_location
+ klass = case owner
+ when Class
+ owner
+ when Module
+ Class.new.tap { |v| v.send(:include, owner) }
+ end
+
+ begin
+ klass.allocate.method(name).source_location
+ rescue TypeError
+
+ # Assume we are dealing with a Singleton Class:
+ # 1. Get the instance object
+ # 2. Forward the source_location lookup to the instance
+ instance ||= ObjectSpace.each_object(owner).first
+ instance.method(name).source_location
+ end
+ end
+ end
+ end
+end
diff --git a/lib/method_source/version.rb b/lib/method_source/version.rb
index 2ac241c..6a830a9 100644
--- a/lib/method_source/version.rb
+++ b/lib/method_source/version.rb
@@ -1,3 +1,3 @@
module MethodSource
- VERSION = "0.2.0"
+ VERSION = "0.3.0"
end