Class: RailsERD::Diagram
- Inherits:
-
Object
- Object
- RailsERD::Diagram
- Defined in:
- lib/rails_erd/diagram.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 (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. - 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 exampleexclude_attributes="BigTable,User.password_digest". - 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.
Defined Under Namespace
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::Basesubclasses, and creates a new diagram. -
.normalize_exclude_attributes(value) ⇒ Object
Canonicalises the
exclude_attributesoption into a hash that maps model names (as strings) to eithertrue(hide all attributes) or an array of attribute names to hide.
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.
162 163 164 |
# File 'lib/rails_erd/diagram.rb', line 162 def domain @domain end |
#options ⇒ Object (readonly)
The options that are used to create this diagram.
159 160 161 |
# File 'lib/rails_erd/diagram.rb', line 159 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.
83 84 85 |
# File 'lib/rails_erd/diagram.rb', line 83 def create( = {}) new(Domain.generate(), ).create end |
.normalize_exclude_attributes(value) ⇒ Object
Canonicalises the exclude_attributes option into a hash that maps model names (as strings) to either true (hide all attributes) or an array of attribute names to hide. Accepts several input shapes:
* a hash, e.g. <tt>{ "BigTable" => true, "User" => ["password_digest"] }</tt>
(values may be +true+/+"all"+ to hide every attribute, +false+/+nil+
to hide none, 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>.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/rails_erd/diagram.rb', line 96 def normalize_exclude_attributes(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_exclude_attribute_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 |
Instance Method Details
#create ⇒ Object
Generates and saves the diagram, returning the result of save.
170 171 172 173 |
# File 'lib/rails_erd/diagram.rb', line 170 def create generate save end |
#generate ⇒ Object
Generates the diagram, but does not save the output. It is called internally by Diagram#create.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/rails_erd/diagram.rb', line 177 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
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/rails_erd/diagram.rb', line 202 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
222 223 224 |
# File 'lib/rails_erd/diagram.rb', line 222 def save instance_eval(&callbacks[:save]) end |