Last active
December 26, 2015 23:09
-
-
Save hiroara/7228642 to your computer and use it in GitHub Desktop.
Rubyのメソッド実行をキャプチャしてツリー構造で返すモジュール
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Capturable | |
EXCLUDE_METHODS = [:class, :send, :respond_to?, :capture_methods, :__send__, :__id__] | |
def self.included(base) | |
base.class_eval do | |
def self.capture_methods options={} | |
method_list = options[:method_list] | |
exclude_methods = options[:exclude_methods] || [] | |
exclude_regexp = options[:exclude_regexp] || /\A_/ | |
source_root = case options[:source_root] | |
when :rails then /\A#{Rails.root.to_s}/ | |
else options[:source_root] | |
end | |
if method_list.nil? | |
method_list = (public_instance_methods - EXCLUDE_METHODS).reject do |method_sym| | |
method_sym.to_s =~ exclude_regexp | |
end | |
method_list = method_list - superclass.public_instance_methods unless options[:superclass_methods] | |
end | |
(method_list - exclude_methods).each do |method_sym| | |
orig_method = public_instance_method method_sym | |
next if source_root && (!orig_method.source_location || !orig_method.source_location.first =~ source_root) | |
define_method method_sym, -> (*args, &block) do | |
Capturable.call_method self.class, method_sym, false do | |
orig_method.bind(self).call *args, &block | |
end | |
end | |
end | |
class_method_list = options[:class_method_list] | |
exclude_class_methods = options[:exclude_class_methods] || [] | |
if class_method_list.nil? | |
class_method_list = (public_methods - EXCLUDE_METHODS).reject do |method_sym| | |
method_sym.to_s =~ exclude_regexp | |
end | |
method_list = method_list - superclass.public_methods unless options[:superclass_methods] | |
end | |
(class_method_list - exclude_class_methods).map do |method_sym| | |
orig_method = public_method method_sym | |
next if source_root && (!orig_method.source_location || !orig_method.source_location.first =~ source_root) | |
define_singleton_method method_sym, -> (*args, &block) do | |
Capturable.call_method self, method_sym, true do | |
orig_method.call *args, &block | |
end | |
end | |
end | |
end | |
end | |
end | |
module_function | |
def capture &block | |
@method_head = @tree_root = MethodTree.new self, :capture, true | |
begin | |
block.call | |
ensure | |
tree_root = @tree_root | |
@tree_root = @method_head = nil | |
end | |
tree_root.to_h | |
end | |
def in_capture_block? | |
!!@method_head | |
end | |
def call_method clazz, method_sym, class_method, &block | |
Capturable.visit_method MethodTree.new(clazz, method_sym, class_method) if Capturable.in_capture_block? | |
result = block.call | |
Capturable.leave_method if Capturable.in_capture_block? | |
result | |
end | |
def visit_method method_tree | |
@method_head = @method_head.append_child method_tree | |
end | |
def leave_method | |
@method_head = @method_head.parent | |
end | |
class MethodTree | |
attr_accessor :name, :parent, :children | |
def initialize clazz, name, class_method | |
@clazz = clazz | |
@name = name | |
@children = [] | |
@class_method = class_method | |
end | |
def append_child child | |
@children << child | |
child.parent = self | |
child | |
end | |
def to_h | |
signature = @class_method ? "#{@clazz}.#{@name}" : "#{@clazz}##{@name}" | |
{ signature => @children.map { |child| child.to_h } } | |
end | |
def to_s | |
to_h.to_s | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require './capturable' | |
class Foo | |
include Capturable | |
def foo | |
puts "foo!" | |
Hoge.new.hoge2 | |
self.class.class_foo | |
end | |
def self.class_foo | |
puts "class_foo!" | |
end | |
capture_methods exclude_class_methods: [:new] | |
end | |
class Hoge | |
include Capturable | |
def hoge; Foo.new.foo; end | |
def hoge2; puts "hoge2!"; end | |
capture_methods exclude_class_methods: [:new] | |
end | |
tree = Capturable.capture do | |
Hoge.new.hoge | |
end | |
puts tree |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment