Posted at

Ruby のメソッド、Proc のソースを取り出す

Ruby 2.6 から RubyVM::AbstractSyntaxTree.of が使えるようになりました。これは MethodProc オブジェクトを受け取りその AST を返すものです。

RubyVM::AbstractSyntaxTree.of(-> {1 + 1}) #=> RubyVM::AbstractSyntaxTree::Node

例えばこんな感じでメソッドや Proc のソースを取り出すことが出来るようになります。

module NodeToSource

def to_source
node = RubyVM::AbstractSyntaxTree.of(self)
return nil if node.nil?

path, _lineno = source_location
lines_for_file = File.readlines(path)
extract_source(node: node, lines_for_file: lines_for_file)
end

private

def extract_source(node:, lines_for_file:)
first_lineno = node.first_lineno - 1
first_column = node.first_column
last_lineno = node.last_lineno - 1
last_column = node.last_column - 1

if first_lineno == last_lineno
lines_for_file[first_lineno][first_column..last_column]
else
src = ' ' * first_column + lines_for_file[first_lineno][first_column..]
((first_lineno + 1)...last_lineno).each do |lineno|
src << lines_for_file[lineno]
end
src << lines_for_file[last_lineno][0..last_column]
end
end
end

class Method
include NodeToSource
end

class Proc
include NodeToSource
end

def foo
puts 'hello world'
end

n = Proc.new { |i| i * 3 }

puts method(:foo).to_source
puts n.to_source

実行結果

def foo

puts 'hello world'
end
{ |i| i * 3 }

ただ、C で定義されているメソッドは RubyVM::AbstractSyntaxTree.of(self) の返り値が nil になるためソースは取得出来ません。