lagoon 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/Appraisals +39 -0
- data/CHANGELOG.md +14 -2
- data/README.md +111 -146
- data/Rakefile +17 -3
- data/exe/lagoon +3 -3
- data/gemfiles/adapters.gemfile +15 -0
- data/gemfiles/rails_6.1.gemfile +23 -0
- data/gemfiles/rails_7.0.gemfile +23 -0
- data/gemfiles/rails_7.1.gemfile +23 -0
- data/gemfiles/rails_7.2.gemfile +23 -0
- data/gemfiles/rails_8.0.gemfile +23 -0
- data/gemfiles/rails_8.1.gemfile +23 -0
- data/lib/lagoon/analyzer/action_controller_analyzer.rb +76 -0
- data/lib/lagoon/analyzer/active_record_analyzer.rb +202 -0
- data/lib/lagoon/analyzer/ast/controller_scope_collector.rb +104 -0
- data/lib/lagoon/analyzer/ast/method_reference_visitor.rb +170 -0
- data/lib/lagoon/analyzer/ast_model_reference_analyzer.rb +44 -0
- data/lib/lagoon/analyzer/database_schema_analyzer.rb +83 -0
- data/lib/lagoon/cli.rb +109 -80
- data/lib/lagoon/configuration.rb +42 -6
- data/lib/lagoon/diagram/base.rb +48 -8
- data/lib/lagoon/diagram/controller_diagram.rb +10 -16
- data/lib/lagoon/diagram/controller_model_diagram.rb +28 -0
- data/lib/lagoon/diagram/er_diagram.rb +10 -16
- data/lib/lagoon/diagram/model_diagram.rb +13 -16
- data/lib/lagoon/errors.rb +9 -0
- data/lib/lagoon/options.rb +182 -0
- data/lib/lagoon/parser/application_class_filter.rb +47 -0
- data/lib/lagoon/parser/controller_model_parser.rb +196 -0
- data/lib/lagoon/parser/controller_parser.rb +37 -54
- data/lib/lagoon/parser/model_parser.rb +57 -112
- data/lib/lagoon/parser/schema_parser.rb +120 -67
- data/lib/lagoon/railtie.rb +1 -1
- data/lib/lagoon/renderer/base_renderer.rb +49 -19
- data/lib/lagoon/renderer/class_diagram_renderer.rb +37 -31
- data/lib/lagoon/renderer/controller_model_er_renderer.rb +47 -0
- data/lib/lagoon/renderer/er_diagram_renderer.rb +43 -42
- data/lib/lagoon/result.rb +22 -0
- data/lib/lagoon/version.rb +1 -1
- data/lib/lagoon.rb +70 -21
- data/lib/tasks/lagoon.rake +15 -7
- data/sig/lagoon.rbs +107 -0
- metadata +57 -7
|
@@ -3,61 +3,63 @@
|
|
|
3
3
|
module Lagoon
|
|
4
4
|
module Renderer
|
|
5
5
|
class ClassDiagramRenderer < BaseRenderer
|
|
6
|
+
def initialize(direction: 'TB', show_types: true)
|
|
7
|
+
super(direction: direction)
|
|
8
|
+
@show_types = show_types
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
def render(parsed_data)
|
|
7
|
-
output = [
|
|
12
|
+
output = ['classDiagram']
|
|
8
13
|
output << " direction #{@direction}"
|
|
9
|
-
output <<
|
|
14
|
+
output << ''
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
classes = parsed_data.fetch(:classes, []).select { |klass| klass[:name] }
|
|
17
|
+
classes.sort_by { |klass| klass[:name].to_s }.each do |klass|
|
|
13
18
|
output << render_class(klass)
|
|
19
|
+
output << ''
|
|
14
20
|
end
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# 関係性を追加
|
|
19
|
-
parsed_data[:relationships].each do |rel|
|
|
22
|
+
relationships = parsed_data.fetch(:relationships, []).select { |rel| rel[:source] && rel[:target] }
|
|
23
|
+
relationships.sort_by { |rel| relationship_sort_key(rel) }.each do |rel|
|
|
20
24
|
output << render_relationship(rel)
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
output.join("\n")
|
|
27
|
+
output.join("\n").rstrip
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
private
|
|
27
31
|
|
|
28
32
|
def render_class(klass)
|
|
29
33
|
lines = []
|
|
30
|
-
class_name =
|
|
34
|
+
class_name = aliased_identifier(klass[:name])
|
|
31
35
|
|
|
32
36
|
lines << " class #{class_name} {"
|
|
33
|
-
lines <<
|
|
37
|
+
lines << ' <<abstract>>' if klass[:abstract]
|
|
34
38
|
|
|
35
|
-
# 属性を追加
|
|
36
39
|
if klass[:attributes]&.any?
|
|
37
|
-
klass[:attributes].each do |attr|
|
|
38
|
-
visibility = attr[:visibility] ||
|
|
40
|
+
klass[:attributes].sort_by { |attr| attr[:name].to_s }.each do |attr|
|
|
41
|
+
visibility = attr[:visibility] || '+'
|
|
39
42
|
type = type_to_mermaid(attr[:type])
|
|
40
|
-
|
|
43
|
+
prefix = @show_types ? "#{type} " : ''
|
|
44
|
+
lines << " #{visibility}#{prefix}#{escape_member(attr[:name])}"
|
|
41
45
|
end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
|
-
# メソッドを追加
|
|
45
48
|
if klass[:methods]&.any?
|
|
46
|
-
klass[:methods].each do |method|
|
|
47
|
-
visibility = method[:visibility] ||
|
|
48
|
-
return_type = method[:return_type] ? " #{type_to_mermaid(method[:return_type])}" :
|
|
49
|
-
lines << " #{visibility}#{method[:name]}()#{return_type}"
|
|
49
|
+
klass[:methods].sort_by { |method| [method[:visibility].to_s, method[:name].to_s] }.each do |method|
|
|
50
|
+
visibility = method[:visibility] || '+'
|
|
51
|
+
return_type = method[:return_type] ? " #{type_to_mermaid(method[:return_type])}" : ''
|
|
52
|
+
lines << " #{visibility}#{escape_member(method[:name])}()#{return_type}"
|
|
50
53
|
end
|
|
51
54
|
end
|
|
52
55
|
|
|
53
|
-
lines <<
|
|
54
|
-
lines << ""
|
|
56
|
+
lines << ' }'
|
|
55
57
|
lines.join("\n")
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
def render_relationship(rel)
|
|
59
|
-
source =
|
|
60
|
-
target =
|
|
61
|
+
source = safe_identifier(rel[:source])
|
|
62
|
+
target = safe_identifier(rel[:target])
|
|
61
63
|
type = rel[:type]
|
|
62
64
|
label = rel[:label]
|
|
63
65
|
source_cardinality = rel[:source_cardinality]
|
|
@@ -65,17 +67,17 @@ module Lagoon
|
|
|
65
67
|
|
|
66
68
|
arrow = case type
|
|
67
69
|
when :inheritance
|
|
68
|
-
|
|
70
|
+
'<|--'
|
|
69
71
|
when :composition
|
|
70
|
-
|
|
72
|
+
'*--'
|
|
71
73
|
when :aggregation
|
|
72
|
-
|
|
74
|
+
'o--'
|
|
73
75
|
when :association
|
|
74
|
-
|
|
76
|
+
'-->'
|
|
75
77
|
when :dependency
|
|
76
|
-
|
|
78
|
+
'..>'
|
|
77
79
|
else
|
|
78
|
-
|
|
80
|
+
'--'
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
line = " #{source}"
|
|
@@ -83,9 +85,13 @@ module Lagoon
|
|
|
83
85
|
line += " #{arrow} "
|
|
84
86
|
line += "\"#{target_cardinality}\" " if target_cardinality
|
|
85
87
|
line += target
|
|
86
|
-
line += " : #{label}" if label
|
|
88
|
+
line += " : #{escape_label(label)}" if label && !label.to_s.empty?
|
|
87
89
|
line
|
|
88
90
|
end
|
|
91
|
+
|
|
92
|
+
def relationship_sort_key(relationship)
|
|
93
|
+
%i[source target type label].map { |key| relationship[key].to_s }
|
|
94
|
+
end
|
|
89
95
|
end
|
|
90
96
|
end
|
|
91
97
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lagoon
|
|
4
|
+
module Renderer
|
|
5
|
+
class ControllerModelErRenderer < BaseRenderer
|
|
6
|
+
def initialize(options = {})
|
|
7
|
+
super(direction: options[:direction] || 'TB')
|
|
8
|
+
@show_actions = options.fetch(:show_actions, true)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def render(parsed_data)
|
|
12
|
+
output = ['classDiagram', " direction #{@direction}", '']
|
|
13
|
+
|
|
14
|
+
relationships = parsed_data[:relationships] || []
|
|
15
|
+
return output.join("\n").rstrip if relationships.empty?
|
|
16
|
+
|
|
17
|
+
render_classes(output, relationships)
|
|
18
|
+
output << ''
|
|
19
|
+
|
|
20
|
+
relationships.sort_by { |rel| [rel[:controller].to_s, rel[:model].to_s] }.each do |rel|
|
|
21
|
+
output << render_relationship(rel)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
output.join("\n").rstrip
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def render_relationship(rel)
|
|
30
|
+
controller = safe_identifier(rel[:controller])
|
|
31
|
+
model = safe_identifier(rel[:model])
|
|
32
|
+
actions = rel[:actions] || []
|
|
33
|
+
label = @show_actions && actions.any? ? " : #{escape_label(actions.sort.join(', '))}" : ''
|
|
34
|
+
|
|
35
|
+
" #{controller} ..> #{model}#{label}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render_classes(output, relationships)
|
|
39
|
+
controllers = relationships.filter_map { |rel| rel[:controller] }.uniq.sort
|
|
40
|
+
models = relationships.filter_map { |rel| rel[:model] }.uniq.sort
|
|
41
|
+
|
|
42
|
+
controllers.each { |name| output << " class #{aliased_identifier(name)}" }
|
|
43
|
+
models.each { |name| output << " class #{aliased_identifier(name)}" }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -4,101 +4,102 @@ module Lagoon
|
|
|
4
4
|
module Renderer
|
|
5
5
|
class ErDiagramRenderer < BaseRenderer
|
|
6
6
|
def render(parsed_data)
|
|
7
|
-
output = [
|
|
7
|
+
output = ['erDiagram']
|
|
8
8
|
|
|
9
|
-
# リレーションシップを先に追加
|
|
10
9
|
if parsed_data[:relationships]&.any?
|
|
11
|
-
parsed_data[:relationships].each do |rel|
|
|
10
|
+
parsed_data[:relationships].sort_by { |rel| relationship_sort_key(rel) }.each do |rel|
|
|
12
11
|
output << render_relationship(rel)
|
|
13
12
|
end
|
|
14
|
-
output <<
|
|
13
|
+
output << ''
|
|
15
14
|
end
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
parsed_data[:entities].each do |entity|
|
|
16
|
+
parsed_data.fetch(:entities, []).sort_by { |entity| entity[:name].to_s }.each do |entity|
|
|
19
17
|
output << render_entity(entity)
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
output.join("\n")
|
|
20
|
+
output.join("\n").rstrip
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
private
|
|
26
24
|
|
|
27
25
|
def render_entity(entity)
|
|
28
26
|
lines = []
|
|
29
|
-
entity_name = entity[:name]
|
|
27
|
+
entity_name = aliased_identifier(entity[:name], uppercase: true)
|
|
30
28
|
|
|
31
29
|
lines << " #{entity_name} {"
|
|
32
|
-
entity
|
|
30
|
+
entity.fetch(:attributes, []).sort_by { |attr| attr[:name].to_s }.each do |attr|
|
|
33
31
|
type = type_to_er_type(attr[:type])
|
|
34
32
|
constraints = []
|
|
35
|
-
constraints <<
|
|
36
|
-
constraints <<
|
|
37
|
-
constraints <<
|
|
33
|
+
constraints << 'PK' if attr[:primary_key]
|
|
34
|
+
constraints << 'FK' if attr[:foreign_key]
|
|
35
|
+
constraints << 'UK' if attr[:unique]
|
|
38
36
|
|
|
39
|
-
line = " #{type} #{attr[:name]}"
|
|
40
|
-
line += " #{constraints.join(
|
|
37
|
+
line = " #{safe_identifier(type)} #{safe_identifier(attr[:name])}"
|
|
38
|
+
line += " #{constraints.join(',')}" if constraints.any?
|
|
39
|
+
line += " #{mermaid_string(attr[:name])}" if safe_identifier(attr[:name]) != attr[:name].to_s
|
|
41
40
|
lines << line
|
|
42
41
|
end
|
|
43
|
-
lines <<
|
|
44
|
-
lines <<
|
|
42
|
+
lines << ' }'
|
|
43
|
+
lines << ''
|
|
45
44
|
|
|
46
45
|
lines.join("\n")
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def render_relationship(rel)
|
|
50
|
-
source = rel[:source]
|
|
51
|
-
target = rel[:target]
|
|
49
|
+
source = safe_identifier(rel[:source], uppercase: true)
|
|
50
|
+
target = safe_identifier(rel[:target], uppercase: true)
|
|
52
51
|
label = rel[:label]
|
|
53
52
|
|
|
54
|
-
# カーディナリティを決定
|
|
55
53
|
source_card = cardinality_symbol(rel[:source_cardinality])
|
|
56
54
|
target_card = cardinality_symbol(rel[:target_cardinality])
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
line_type = rel[:identifying] ? "--" : ".."
|
|
56
|
+
line_type = rel[:identifying] ? '--' : '..'
|
|
60
57
|
|
|
61
|
-
" #{source} #{source_card}#{line_type}#{target_card} #{target} :
|
|
58
|
+
" #{source} #{source_card}#{line_type}#{target_card} #{target} : #{mermaid_string(label)}"
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
def cardinality_symbol(cardinality)
|
|
65
|
-
case cardinality
|
|
66
|
-
when
|
|
67
|
-
|
|
68
|
-
when
|
|
69
|
-
|
|
70
|
-
when
|
|
71
|
-
|
|
72
|
-
when
|
|
73
|
-
|
|
62
|
+
case cardinality&.to_sym
|
|
63
|
+
when :one
|
|
64
|
+
'||'
|
|
65
|
+
when :zero_or_one
|
|
66
|
+
'o|'
|
|
67
|
+
when :one_or_more
|
|
68
|
+
'}|'
|
|
69
|
+
when :zero_or_many
|
|
70
|
+
'o{'
|
|
74
71
|
else
|
|
75
|
-
"
|
|
72
|
+
raise ConfigurationError, "Unknown ER cardinality: #{cardinality.inspect}"
|
|
76
73
|
end
|
|
77
74
|
end
|
|
78
75
|
|
|
76
|
+
def relationship_sort_key(relationship)
|
|
77
|
+
%i[source target label].map { |key| relationship[key].to_s }
|
|
78
|
+
end
|
|
79
|
+
|
|
79
80
|
def type_to_er_type(type)
|
|
80
81
|
# Ruby/Rails型をERダイアグラム用の型に変換
|
|
81
82
|
case type.to_s.downcase
|
|
82
83
|
when /integer/, /bigint/
|
|
83
|
-
|
|
84
|
+
'int'
|
|
84
85
|
when /string/, /varchar/
|
|
85
|
-
|
|
86
|
+
'string'
|
|
86
87
|
when /text/
|
|
87
|
-
|
|
88
|
+
'text'
|
|
88
89
|
when /boolean/
|
|
89
|
-
|
|
90
|
+
'boolean'
|
|
90
91
|
when /datetime/, /timestamp/
|
|
91
|
-
|
|
92
|
+
'datetime'
|
|
92
93
|
when /date/
|
|
93
|
-
|
|
94
|
+
'date'
|
|
94
95
|
when /time/
|
|
95
|
-
|
|
96
|
+
'time'
|
|
96
97
|
when /decimal/, /numeric/
|
|
97
|
-
|
|
98
|
+
'decimal'
|
|
98
99
|
when /float/, /double/
|
|
99
|
-
|
|
100
|
+
'float'
|
|
100
101
|
when /json/, /jsonb/
|
|
101
|
-
|
|
102
|
+
'json'
|
|
102
103
|
else
|
|
103
104
|
type.to_s.downcase
|
|
104
105
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lagoon
|
|
4
|
+
Result = Data.define(:path, :content, :warnings, :counts) do
|
|
5
|
+
def initialize(path:, content:, warnings: [], counts: {})
|
|
6
|
+
super(
|
|
7
|
+
path: path.to_s.freeze,
|
|
8
|
+
content: content.to_s.freeze,
|
|
9
|
+
warnings: warnings.map(&:to_s).freeze,
|
|
10
|
+
counts: counts.transform_keys(&:to_sym).freeze
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
path
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_path
|
|
19
|
+
path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/lagoon/version.rb
CHANGED
data/lib/lagoon.rb
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
11
|
-
require_relative
|
|
12
|
-
require_relative
|
|
13
|
-
require_relative
|
|
14
|
-
require_relative
|
|
15
|
-
require_relative
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'active_support'
|
|
5
|
+
require_relative 'lagoon/version'
|
|
6
|
+
require_relative 'lagoon/errors'
|
|
7
|
+
require_relative 'lagoon/result'
|
|
8
|
+
require_relative 'lagoon/configuration'
|
|
9
|
+
require_relative 'lagoon/options'
|
|
10
|
+
require_relative 'lagoon/diagram/base'
|
|
11
|
+
require_relative 'lagoon/diagram/model_diagram'
|
|
12
|
+
require_relative 'lagoon/diagram/controller_diagram'
|
|
13
|
+
require_relative 'lagoon/diagram/er_diagram'
|
|
14
|
+
require_relative 'lagoon/diagram/controller_model_diagram'
|
|
15
|
+
require_relative 'lagoon/analyzer/active_record_analyzer'
|
|
16
|
+
require_relative 'lagoon/analyzer/action_controller_analyzer'
|
|
17
|
+
require_relative 'lagoon/analyzer/database_schema_analyzer'
|
|
18
|
+
require_relative 'lagoon/parser/application_class_filter'
|
|
19
|
+
require_relative 'lagoon/parser/model_parser'
|
|
20
|
+
require_relative 'lagoon/parser/controller_parser'
|
|
21
|
+
require_relative 'lagoon/parser/schema_parser'
|
|
22
|
+
require_relative 'lagoon/parser/controller_model_parser'
|
|
23
|
+
require_relative 'lagoon/renderer/base_renderer'
|
|
24
|
+
require_relative 'lagoon/renderer/class_diagram_renderer'
|
|
25
|
+
require_relative 'lagoon/renderer/er_diagram_renderer'
|
|
26
|
+
require_relative 'lagoon/renderer/controller_model_er_renderer'
|
|
27
|
+
require_relative 'lagoon/railtie' if defined?(Rails::Railtie)
|
|
16
28
|
|
|
17
29
|
module Lagoon
|
|
18
|
-
class Error < StandardError; end
|
|
19
|
-
|
|
20
30
|
class << self
|
|
21
31
|
attr_writer :configuration
|
|
22
32
|
|
|
@@ -28,24 +38,63 @@ module Lagoon
|
|
|
28
38
|
yield(configuration)
|
|
29
39
|
end
|
|
30
40
|
|
|
41
|
+
def reset_configuration!
|
|
42
|
+
@configuration = Configuration.new
|
|
43
|
+
end
|
|
44
|
+
|
|
31
45
|
def generate_model_diagram(options = {})
|
|
32
|
-
Diagram::ModelDiagram.new(options).generate
|
|
46
|
+
Diagram::ModelDiagram.new(options, configuration: configuration.dup).generate
|
|
33
47
|
end
|
|
34
48
|
|
|
35
49
|
def generate_controller_diagram(options = {})
|
|
36
|
-
Diagram::ControllerDiagram.new(options).generate
|
|
50
|
+
Diagram::ControllerDiagram.new(options, configuration: configuration.dup).generate
|
|
37
51
|
end
|
|
38
52
|
|
|
39
53
|
def generate_er_diagram(options = {})
|
|
40
|
-
Diagram::ErDiagram.new(options).generate
|
|
54
|
+
Diagram::ErDiagram.new(options, configuration: configuration.dup).generate
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def generate_controller_model_diagram(options = {})
|
|
58
|
+
Diagram::ControllerModelDiagram.new(options, configuration: configuration.dup).generate
|
|
41
59
|
end
|
|
42
60
|
|
|
43
61
|
def generate_all(options = {})
|
|
62
|
+
raw_options = options.to_h.transform_keys(&:to_sym)
|
|
63
|
+
validate_all_options!(raw_options)
|
|
64
|
+
eager_load_application!
|
|
65
|
+
output_dir = raw_options.delete(:output)
|
|
66
|
+
|
|
44
67
|
{
|
|
45
|
-
models: generate_model_diagram(
|
|
46
|
-
controllers: generate_controller_diagram(
|
|
47
|
-
|
|
68
|
+
models: generate_model_diagram(options_for(:model, raw_options, output_dir, 'models.mermaid')),
|
|
69
|
+
controllers: generate_controller_diagram(
|
|
70
|
+
options_for(:controller, raw_options, output_dir, 'controllers.mermaid')
|
|
71
|
+
),
|
|
72
|
+
er: generate_er_diagram(options_for(:er, raw_options, output_dir, 'er_diagram.mermaid')),
|
|
73
|
+
controller_models: generate_controller_model_diagram(
|
|
74
|
+
options_for(:controller_model, raw_options, output_dir, 'controller_models.mermaid')
|
|
75
|
+
)
|
|
48
76
|
}
|
|
49
77
|
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def eager_load_application!
|
|
82
|
+
Rails.application.eager_load! if defined?(Rails) && Rails.respond_to?(:application)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate_all_options!(options)
|
|
86
|
+
allowed = Options::KEYS.values.flatten.uniq + [:output]
|
|
87
|
+
unknown = options.keys - allowed
|
|
88
|
+
return if unknown.empty?
|
|
89
|
+
|
|
90
|
+
raise ConfigurationError, "Unknown all option(s): #{unknown.sort.join(', ')}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def options_for(kind, options, output_dir, filename)
|
|
94
|
+
selected = options.slice(*Options.allowed_keys(kind))
|
|
95
|
+
selected[:eager_load] = false
|
|
96
|
+
selected[:output] = File.join(output_dir.to_s, filename) if output_dir
|
|
97
|
+
selected
|
|
98
|
+
end
|
|
50
99
|
end
|
|
51
100
|
end
|
data/lib/tasks/lagoon.rake
CHANGED
|
@@ -1,39 +1,47 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
namespace :mermaid do
|
|
4
|
-
desc
|
|
4
|
+
desc 'Generate all Mermaid diagrams'
|
|
5
5
|
task all: :environment do
|
|
6
6
|
results = Lagoon.generate_all
|
|
7
|
-
puts
|
|
7
|
+
puts 'All diagrams generated:'
|
|
8
8
|
puts " Models: #{results[:models]}"
|
|
9
9
|
puts " Controllers: #{results[:controllers]}"
|
|
10
10
|
puts " ER: #{results[:er]}"
|
|
11
|
+
puts " Controller-Models: #{results[:controller_models]}"
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
desc
|
|
14
|
+
desc 'Generate Mermaid model diagram'
|
|
14
15
|
task models: :environment do
|
|
15
16
|
output_file = Lagoon.generate_model_diagram
|
|
16
17
|
puts "Model diagram generated: #{output_file}"
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
desc
|
|
20
|
+
desc 'Generate Mermaid controller diagram'
|
|
20
21
|
task controllers: :environment do
|
|
21
22
|
output_file = Lagoon.generate_controller_diagram
|
|
22
23
|
puts "Controller diagram generated: #{output_file}"
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
desc
|
|
26
|
+
desc 'Generate Mermaid ER diagram'
|
|
26
27
|
task er: :environment do
|
|
27
28
|
output_file = Lagoon.generate_er_diagram
|
|
28
29
|
puts "ER diagram generated: #{output_file}"
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
desc
|
|
32
|
+
desc 'Generate controller-model relationship diagram'
|
|
33
|
+
task controller_models: :environment do
|
|
34
|
+
output_file = Lagoon.generate_controller_model_diagram
|
|
35
|
+
puts "Controller-model diagram generated: #{output_file}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
desc 'Generate brief diagrams (no attributes/methods)'
|
|
32
39
|
task brief: :environment do
|
|
33
40
|
results = Lagoon.generate_all(brief: true)
|
|
34
|
-
puts
|
|
41
|
+
puts 'Brief diagrams generated:'
|
|
35
42
|
puts " Models: #{results[:models]}"
|
|
36
43
|
puts " Controllers: #{results[:controllers]}"
|
|
37
44
|
puts " ER: #{results[:er]}"
|
|
45
|
+
puts " Controller-Models: #{results[:controller_models]}"
|
|
38
46
|
end
|
|
39
47
|
end
|
data/sig/lagoon.rbs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module Lagoon
|
|
2
|
+
VERSION: String
|
|
3
|
+
|
|
4
|
+
type option_key = Symbol | String
|
|
5
|
+
type options_hash = Hash[option_key, untyped]
|
|
6
|
+
type counts_hash = Hash[Symbol, Integer]
|
|
7
|
+
|
|
8
|
+
class Error < StandardError
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class ConfigurationError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class RailsLoadError < Error
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class ParseError < Error
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class OutputError < Error
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Configuration
|
|
24
|
+
DIRECTIONS: Array[String]
|
|
25
|
+
|
|
26
|
+
attr_accessor show_attributes: bool
|
|
27
|
+
attr_accessor show_methods: bool
|
|
28
|
+
attr_accessor include_inheritance: bool
|
|
29
|
+
attr_accessor exclude_models: Array[String]
|
|
30
|
+
attr_accessor exclude_controllers: Array[String]
|
|
31
|
+
attr_accessor exclude_tables: Array[String]
|
|
32
|
+
attr_accessor internal_tables: Array[String]
|
|
33
|
+
attr_accessor include_framework_bases: bool
|
|
34
|
+
attr_accessor strict: bool
|
|
35
|
+
attr_accessor helper_models: Hash[String, String]
|
|
36
|
+
attr_reader output_dir: String
|
|
37
|
+
attr_reader diagram_direction: String
|
|
38
|
+
|
|
39
|
+
def initialize: () -> void
|
|
40
|
+
def output_dir=: (String value) -> String
|
|
41
|
+
def diagram_direction=: (String value) -> String
|
|
42
|
+
def self.validate_direction!: (String | Symbol value) -> String
|
|
43
|
+
def initialize_copy: (Configuration original) -> void
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Options
|
|
47
|
+
include Enumerable[[Symbol, untyped]]
|
|
48
|
+
|
|
49
|
+
COMMON_KEYS: Array[Symbol]
|
|
50
|
+
KEYS: Hash[Symbol, Array[Symbol]]
|
|
51
|
+
|
|
52
|
+
attr_reader kind: Symbol
|
|
53
|
+
|
|
54
|
+
def self.for: (Symbol kind, ?options_hash? raw_options, ?config: Configuration, **untyped keyword_options) -> Options
|
|
55
|
+
def self.allowed_keys: (Symbol kind) -> Array[Symbol]
|
|
56
|
+
def initialize: (Symbol kind, ?options_hash raw_options, ?config: Configuration) -> void
|
|
57
|
+
def []: (option_key key) -> untyped
|
|
58
|
+
def fetch: (option_key key, *untyped defaults) { (Symbol) -> untyped } -> untyped
|
|
59
|
+
| (option_key key, *untyped defaults) -> untyped
|
|
60
|
+
def key?: (option_key key) -> bool
|
|
61
|
+
def each: () { ([Symbol, untyped]) -> void } -> Options
|
|
62
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Result
|
|
66
|
+
attr_reader path: String
|
|
67
|
+
attr_reader content: String
|
|
68
|
+
attr_reader warnings: Array[String]
|
|
69
|
+
attr_reader counts: counts_hash
|
|
70
|
+
|
|
71
|
+
def initialize: (path: String, content: String, ?warnings: Array[String], ?counts: counts_hash) -> void
|
|
72
|
+
def to_s: () -> String
|
|
73
|
+
def to_path: () -> String
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.configuration: () -> Configuration
|
|
77
|
+
def self.configuration=: (Configuration configuration) -> Configuration
|
|
78
|
+
def self.configure: () { (Configuration) -> untyped } -> untyped
|
|
79
|
+
def self.reset_configuration!: () -> Configuration
|
|
80
|
+
def self.generate_model_diagram: (?options_hash options) -> Result
|
|
81
|
+
def self.generate_controller_diagram: (?options_hash options) -> Result
|
|
82
|
+
def self.generate_er_diagram: (?options_hash options) -> Result
|
|
83
|
+
def self.generate_controller_model_diagram: (?options_hash options) -> Result
|
|
84
|
+
def self.generate_all: (?options_hash options) -> Hash[Symbol, Result]
|
|
85
|
+
|
|
86
|
+
module Diagram
|
|
87
|
+
class Base
|
|
88
|
+
attr_reader options: Options
|
|
89
|
+
attr_reader config: Configuration
|
|
90
|
+
|
|
91
|
+
def initialize: (?options_hash? options, ?configuration: Configuration, **untyped keyword_options) -> void
|
|
92
|
+
def generate: () -> Result
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class ModelDiagram < Base
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class ControllerDiagram < Base
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class ErDiagram < Base
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class ControllerModelDiagram < Base
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|