2010-08-13 21:05:38 -04:00
|
|
|
module Arel
|
|
|
|
module Visitors
|
|
|
|
class Dot
|
|
|
|
class Node # :nodoc:
|
|
|
|
attr_accessor :name, :id, :fields
|
|
|
|
|
|
|
|
def initialize name, id, fields = []
|
|
|
|
@name = name
|
|
|
|
@id = id
|
|
|
|
@fields = fields
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Edge < Struct.new :name, :from, :to # :nodoc:
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@nodes = []
|
|
|
|
@edges = []
|
|
|
|
@node_stack = []
|
|
|
|
@edge_stack = []
|
|
|
|
@seen = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def accept object
|
|
|
|
visit object
|
|
|
|
to_dot
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2010-09-07 18:10:27 -04:00
|
|
|
def visit_Arel_Nodes_TableAlias o
|
|
|
|
visit_edge o, "name"
|
|
|
|
visit_edge o, "relation"
|
|
|
|
visit_edge o, "columns"
|
|
|
|
end
|
|
|
|
|
2010-09-06 21:07:36 -04:00
|
|
|
def visit_Arel_Nodes_Count o
|
|
|
|
visit_edge o, "expressions"
|
|
|
|
visit_edge o, "distinct"
|
|
|
|
end
|
|
|
|
|
2010-09-12 20:49:08 -04:00
|
|
|
def visit_Arel_Nodes_On o
|
|
|
|
visit_edge o, "expr"
|
|
|
|
end
|
|
|
|
|
2010-08-24 20:59:03 -04:00
|
|
|
def visit_Arel_Nodes_StringJoin o
|
|
|
|
visit_edge o, "left"
|
|
|
|
visit_edge o, "right"
|
2010-09-12 20:49:08 -04:00
|
|
|
visit_edge o, "constraint"
|
2010-08-24 20:59:03 -04:00
|
|
|
end
|
2010-09-07 18:10:27 -04:00
|
|
|
alias :visit_Arel_Nodes_InnerJoin :visit_Arel_Nodes_StringJoin
|
2010-09-07 18:13:44 -04:00
|
|
|
alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_StringJoin
|
2010-08-24 20:59:03 -04:00
|
|
|
|
2010-08-16 18:46:53 -04:00
|
|
|
def visit_Arel_Nodes_InsertStatement o
|
|
|
|
visit_edge o, "relation"
|
|
|
|
visit_edge o, "columns"
|
|
|
|
visit_edge o, "values"
|
|
|
|
end
|
|
|
|
|
2010-08-13 21:28:38 -04:00
|
|
|
def visit_Arel_Nodes_SelectCore o
|
|
|
|
visit_edge o, "froms"
|
|
|
|
visit_edge o, "projections"
|
|
|
|
visit_edge o, "wheres"
|
|
|
|
end
|
|
|
|
|
|
|
|
def visit_Arel_Nodes_SelectStatement o
|
|
|
|
visit_edge o, "cores"
|
|
|
|
visit_edge o, "limit"
|
2010-09-14 14:21:50 -04:00
|
|
|
visit_edge o, "orders"
|
2010-08-13 21:28:38 -04:00
|
|
|
end
|
|
|
|
|
2010-08-16 22:59:52 -04:00
|
|
|
def visit_Arel_Nodes_UpdateStatement o
|
|
|
|
visit_edge o, "relation"
|
|
|
|
visit_edge o, "wheres"
|
|
|
|
visit_edge o, "values"
|
|
|
|
end
|
|
|
|
|
2010-08-13 21:05:38 -04:00
|
|
|
def visit_Arel_Table o
|
|
|
|
visit_edge o, "name"
|
|
|
|
end
|
|
|
|
|
|
|
|
def visit_Arel_Attribute o
|
|
|
|
visit_edge o, "relation"
|
|
|
|
visit_edge o, "name"
|
|
|
|
end
|
|
|
|
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
|
|
|
|
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
|
|
|
|
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
|
2010-08-16 18:46:53 -04:00
|
|
|
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
|
2010-09-08 16:44:34 -04:00
|
|
|
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
|
2010-08-13 21:05:38 -04:00
|
|
|
|
2010-08-16 22:34:07 -04:00
|
|
|
def visit_Arel_Nodes_Equality o
|
|
|
|
visit_edge o, "left"
|
|
|
|
visit_edge o, "right"
|
|
|
|
end
|
2010-09-14 08:26:01 -04:00
|
|
|
alias :visit_Arel_Nodes_And :visit_Arel_Nodes_Equality
|
|
|
|
alias :visit_Arel_Nodes_Or :visit_Arel_Nodes_Equality
|
|
|
|
alias :visit_Arel_Nodes_NotEqual :visit_Arel_Nodes_Equality
|
|
|
|
alias :visit_Arel_Nodes_GreaterThan :visit_Arel_Nodes_Equality
|
|
|
|
alias :visit_Arel_Nodes_GreaterThanOrEqual :visit_Arel_Nodes_Equality
|
|
|
|
alias :visit_Arel_Nodes_Assignment :visit_Arel_Nodes_Equality
|
2010-08-16 22:34:07 -04:00
|
|
|
|
2010-08-13 21:05:38 -04:00
|
|
|
def visit_String o
|
|
|
|
@node_stack.last.fields << o
|
|
|
|
end
|
|
|
|
alias :visit_Time :visit_String
|
|
|
|
alias :visit_NilClass :visit_String
|
2010-08-16 18:46:53 -04:00
|
|
|
alias :visit_TrueClass :visit_String
|
2010-09-10 16:26:30 -04:00
|
|
|
alias :visit_FalseClass :visit_String
|
2010-08-16 17:09:48 -04:00
|
|
|
alias :visit_Arel_SqlLiteral :visit_String
|
|
|
|
alias :visit_Fixnum :visit_String
|
2010-08-16 22:29:35 -04:00
|
|
|
alias :visit_Symbol :visit_String
|
2010-09-12 18:45:59 -04:00
|
|
|
alias :visit_Arel_Nodes_SqlLiteral :visit_String
|
2010-08-13 21:05:38 -04:00
|
|
|
|
|
|
|
def visit_Hash o
|
2010-08-13 21:28:38 -04:00
|
|
|
o.each_with_index do |pair, i|
|
|
|
|
edge("pair_#{i}") { visit pair }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def visit_Array o
|
|
|
|
o.each_with_index do |x,i|
|
|
|
|
edge(i) { visit x }
|
2010-08-13 21:05:38 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def visit_edge o, method
|
|
|
|
edge(method) { visit o.send(method) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def visit o
|
|
|
|
if node = @seen[o.object_id]
|
|
|
|
@edge_stack.last.to = node
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
node = Node.new(o.class.name, o.object_id)
|
|
|
|
@seen[node.id] = node
|
|
|
|
@nodes << node
|
|
|
|
with_node node do
|
|
|
|
send "visit_#{o.class.name.gsub('::', '_')}", o
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def edge name
|
|
|
|
edge = Edge.new(name, @node_stack.last)
|
|
|
|
@edge_stack.push edge
|
|
|
|
@edges << edge
|
|
|
|
yield
|
|
|
|
@edge_stack.pop
|
|
|
|
end
|
|
|
|
|
|
|
|
def with_node node
|
|
|
|
if edge = @edge_stack.last
|
|
|
|
edge.to = node
|
|
|
|
end
|
|
|
|
|
|
|
|
@node_stack.push node
|
|
|
|
yield
|
|
|
|
@node_stack.pop
|
|
|
|
end
|
|
|
|
|
2010-08-16 17:09:48 -04:00
|
|
|
def quote string
|
|
|
|
string.to_s.gsub('"', '\"')
|
|
|
|
end
|
|
|
|
|
2010-08-13 21:05:38 -04:00
|
|
|
def to_dot
|
|
|
|
"digraph \"ARel\" {\nnode [width=0.375,height=0.25,shape=record];\n" +
|
|
|
|
@nodes.map { |node|
|
|
|
|
label = "<f0>#{node.name}"
|
|
|
|
|
|
|
|
node.fields.each_with_index do |field, i|
|
2010-08-16 17:09:48 -04:00
|
|
|
label << "|<f#{i + 1}>#{quote field}"
|
2010-08-13 21:05:38 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
"#{node.id} [label=\"#{label}\"];"
|
|
|
|
}.join("\n") + "\n" + @edges.map { |edge|
|
|
|
|
"#{edge.from.id} -> #{edge.to.id} [label=\"#{edge.name}\"];"
|
|
|
|
}.join("\n") + "\n}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|