Class: RailsERD::Diagram
- Inherits:
-
Object
- Object
- RailsERD::Diagram
- Defined in:
- lib/rails_erd/diagram.rb,
lib/rails_erd/diagram/tbls.rb,
lib/rails_erd/diagram/mermaid.rb,
lib/rails_erd/diagram/graphviz.rb
Overview
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 (https://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.- only_attributes
Shows only the listed attributes for the given models, without affecting other models. Accepts a hash that maps model names to a list of attribute names to keep. From the command line it can also be given as a comma separated string of
Model.attributeentries, for example only_attributes="Book.title,Book.isbn". Models that are not listed keep all of their attributes. Unlikeexclude_attributesit cannot be set totrue.- exclude_attributes
Hides attributes on a per-model basis, without affecting other models. Accepts a hash that maps model names to either
true(hide all attributes for that model) or a list of attribute names to hide. From the command line it can also be given as a comma separated string where each entry is eitherModel(hide all attributes) orModel.attribute(hide a single attribute), for example exclude_attributes="BigTable,User.password_digest". Applied afteronly_attributes.- 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 with has_many :through associations.- 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.
Defined Under Namespace
Classes: Graphviz, Mermaid, Tbls
Instance Attribute Summary collapse
-
#domain ⇒ Object
readonly
The domain that this diagram represents.
-
#options ⇒ Object
readonly
The options that are used to create this diagram.
Class Method Summary collapse
-
.create(options = {}) ⇒ Object
Generates a new domain model based on all ActiveRecord::Base subclasses, and creates a new diagram.
-
.normalize_attribute_filter(value) ⇒ Object
Canonicalises an attribute filter option (+only_attributes+ or
exclude_attributes) into a hash that maps model names (as strings) to eithertrue(the whole model was named, without any attribute) or an array of attribute names. -
.normalize_exclude_attributes(value) ⇒ Object
Canonicalises the
exclude_attributesoption. -
.normalize_only_attributes(value) ⇒ Object
Canonicalises the
only_attributesoption.
Instance Method Summary collapse
-
#create ⇒ Object
Generates and saves the diagram, returning the result of
save. -
#generate ⇒ Object
Generates the diagram, but does not save the output.
-
#initialize(domain, options = {}) ⇒ Diagram
constructor
Create a new diagram based on the given domain.
- #recurse_into_relationships(entity, max_level, current_level = 0) ⇒ Object
- #save ⇒ Object
Constructor Details
Instance Attribute Details
#domain ⇒ Object (readonly)
The domain that this diagram represents.
195 196 197 |
# File 'lib/rails_erd/diagram.rb', line 195 def domain @domain end |
#options ⇒ Object (readonly)
The options that are used to create this diagram.
192 193 194 |
# File 'lib/rails_erd/diagram.rb', line 192 def @options end |
Class Method Details
.create(options = {}) ⇒ Object
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.
92 93 94 |
# File 'lib/rails_erd/diagram.rb', line 92 def create( = {}) new(Domain.generate(), ).create end |
.normalize_attribute_filter(value) ⇒ Object
Canonicalises an attribute filter option (+only_attributes+ or
exclude_attributes) into a hash that maps model names (as strings) to
either true (the whole model was named, without any attribute) or an
array of attribute names. Accepts several input shapes:
* a hash, e.g. <tt>{ "BigTable" => true, "User" => ["password_digest"] }</tt>
(values may be +true+/+"all"+ to name the whole model, +false+/+nil+
to name no attributes, or a comma separated string / array of names);
* a string or array of <tt>Model</tt> / <tt>Model.attribute</tt>
entries, e.g. <tt>"BigTable,User.password_digest"</tt>.
How true and an empty list are interpreted is up to the caller: for
exclude_attributes they mean "hide every attribute" and "hide none",
for only_attributes they both mean "keep every attribute".
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/rails_erd/diagram.rb', line 110 def normalize_attribute_filter(value) return {} if value.nil? || value == false if value.is_a?(Hash) value.each_with_object({}) do |(model, attrs), result| result[model.to_s] = normalize_attribute_filter_value(attrs) end else entries = Array(value).flat_map { |entry| entry.to_s.split(",") } entries.each_with_object({}) do |entry, result| model, attribute = entry.split(".", 2) model = model.to_s.strip next if model.empty? if attribute.nil? result[model] = true elsif result[model] != true (result[model] ||= []) << attribute.strip end end end end |
.normalize_exclude_attributes(value) ⇒ Object
Canonicalises the exclude_attributes option. See #normalize_attribute_filter.
148 149 150 |
# File 'lib/rails_erd/diagram.rb', line 148 def normalize_exclude_attributes(value) normalize_attribute_filter(value) end |
.normalize_only_attributes(value) ⇒ Object
Canonicalises the only_attributes option. See #normalize_attribute_filter.
Unlike exclude_attributes it cannot be enabled with a bare true:
"show only all attributes" has no meaning, so it is rejected rather than
silently ignored.
137 138 139 140 141 142 143 144 145 |
# File 'lib/rails_erd/diagram.rb', line 137 def normalize_only_attributes(value) if value == true raise ArgumentError, "only_attributes cannot be true: it takes the attributes to keep, " \ "either as a hash of model names to attribute names, or as a list of Model.attribute " \ "entries. Use attributes: false or exclude_attributes to hide attributes." end normalize_attribute_filter(value) end |
Instance Method Details
#create ⇒ Object
Generates and saves the diagram, returning the result of save.
203 204 205 206 |
# File 'lib/rails_erd/diagram.rb', line 203 def create generate save end |
#generate ⇒ Object
Generates the diagram, but does not save the output. It is called internally by Diagram#create.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/rails_erd/diagram.rb', line 210 def generate instance_eval(&callbacks[:setup]) if .only_recursion_depth.present? depth = .only_recursion_depth.to_s.to_i # Ensure options[:only] is an array (CLI may pass a single string) [:only] = [[:only]].flatten [:only].dup.each do |class_name| [:only] += recurse_into_relationships(@domain.entity_by_name(class_name), depth) end [: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 |
#recurse_into_relationships(entity, max_level, current_level = 0) ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/rails_erd/diagram.rb', line 235 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 |
#save ⇒ Object
255 256 257 |
# File 'lib/rails_erd/diagram.rb', line 255 def save instance_eval(&callbacks[:save]) end |