lib/rails_erd/diagram/graphviz.rb in rails-erd-0.4.3 vs lib/rails_erd/diagram/graphviz.rb in rails-erd-0.4.4
- old
+ new
@@ -38,17 +38,17 @@
# homepage for more information and examples.
# orientation:: The direction of the hierarchy of entities. Either +:horizontal+
# or +:vertical+. Defaults to +horizontal+. The orientation of the
# PDF that is generated depends on the amount of hierarchy
# in your models.
- # title:: The title to add at the top of the diagram. Defaults to
+ # title:: The title to add at the top of the diagram. Defaults to
# <tt>"YourApplication domain model"</tt>.
class Graphviz < Diagram
- NODE_LABEL_TEMPLATE = ERB.new(File.read(File.expand_path("templates/node.erb", File.dirname(__FILE__))), nil, "<>") # @private :nodoc:
+ NODE_LABEL_TEMPLATES = { :html => "node.html.erb", :record => "node.record.erb" } # @private :nodoc:
NODE_WIDTH = 130 # @private :nodoc:
-
+
# Default graph attributes.
GRAPH_ATTRIBUTES = {
:rankdir => :LR,
:ranksep => 0.5,
:nodesep => 0.4,
@@ -76,20 +76,20 @@
:dir => :both,
:arrowsize => 0.9,
:penwidth => 1.0,
:labelangle => 32,
:labeldistance => 1.8,
- :fontsize => 7
+ :fontsize => 7
}
-
+
module Simple
def entity_style(entity, attributes)
{}.tap do |options|
options[:fontcolor] = options[:color] = :grey60 if entity.abstract?
end
end
-
+
def relationship_style(relationship)
{}.tap do |options|
options[:style] = :dotted if relationship.indirect?
# Closed arrows for to/from many.
@@ -100,11 +100,11 @@
def specialization_style(specialization)
{ :color => :grey60, :arrowtail => :onormal, :arrowhead => :none, :arrowsize => 1.2 }
end
end
-
+
module Bachman
include Simple
def relationship_style(relationship)
{}.tap do |options|
options[:style] = :dotted if relationship.indirect?
@@ -119,11 +119,11 @@
options[:arrowsize] = 0.6
options[:arrowhead], options[:arrowtail] = dst, src
end
end
end
-
+
module Uml
include Simple
def relationship_style(relationship)
{}.tap do |options|
options[:style] = :dotted if relationship.indirect?
@@ -141,11 +141,11 @@
end
options[:headlabel], options[:taillabel] = *ranges
end
end
end
-
+
attr_accessor :graph
setup do
self.graph = GraphViz.digraph(domain.name)
@@ -154,97 +154,109 @@
NODE_ATTRIBUTES.each { |attribute, value| graph.node[attribute] = value }
EDGE_ATTRIBUTES.each { |attribute, value| graph.edge[attribute] = value }
# Switch rank direction if we're creating a vertically oriented graph.
graph[:rankdir] = :TB if options.orientation == :vertical
-
+
# Title of the graph itself.
graph[:label] = "#{title}\\n\\n" if title
-
+
# Setup notation options.
extend self.class.const_get(options.notation.to_s.capitalize.to_sym)
end
-
+
save do
- raise "Saving diagram failed. Output directory '#{File.dirname(filename)}' does not exist." unless File.directory?(File.dirname(filename))
+ raise "Saving diagram failed!\nOutput directory '#{File.dirname(filename)}' does not exist." unless File.directory?(File.dirname(filename))
begin
graph.output(filetype => filename)
filename
+ rescue RuntimeError => e
+ raise "Saving diagram failed!\nGraphviz produced errors. Verify it has support for filetype=#{options.filetype}, or use filetype=dot." <<
+ "\nOriginal error: #{e.message.split("\n").last}"
rescue StandardError => e
- raise "Saving diagram failed. Verify that Graphviz is installed or select filetype=dot."
+ raise "Saving diagram failed!\nVerify that Graphviz is installed and in your path, or use filetype=dot."
end
end
each_entity do |entity, attributes|
draw_node entity.name, entity_options(entity, attributes)
end
-
+
each_specialization do |specialization|
from, to = specialization.generalized, specialization.specialized
draw_edge from.name, to.name, specialization_options(specialization)
end
-
+
each_relationship do |relationship|
from, to = relationship.source, relationship.destination
unless draw_edge from.name, to.name, relationship_options(relationship)
if from.children.any?
from.children.each do |child|
draw_edge child.name, to.name, relationship_options(relationship)
end
end
end
end
-
+
private
-
+
def node_exists?(name)
- !!graph.get_node(name)
+ !!graph.get_node(escape_name(name))
end
-
+
def draw_node(name, options)
- graph.add_node name, options
+ graph.add_node escape_name(name), options
end
-
+
def draw_edge(from, to, options)
- graph.add_edge graph.get_node(from), graph.get_node(to), options if node_exists?(from) and node_exists?(to)
+ graph.add_edge graph.get_node(escape_name(from)), graph.get_node(escape_name(to)), options if node_exists?(from) and node_exists?(to)
end
+ def escape_name(name)
+ "m_#{name}"
+ end
+
# Returns the title to be used for the graph.
def title
case options.title
when false then nil
when true
if domain.name then "#{domain.name} domain model" else "Domain model" end
else options.title
end
end
-
+
# Returns the file name that will be used when saving the diagram.
def filename
"#{options.filename}.#{options.filetype}"
end
-
+
# Returns the default file extension to be used when saving the diagram.
def filetype
if options.filetype.to_sym == :dot then :none else options.filetype.to_sym end
end
def entity_options(entity, attributes)
- entity_style(entity, attributes).merge :label => "<#{NODE_LABEL_TEMPLATE.result(binding)}>"
+ label = options[:markup] ? "<#{read_template(:html).result(binding)}>" : "#{read_template(:record).result(binding)}"
+ entity_style(entity, attributes).merge :label => label
end
-
+
def relationship_options(relationship)
relationship_style(relationship).tap do |options|
# Edges with a higher weight are optimised to be shorter and straighter.
options[:weight] = relationship.strength
-
+
# Indirect relationships should not influence node ranks.
options[:constraint] = false if relationship.indirect?
end
end
def specialization_options(specialization)
specialization_style(specialization)
+ end
+
+ def read_template(type)
+ ERB.new(File.read(File.expand_path("templates/#{NODE_LABEL_TEMPLATES[type]}", File.dirname(__FILE__))), nil, "<>")
end
end
end
end