rails-erd 1.7.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsERD
2
4
  class Domain
3
5
  # Entities represent your Active Record models. Entities may be connected
@@ -38,6 +40,10 @@ module RailsERD
38
40
  @domain, @name, @model = domain, name, model
39
41
  end
40
42
 
43
+ def label
44
+ RailsERD.options[:table_names] ? model.table_name : name
45
+ end
46
+
41
47
  # Returns an array of attributes for this entity.
42
48
  def attributes
43
49
  @attributes ||= generalized? ? [] : Attribute.from_model(domain, model)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsERD
2
4
  class Domain
3
5
  class Relationship
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "set"
2
4
  require "active_support/core_ext/module/delegation"
3
5
  require "rails_erd/domain/relationship/cardinality"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsERD
2
4
  class Domain
3
5
  # Describes the specialization of an entity. Specialized entities correspond
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails_erd"
2
4
  require "rails_erd/domain/attribute"
3
5
  require "rails_erd/domain/entity"
@@ -65,7 +67,9 @@ module RailsERD
65
67
 
66
68
  # Returns all relationships in your domain model.
67
69
  def relationships
68
- @relationships ||= Relationship.from_associations(self, associations)
70
+ @relationships ||= Relationship.from_associations(self, associations).select do |relationship|
71
+ relationship.source && relationship.destination
72
+ end
69
73
  end
70
74
 
71
75
  # Returns all specializations in your domain model.
@@ -130,12 +134,26 @@ module RailsERD
130
134
  def rails_models
131
135
  %w(
132
136
  ActionMailbox::InboundEmail
137
+ ActionText::EncryptedRichText
138
+ ActionText::RichText
133
139
  ActiveStorage::Attachment
134
140
  ActiveStorage::Blob
135
141
  ActiveStorage::VariantRecord
136
- ActionText::RichText
137
- ActionText::EncryptedRichText
138
- ).map{ |model| Object.const_get(model) rescue nil }.compact
142
+ SolidCable::Message
143
+ SolidCache::Entry
144
+ SolidQueue::BlockedExecution
145
+ SolidQueue::ClaimedExecution
146
+ SolidQueue::Execution
147
+ SolidQueue::FailedExecution
148
+ SolidQueue::Job
149
+ SolidQueue::Pause
150
+ SolidQueue::Process
151
+ SolidQueue::ReadyExecution
152
+ SolidQueue::RecurringExecution
153
+ SolidQueue::RecurringTask
154
+ SolidQueue::ScheduledExecution
155
+ SolidQueue::Semaphore
156
+ ).map { |model| Object.const_get(model) rescue nil }.compact
139
157
  end
140
158
 
141
159
  def tableless_rails_models
@@ -163,7 +181,13 @@ module RailsERD
163
181
  raise "table #{model.table_name} does not exist"
164
182
  end
165
183
  rescue => e
166
- warn "Ignoring invalid model #{model.name} (#{e.message})"
184
+ warn "Ignoring invalid model #{model.name} (#{e.message})" unless excluded_model?(model)
185
+ end
186
+
187
+ def excluded_model?(model)
188
+ return false unless options.exclude.present?
189
+
190
+ [options.exclude].flatten.map(&:to_sym).include?(model.name.to_sym)
167
191
  end
168
192
 
169
193
  def check_association_validity(association)
@@ -177,7 +201,20 @@ module RailsERD
177
201
  entity_by_name(entity_name) or raise "model #{entity_name} exists, but is not included in domain"
178
202
  end
179
203
  rescue => e
180
- warn "Ignoring invalid association #{association_description(association)} (#{e.message})"
204
+ warn "Ignoring invalid association #{association_description(association)} (#{e.message})" unless excluded_association?(association)
205
+ end
206
+
207
+ def excluded_association?(association)
208
+ return false unless options.exclude.present?
209
+
210
+ excluded_names = [options.exclude].flatten.map(&:to_sym)
211
+
212
+ # Suppress warning if either the source model or target model is excluded
213
+ excluded_names.include?(association.active_record.name.to_sym) ||
214
+ (association.klass.name && excluded_names.include?(association.klass.name.to_sym))
215
+ rescue NameError
216
+ # If we can't determine the target class, check only the source model
217
+ excluded_names.include?(association.active_record.name.to_sym)
181
218
  end
182
219
 
183
220
  def check_polymorphic_association_validity(association)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsERD
2
- # Rails ERD integrates with Rails 3. If you add it to your +Gemfile+, you
4
+ # Rails ERD integrates with Rails. If you add it to your +Gemfile+, you
3
5
  # will gain a Rake task called +erd+, which you can use to generate diagrams
4
6
  # of your domain model.
5
7
  class Railtie < Rails::Railtie
@@ -1,5 +1,3 @@
1
- require 'graphviz/utils'
2
-
3
1
  module ErdRakeHelper
4
2
  def say(message)
5
3
  puts message unless Rake.application.options.silent
@@ -8,10 +6,18 @@ end
8
6
 
9
7
  namespace :erd do
10
8
  task :check_dependencies do
11
- include GraphViz::Utils
12
- unless find_executable("dot", nil)
13
- raise "Unable to find GraphViz's \"dot\" executable. Please " \
14
- "visit https://voormedia.github.io/rails-erd/install.html for installation instructions."
9
+ if RailsERD.options.generator == :graphviz
10
+ begin
11
+ require 'graphviz/utils'
12
+ include GraphViz::Utils
13
+ unless find_executable("dot", nil)
14
+ raise "Unable to find GraphViz's \"dot\" executable. Please " \
15
+ "visit https://voormedia.github.io/rails-erd/install.html for installation instructions."
16
+ end
17
+ rescue LoadError
18
+ raise "The ruby-graphviz gem is required for Graphviz output. " \
19
+ "Add `gem 'ruby-graphviz'` to your Gemfile, or use `generator=mermaid` instead."
20
+ end
15
21
  end
16
22
  end
17
23
 
@@ -58,13 +64,22 @@ namespace :erd do
58
64
  raise "Active Record was not loaded." unless defined? ActiveRecord
59
65
  end
60
66
 
61
- task :generate => [:check_dependencies, :options, :load_models] do
67
+ task :generate => [:options, :check_dependencies, :load_models] do
62
68
  include ErdRakeHelper
63
69
 
64
70
  say "Generating Entity-Relationship Diagram for #{ActiveRecord::Base.descendants.length} models..."
65
71
 
66
- require "rails_erd/diagram/graphviz"
67
- file = RailsERD::Diagram::Graphviz.create
72
+ file = case RailsERD.options.generator
73
+ when :mermaid
74
+ require "rails_erd/diagram/mermaid"
75
+ RailsERD::Diagram::Mermaid.create
76
+ when :graphviz
77
+ require "rails_erd/diagram/graphviz"
78
+ RailsERD::Diagram::Graphviz.create
79
+ else
80
+ raise "Unknown generator: #{RailsERD.options.generator}"
81
+ end
82
+
68
83
 
69
84
  say "Done! Saved diagram to ./#{file}"
70
85
  end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsERD
2
- VERSION = "1.7.2"
4
+ VERSION = "2.0.0"
3
5
  BANNER = "RailsERD #{VERSION}"
4
6
  end
data/lib/rails_erd.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/ordered_options"
2
4
  require "rails_erd/railtie" if defined? Rails
3
5
  require "rails_erd/config"
@@ -36,14 +38,16 @@ module RailsERD
36
38
 
37
39
  def default_options
38
40
  ActiveSupport::OrderedOptions[
41
+ :generator, :mermaid,
39
42
  :attributes, :content,
40
43
  :disconnected, true,
41
44
  :filename, "erd",
42
- :filetype, :pdf,
45
+ :filetype, :mmd,
43
46
  :fonts, {},
44
47
  :indirect, true,
45
48
  :inheritance, false,
46
49
  :markup, true,
50
+ :mermaid_style, :erdiagram,
47
51
  :notation, :simple,
48
52
  :orientation, :horizontal,
49
53
  :polymorphism, false,
@@ -55,6 +59,8 @@ module RailsERD
55
59
  :only_recursion_depth, nil,
56
60
  :prepend_primary, false,
57
61
  :cluster, false,
62
+ :table_names, false,
63
+ :native_types, false
58
64
  ]
59
65
  end
60
66
 
File without changes
@@ -0,0 +1,75 @@
1
+ require File.expand_path("../test_helper", File.dirname(__FILE__))
2
+ require "rails_erd/cli"
3
+
4
+ class CLITest < ActiveSupport::TestCase
5
+ def setup
6
+ RailsERD.options.filetype = :dot
7
+ RailsERD.options.warn = false
8
+ end
9
+
10
+ # Generator selection ========================================================
11
+ test "CLI should use mermaid generator by default" do
12
+ require "rails_erd/diagram/mermaid"
13
+ cli = RailsERD::CLI.new(Dir.pwd, {})
14
+ assert_equal RailsERD::Diagram::Mermaid, cli.send(:generator)
15
+ end
16
+
17
+ test "CLI should use graphviz generator when generator option is graphviz symbol" do
18
+ cli = RailsERD::CLI.new(Dir.pwd, { generator: :graphviz })
19
+ assert_equal RailsERD::Diagram::Graphviz, cli.send(:generator)
20
+ end
21
+
22
+ test "CLI should use mermaid generator when generator option is mermaid symbol" do
23
+ require "rails_erd/diagram/mermaid"
24
+ cli = RailsERD::CLI.new(Dir.pwd, { generator: :mermaid })
25
+ assert_equal RailsERD::Diagram::Mermaid, cli.send(:generator)
26
+ end
27
+
28
+ # Option parsing (SYMBOL_OPTIONS conversion) =================================
29
+ test "CLI start should convert generator string to symbol" do
30
+ # Simulate what Choice.choices returns (strings)
31
+ Choice.stubs(:choices).returns({ "generator" => "mermaid" })
32
+ Choice.stubs(:rest).returns([])
33
+
34
+ # Capture the options passed to new
35
+ captured_options = nil
36
+ RailsERD::CLI.stubs(:new).with do |path, options|
37
+ captured_options = options
38
+ true
39
+ end.returns(stub(start: nil))
40
+
41
+ RailsERD::CLI.start
42
+
43
+ assert_equal :mermaid, captured_options[:generator]
44
+ end
45
+
46
+ test "CLI start should convert mermaid_style string to symbol" do
47
+ Choice.stubs(:choices).returns({ "mermaid_style" => "erdiagram" })
48
+ Choice.stubs(:rest).returns([])
49
+
50
+ captured_options = nil
51
+ RailsERD::CLI.stubs(:new).with do |path, options|
52
+ captured_options = options
53
+ true
54
+ end.returns(stub(start: nil))
55
+
56
+ RailsERD::CLI.start
57
+
58
+ assert_equal :erdiagram, captured_options[:mermaid_style]
59
+ end
60
+
61
+ test "CLI start should convert filetype string to symbol" do
62
+ Choice.stubs(:choices).returns({ "filetype" => "pdf" })
63
+ Choice.stubs(:rest).returns([])
64
+
65
+ captured_options = nil
66
+ RailsERD::CLI.stubs(:new).with do |path, options|
67
+ captured_options = options
68
+ true
69
+ end.returns(stub(start: nil))
70
+
71
+ RailsERD::CLI.start
72
+
73
+ assert_equal :pdf, captured_options[:filetype]
74
+ end
75
+ end
@@ -8,6 +8,13 @@ class ConfigTest < ActiveSupport::TestCase
8
8
  assert_equal expected_hash, RailsERD::Config.load
9
9
  end
10
10
 
11
+ test "load_config_file should return blank hash when config file is empty" do
12
+ set_local_config_file_to("erdconfig.empty")
13
+
14
+ expected_hash = {}
15
+ assert_equal expected_hash, RailsERD::Config.load
16
+ end
17
+
11
18
  test "load_config_gile should return a hash from USER_WIDE_CONFIG_FILE when only USER_WIDE_CONFIG_FILE exists." do
12
19
  set_user_config_file_to("erdconfig.example")
13
20
 
@@ -127,21 +127,16 @@ class DomainTest < ActiveSupport::TestCase
127
127
  end
128
128
 
129
129
  test "relationships should count relationship between same models with distinct foreign key seperately" do
130
- # TODO: Once we drop Rails 3.2 support, we _should_ be able to drop the
131
- # :respond_to? check
132
- #
133
- if respond_to? :skip
134
- skip("multiple edges between the same objects can cause segfaults in some versions of Graphviz")
130
+ skip("multiple edges between the same objects can cause segfaults in some versions of Graphviz")
135
131
 
136
- create_model "Foo", :bar => :references, :special_bar => :references do
137
- belongs_to :bar
138
- end
139
- create_model "Bar" do
140
- has_many :foos, :foreign_key => :special_bar_id
141
- end
142
-
143
- assert_equal [Domain::Relationship] * 2, Domain.generate.relationships.collect(&:class)
132
+ create_model "Foo", :bar => :references, :special_bar => :references do
133
+ belongs_to :bar
144
134
  end
135
+ create_model "Bar" do
136
+ has_many :foos, :foreign_key => :special_bar_id
137
+ end
138
+
139
+ assert_equal [Domain::Relationship] * 2, Domain.generate.relationships.collect(&:class)
145
140
  end
146
141
 
147
142
  test "relationships should use model name first in alphabet as source for many to many relationships" do
@@ -193,7 +193,9 @@ class GraphvizTest < ActiveSupport::TestCase
193
193
 
194
194
  test "generate should add set value for fontname attribute" do
195
195
  create_simple_domain
196
- assert_equal '"Arial Bold"', diagram(fonts: {bold: "Arial Bold"}).graph.graph[:fontname].to_s
196
+ # Use OS-appropriate font name (macOS uses PostScript names like "Arial BoldMT")
197
+ expected_font = RailsERD::Config.font_names_based_on_os[:bold]
198
+ assert_equal "\"#{expected_font}\"", diagram(fonts: {bold: expected_font}).graph.graph[:fontname].to_s
197
199
  end
198
200
 
199
201
  test "generate should add default value for splines attribute" do
@@ -314,21 +316,16 @@ class GraphvizTest < ActiveSupport::TestCase
314
316
  end
315
317
 
316
318
  test "generate should create edge for each relationship" do
317
- # TODO: Once we drop Rails 3.2 support, we _should_ be able to drop the
318
- # :respond_to? check
319
- #
320
- if respond_to? :skip
321
- skip("multiple edges between the same objects can cause segfaults in some versions of Graphviz")
322
-
323
- create_model "Foo", :bar => :references do
324
- belongs_to :bar
325
- end
326
- create_model "Bar", :foo => :references do
327
- belongs_to :foo
328
- end
319
+ skip("multiple edges between the same objects can cause segfaults in some versions of Graphviz")
329
320
 
330
- assert_equal [["m_Bar", "m_Foo"], ["m_Foo", "m_Bar"]], find_dot_node_pairs(diagram).sort
321
+ create_model "Foo", :bar => :references do
322
+ belongs_to :bar
323
+ end
324
+ create_model "Bar", :foo => :references do
325
+ belongs_to :foo
331
326
  end
327
+
328
+ assert_equal [["m_Bar", "m_Foo"], ["m_Foo", "m_Bar"]], find_dot_node_pairs(diagram).sort
332
329
  end
333
330
 
334
331
  test "generate should create edge to polymorphic entity if polymorphism is true" do