class RailsERD::Diagram
This class is an abstract class that will process a domain model and allows easy creation of diagrams. To implement a new diagram type, derive from this class and override process_entity, process_relationship, and (optionally) save.
As an example, a diagram class that generates code that can be used with yUML (yuml.me) can be as simple as:
require "rails_erd/diagram" class YumlDiagram < RailsERD::Diagram setup { @edges = [] } each_relationship do |relationship| return if relationship.indirect? arrow = case when relationship.one_to_one? then "1-1>" when relationship.one_to_many? then "1-*>" when relationship.many_to_many? then "*-*>" end @edges << "[#{relationship.source}] #{arrow} [#{relationship.destination}]" end save { @edges * "\n" } end
Then, to generate the diagram (example based on the domain model of Gemcutter):
YumlDiagram.create #=> "[Rubygem] 1-*> [Ownership] # [Rubygem] 1-*> [Subscription] # [Rubygem] 1-*> [Version] # [Rubygem] 1-1> [Linkset] # [Rubygem] 1-*> [Dependency] # [Version] 1-*> [Dependency] # [User] 1-*> [Ownership] # [User] 1-*> [Subscription] # [User] 1-*> [WebHook]"
For another example implementation, see Diagram::Graphviz, which is the default (and currently only) diagram type that is used by Rails ERD.
Options¶ ↑
The following options are available and will by automatically used by any diagram generator inheriting from this class.
- attributes
-
Selects which attributes to display. Can be any combination of
:content,:primary_keys,:foreign_keys,:timestamps, or:inheritance. - disconnected
-
Set to
falseto exclude entities that are not connected to other entities. Defaults tofalse. - indirect
-
Set to
falseto exclude relationships that are indirect. Indirect relationships are defined in Active Record withhas_many :throughassociations. - inheritance
-
Set to
trueto include specializations, which correspond to Rails single table inheritance. - polymorphism
-
Set to
trueto include generalizations, which correspond to Rails polymorphic associations. - warn
-
When set to
false, no warnings are printed to the command line while processing the domain model. Defaults totrue.
Attributes
The domain that this diagram represents.
The options that are used to create this diagram.
Public Class Methods
Generates a new domain model based on all ActiveRecord::Base subclasses, and creates a new diagram. Use the given options for both the domain generation and the diagram generation.
# File lib/rails_erd/diagram.rb, line 73 def create(options = {}) new(Domain.generate(options), options).create end
Create a new diagram based on the given domain.
# File lib/rails_erd/diagram.rb, line 113 def initialize(domain, options = {}) @domain, @options = domain, RailsERD.options.merge(options) end
Protected Class Methods
# File lib/rails_erd/diagram.rb, line 83 def each_entity(&block) callbacks[:each_entity] = block end
# File lib/rails_erd/diagram.rb, line 87 def each_relationship(&block) callbacks[:each_relationship] = block end
# File lib/rails_erd/diagram.rb, line 91 def each_specialization(&block) callbacks[:each_specialization] = block end
# File lib/rails_erd/diagram.rb, line 95 def save(&block) callbacks[:save] = block end
# File lib/rails_erd/diagram.rb, line 79 def setup(&block) callbacks[:setup] = block end
Private Class Methods
# File lib/rails_erd/diagram.rb, line 101 def callbacks @callbacks ||= Hash.new { proc {} } end
Public Instance Methods
Generates and saves the diagram, returning the result of save.
# File lib/rails_erd/diagram.rb, line 118 def create generate save end
Generates the diagram, but does not save the output. It is called internally by Diagram#create.
# File lib/rails_erd/diagram.rb, line 125 def generate instance_eval(&callbacks[:setup]) if options.only_recursion_depth.present? depth = options.only_recursion_depth.to_s.to_i options[:only].dup.each do |class_name| options[:only]+= recurse_into_relationships(@domain.entity_by_name(class_name), depth) end options[:only].uniq! end filtered_entities.each do |entity| instance_exec entity, filtered_attributes(entity), &callbacks[:each_entity] end filtered_specializations.each do |specialization| instance_exec specialization, &callbacks[:each_specialization] end filtered_relationships.each do |relationship| instance_exec relationship, &callbacks[:each_relationship] end end
# File lib/rails_erd/diagram.rb, line 148 def recurse_into_relationships(entity, max_level, current_level = 0) return [] unless entity return [] if max_level == current_level relationships = entity.relationships.reject{|r| r.indirect? || r.recursive?} relationships.map do |relationship| other_entitiy = if relationship.source == entity relationship.destination else relationship.source end if other_entitiy and !other_entitiy.generalized? [other_entitiy.name] + recurse_into_relationships(other_entitiy, max_level, current_level + 1) else [] end end.flatten.uniq end
# File lib/rails_erd/diagram.rb, line 168 def save instance_eval(&callbacks[:save]) end
Private Instance Methods
# File lib/rails_erd/diagram.rb, line 174 def callbacks @callbacks ||= self.class.send(:callbacks) end
# File lib/rails_erd/diagram.rb, line 203 def filtered_attributes(entity) entity.attributes.reject { |attribute| # Select attributes that satisfy the conditions in the :attributes option. !options.attributes or entity.specialized? or [*options.attributes].none? { |type| attribute.send(:"#{type.to_s.chomp('s')}?") } } end
# File lib/rails_erd/diagram.rb, line 178 def filtered_entities @domain.entities.reject { |entity| options.exclude.present? && [options.exclude].flatten.map(&:to_sym).include?(entity.name.to_sym) or options[:only].present? && entity.model && ![options[:only]].flatten.map(&:to_sym).include?(entity.name.to_sym) or !options.inheritance && entity.specialized? or !options.polymorphism && entity.generalized? or !options.disconnected && entity.disconnected? }.compact.tap do |entities| raise "No entities found; create your models first!" if entities.empty? end end
# File lib/rails_erd/diagram.rb, line 190 def filtered_relationships @domain.relationships.reject { |relationship| !options.indirect && relationship.indirect? } end
# File lib/rails_erd/diagram.rb, line 196 def filtered_specializations @domain.specializations.reject { |specialization| !options.inheritance && specialization.inheritance? or !options.polymorphism && specialization.polymorphic? } end
# File lib/rails_erd/diagram.rb, line 211 def warn(message) puts "Warning: #{message}" if options.warn end