Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/method_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ class SourceNotFoundError < StandardError; end
# @param [Array] source_location The array returned by Method#source_location
# @param [String] method_name
# @return [String] The method body
def self.source_helper(source_location, name=nil)
def self.source_helper(source_location, name=nil, options={})
raise SourceNotFoundError, "Could not locate source for #{name}!" unless source_location
file, line = *source_location

expression_at(lines_for(file), line)
expression_at(lines_for(file), line, options)
rescue SyntaxError => e
raise SourceNotFoundError, "Could not parse source for #{name}: #{e.message}"
end

def self.expression_options
{}
end

# Helper method responsible for opening source file and buffering up
# the comments for a specified method. Defined here to avoid polluting
# `Method` class.
Expand Down Expand Up @@ -158,6 +162,12 @@ def class_comment
end
alias module_comment class_comment
end

module ProcExtensions
def source
MethodSource.source_helper(source_location, defined?(name) ? name : inspect, block: true)
end
end
end

class Method
Expand All @@ -173,5 +183,6 @@ class UnboundMethod
class Proc
include MethodSource::SourceLocation::ProcExtensions
include MethodSource::MethodExtensions
include MethodSource::ProcExtensions
end

30 changes: 20 additions & 10 deletions lib/method_source/code_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,28 @@ def expression_at(file, line_number, options={})

lines = file.is_a?(Array) ? file : file.each_line.to_a

relevant_lines = lines[(line_number - 1)..-1] || []

extract_first_expression(relevant_lines, options[:consume])
rescue SyntaxError => e
raise if options[:strict]

begin
extract_first_expression(relevant_lines) do |code|
code.gsub(/\#\{.*?\}/, "temp")
relevant_lines = lines[(line_number - 1)..-1] || []

extract_first_expression(relevant_lines, options[:consume])
rescue SyntaxError => e
raise if options[:strict]

# blocks may be part of a multiline method call,
# and aren't valid if you cut off previous lines
if options[:block] && line_number > 1
# search backwards until it's valid
line_number -= 1
retry
end

begin
extract_first_expression(relevant_lines) do |code|
code.gsub(/\#\{.*?\}/, "temp")
end
rescue SyntaxError
raise e
end
rescue SyntaxError
raise e
end
end

Expand Down
8 changes: 8 additions & 0 deletions spec/method_source_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
@hello_instance_evaled_source_2 = " def \#{name}_two()\n if 40 + 4\n 45\n end\n end\n"
@hello_class_evaled_source = " def hello_\#{name}(*args)\n send_mesg(:\#{name}, *args)\n end\n"
@hi_module_evaled_source = " def hi_\#{name}\n @var = \#{name}\n end\n"
@multiline_method_block_source = <<~RUBY
MultilineMethodBlock = save_block(
) {}
RUBY
end

it 'should define methods on Method and UnboundMethod and Proc' do
Expand Down Expand Up @@ -123,6 +127,10 @@
it 'should return comment for class instance' do
expect(C.new.method(:hello).class_comment).to eq(@class_comment)
end

it 'should return source for a block from a multiline method' do
expect(MultilineMethodBlock.source).to eq(@multiline_method_block_source)
end
end
# end
describe "Comment tests" do
Expand Down
7 changes: 7 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,10 @@ def #{name}_two()

M.class_eval "def #{name}_three; @tempfile.#{name}; end"

# multiline method call
def save_block(&block)
block
end

MultilineMethodBlock = save_block(
) {}