diff options
Diffstat (limited to 'lib/method_source.rb')
-rw-r--r-- | lib/method_source.rb | 139 |
1 files changed, 43 insertions, 96 deletions
diff --git a/lib/method_source.rb b/lib/method_source.rb index 0353144..fa6bc3b 100644 --- a/lib/method_source.rb +++ b/lib/method_source.rb @@ -5,76 +5,27 @@ direc = File.dirname(__FILE__) require "#{direc}/method_source/version" require "#{direc}/method_source/source_location" +require "#{direc}/method_source/code_helpers" module MethodSource + extend MethodSource::CodeHelpers + # An Exception to mark errors that were raised trying to find the source from + # a given source_location. + # class SourceNotFoundError < StandardError; end - # Determine if a string of code is a valid Ruby expression. - # @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?(str) - if defined?(Rubinius::Melbourne19) && RUBY_VERSION =~ /^1\.9/ - Rubinius::Melbourne19.parse_string(str) - elsif defined?(Rubinius::Melbourne) - Rubinius::Melbourne.parse_string(str) - else - catch(:valid) { - eval("BEGIN{throw :valid}\n#{str}") - } - end - true - rescue SyntaxError - false - end - # Helper method responsible for extracting method body. # Defined here to avoid polluting `Method` class. # @param [Array] source_location The array returned by Method#source_location # @return [String] The method body def self.source_helper(source_location) - return nil if !source_location.is_a?(Array) - - # 1st try: simple eval - code = extract_code(source_location) - - unless code - # 2nd try: attempt to re-scan method body, this time, assume we're inside an eval string simulate interpolation of #{} expressions by replacing it with placeholder - # - # A temporary work around for cases where method body is defined inside a - # string (i.e. class_evaled methods), and the resulting valid_expression - # doesn't return true due to string not being interpolated. - # (see https://github.com/banister/method_source/issues/13) - # - code = extract_code(source_location) { |code| code.gsub(/\#\{.*?\}/,"temp") } - end + raise SourceNotFoundError, "Source location not found" unless source_location + file, line = *source_location - code - rescue Errno::ENOENT - raise SourceNotFoundError, "Cannot get source code located at file: #{source_location[0]}" - end - - # @param [Array] source_location The array containing file_name [String], line [Fixnum] - # @param [Block] An optional block that can be passed that will be used to modify - # the code buffer before its syntax is evaluated - # @return [String] The method body - def self.extract_code(source_location) - file_name, line = source_location - code = "" - lines_for_file(file_name)[(line - 1)..-1].each do |line| - code << line - expression = block_given? ? yield(code) : code - return code if valid_expression?(expression) - end - nil - end - - def self.lines_for_file(file_name) - @lines_for_file ||= {} - @lines_for_file[file_name] ||= File.readlines(file_name) + expression_at(lines_for(file), line) + rescue SyntaxError => e + raise SourceNotFoundError, e.message end # Helper method responsible for opening source file and buffering up @@ -83,24 +34,34 @@ module MethodSource # @param [Array] source_location The array returned by Method#source_location # @return [String] The comments up to the point of the method. def self.comment_helper(source_location) - return nil if !source_location.is_a?(Array) - - file_name, line = source_location - File.open(file_name) do |file| - buffer = "" - (line - 1).times do - line = file.readline - # Add any line that is a valid ruby comment, - # but clear as soon as we hit a non comment line. - if (line =~ /^\s*#/) || (line =~ /^\s*$/) - buffer << line.lstrip - else - buffer.replace("") - end - end + raise SourceNotFoundError, "Source location not found" unless source_location + file, line = *source_location - buffer - end + comment_describing(lines_for(file), line) + end + + # Load a memoized copy of the lines in a file. + # + # @param [String] file_name + # @return [Array<String>] the contents of the file + # @raise [SourceNotFoundError] + def self.lines_for(file_name) + @lines_for_file ||= {} + @lines_for_file[file_name] ||= File.readlines(file_name) + rescue Errno::ENOENT => e + raise SourceNotFoundError, e.message + end + + # @deprecated — use MethodSource::CodeHelpers#complete_expression? + def self.valid_expression?(str) + complete_expression?(str) + rescue SyntaxError + false + end + + # @deprecated — use MethodSource::CodeHelpers#expression_at + def self.extract_code(source_location) + source_helper(source_location) end # This module is to be included by `Method` and `UnboundMethod` and @@ -132,8 +93,9 @@ module MethodSource end # 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 + # @raise SourceNotFoundException + # # @example # Set.instance_method(:clear).source.display # => @@ -142,34 +104,19 @@ module MethodSource # self # end def source - if respond_to?(:source_location) - source = MethodSource.source_helper(source_location) - - raise SourceNotFoundError, "Cannot locate source for this method: #{name}" if !source - else - raise SourceNotFoundError, "#{self.class}#source not supported by this Ruby version (#{RUBY_VERSION})" - end - - source + MethodSource.source_helper(source_location) end # Return the comments associated with the method as a string. - # (This functionality is only supported in Ruby 1.9 and above) # @return [String] The method's comments as a string + # @raise SourceNotFoundException + # # @example # Set.instance_method(:clear).comment.display # => # # Removes all elements and returns self. def comment - if respond_to?(:source_location) - comment = MethodSource.comment_helper(source_location) - - raise SourceNotFoundError, "Cannot locate source for this method: #{name}" if !comment - else - raise SourceNotFoundError, "#{self.class}#comment not supported by this Ruby version (#{RUBY_VERSION})" - end - - comment + MethodSource.comment_helper(source_location) end end end |