Skip to content

Instantly share code, notes, and snippets.

@deepak
Last active August 29, 2015 13:57
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 deepak/9717938 to your computer and use it in GitHub Desktop.
Save deepak/9717938 to your computer and use it in GitHub Desktop.
analyse dependencies in a ruby file ie. constants
require 'parser/current'
require 'pp'
require 'pry'
require 'graph'
module SexpLisper
def self.parse(path)
code = File.read(path)
source_buffer = Parser::Source::Buffer.new(path, 1)
source_buffer.source = code
builder = SexpLisper::Builder.new
parser = Parser::CurrentRuby.new(builder)
parser.diagnostics.all_errors_are_fatal = true
parser.diagnostics.ignore_warnings = true
ast, comments = parser.parse_with_comments(source_buffer)
[ast, comments]
rescue Parser::SyntaxError
[nil, nil]
end
class Builder < ::Parser::Builders::Default
def n(type, children, location)
return Node.new(type, children, :location => location)
end
end # Builder
class Node < ::Parser::AST::Node
def line
return location.expression.line if location
end
def column
return location.expression.column + 1 if location
end
def file
return location.expression.source_buffer.name if location
end
end # Node
end
class Analysis < Parser::AST::Processor
attr_accessor :report
def initialize report
@report = report
end
private
def log(node, type)
@report[node.file] ||= []
@report[node.file] << {
line: node.line,
value: node.children[0],
type: type
}
end
end
class ConstAnalyzer < Analysis
attr_accessor :current_class
def initialize current_class, report
super(report)
@current_class = current_class
end
def on_const node
log node if has_dependency? node
end
private
def const_name node
node.children.last
end
def has_dependency? node
current_class != const_name(node)
end
def log node
report[current_class] ||= []
report[current_class] << { line: node.line, value: const_name(node), type: :dependency }
end
end
class ClassAnalyzer < Analysis
attr_accessor :current_class
def on_class node
current_class = const_name(node)
def_check = ConstAnalyzer.new(current_class, report)
def_check.process(node)
end
private
def const_name node
node.children[0].children[1]
end
end
if $0 == __FILE__
path = ARGV[0]
ast, comments = SexpLisper.parse(path)
return 'NA' unless ast
report = {}
dependency_check = ClassAnalyzer.new(report)
dependency_check.process(ast)
pp report
digraph do
report.each do |klass, dependencies|
dependencies.each do |dependency|
edge klass.to_s, dependency[:value].to_s
end
end
save "dependency_analysis", "png"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment