From 21553e3dda02ec44d859f402ee72ca00deed0f26 Mon Sep 17 00:00:00 2001 From: Reginald Tan Date: Wed, 2 May 2012 22:29:17 -0400 Subject: If displaying method source fails for *_evaled methods, retry again. This time, assume inside eval string and simulate interpolation of #{} by replacing with a placeholder when doing syntax validation Only works if *_eval contains the arguments filename and lineno + 1. Without them, UnboundMethod#source_location would not return the proper filename and lineno needed to display the source. --- lib/method_source.rb | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/method_source.rb b/lib/method_source.rb index 9a3c325..75bbb8e 100644 --- a/lib/method_source.rb +++ b/lib/method_source.rb @@ -31,22 +31,48 @@ module MethodSource # 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 [File] The opened source file + # @return [String] The method body def self.source_helper(source_location) return nil if !source_location.is_a?(Array) - file_name, line = source_location - File.open(file_name) do |file| - (line - 1).times { file.readline } + # 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 - code = "" - loop do - val = file.readline - code << val + code + rescue Errno::ENOENT + # source_location[0] of evaled methods would return (eval) if __FILE__ and __LINE__ is not given. File.readlines "(eval)" would raise ENOENT + nil + end - return code if valid_expression?(code) - 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) end # Helper method responsible for opening source file and buffering up -- cgit v1.2.1