Skip to content

Instantly share code, notes, and snippets.

@thegedge
Created September 4, 2020 20:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thegedge/ead846d014257fbbd636e6762ac369bb to your computer and use it in GitHub Desktop.
Save thegedge/ead846d014257fbbd636e6762ac369bb to your computer and use it in GitHub Desktop.
Group methods together that are called together
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# Output a grouping of methods (within a file) such that for each group G, and any method m ∈ G:
# - ∃ x ∈ G such that x calls m, or
# - no other method in all groups calls m
#
# The output is a kind of "function cohesion", where you can find groups of methods that are related.
# This is useful when trying to find what you need to extract from a file.
#
require "set"
def walk(node, call_graph: {}, current_method: nil)
return call_graph unless node.is_a?(::RubyVM::AbstractSyntaxTree::Node)
called_method = nil
case node.type
when :DEFN
current_method = node.children[0]
when :CALL
called_method = node.children[1]
when :FCALL
called_method = node.children[0]
when :QCALL
called_method = node.children[1]
when :VCALL
called_method = node.children[0]
end
if called_method && current_method
call_graph[current_method] ||= Set.new
call_graph[current_method] << called_method
end
node.children.each do |child|
walk(child, call_graph: call_graph, current_method: current_method)
end
call_graph
end
call_graph = walk(::RubyVM::AbstractSyntaxTree.parse(ARGF.read))
result = []
call_graph.each do |method, called_methods|
called_methods = called_methods.filter { |m| call_graph.key?(m) }
existing = result.find { |methods| methods.include?(method) }
if existing
existing += called_methods
else
result << ([method] + called_methods)
end
end
pp result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment