prailroady 1.5.3
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 +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +3 -0
- data/AUTHORS.rdoc +23 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +33 -0
- data/Guardfile +49 -0
- data/LICENSE.rdoc +340 -0
- data/README.md +34 -0
- data/Rakefile +12 -0
- data/bin/prailroady +58 -0
- data/lib/prailroady.rb +5 -0
- data/lib/prailroady/aasm_diagram.rb +105 -0
- data/lib/prailroady/app_diagram.rb +103 -0
- data/lib/prailroady/controllers_diagram.rb +103 -0
- data/lib/prailroady/diagram_graph.rb +125 -0
- data/lib/prailroady/models_diagram.rb +357 -0
- data/lib/prailroady/options_struct.rb +204 -0
- data/lib/prailroady/railtie.rb +11 -0
- data/lib/prailroady/version.rb +3 -0
- data/prailroady.gemspec +25 -0
- data/tasks/prailroady.rake +139 -0
- data/test/file_fixture/app/controllers/application_controller.rb +2 -0
- data/test/file_fixture/app/controllers/dummy1_controller.rb +0 -0
- data/test/file_fixture/app/controllers/dummy2_controller.rb +0 -0
- data/test/file_fixture/app/controllers/sub-dir/sub_dummy_controller.rb +0 -0
- data/test/file_fixture/app/models/concerns/author_settings.rb +12 -0
- data/test/file_fixture/app/models/concerns/taggable.rb +0 -0
- data/test/file_fixture/app/models/dummy1.rb +0 -0
- data/test/file_fixture/app/models/dummy2.rb +0 -0
- data/test/file_fixture/app/models/sub-dir/sub_dummy.rb +0 -0
- data/test/file_fixture/lib/app/controllers/dummy/dummy_controller.rb +0 -0
- data/test/file_fixture/lib/app/models/dummy1.rb +0 -0
- data/test/lib/railroady/aasm_diagram_spec.rb +54 -0
- data/test/lib/railroady/app_diagram_spec.rb +14 -0
- data/test/lib/railroady/controllers_diagram_spec.rb +56 -0
- data/test/lib/railroady/core_ext_spec.rb +13 -0
- data/test/lib/railroady/diagram_graph_spec.rb +53 -0
- data/test/lib/railroady/models_diagram_spec.rb +162 -0
- data/test/spec_helper.rb +5 -0
- metadata +159 -0
data/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Use online preview
|
|
2
|
+
|
|
3
|
+
https://planttext.com/
|
|
4
|
+
|
|
5
|
+
# Or install local
|
|
6
|
+
|
|
7
|
+
## Mac users
|
|
8
|
+
|
|
9
|
+
Brew users can install via:
|
|
10
|
+
|
|
11
|
+
brew install graphviz
|
|
12
|
+
|
|
13
|
+
MacPorts users can install in via
|
|
14
|
+
|
|
15
|
+
sudo port install graphviz
|
|
16
|
+
|
|
17
|
+
## Install Plantuml
|
|
18
|
+
|
|
19
|
+
http://plantuml.com/download
|
|
20
|
+
|
|
21
|
+
## Install Atom Preview
|
|
22
|
+
|
|
23
|
+
https://atom.io/packages/plantuml-viewer
|
|
24
|
+
|
|
25
|
+
https://atom.io/packages/plantuml-preview
|
|
26
|
+
|
|
27
|
+
## Run generate
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
prailroady -o models.pu -M # only model name
|
|
31
|
+
prailroady -o models.pu -M -l # with attributes
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then open `models.pu` using atom preview or use online preview.
|
data/Rakefile
ADDED
data/bin/prailroady
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# PrailRoady - RoR diagrams generator
|
|
4
|
+
# http://github.com/preston/railroady
|
|
5
|
+
#
|
|
6
|
+
# PrailRoady generates models and controllers diagrams in DOT language
|
|
7
|
+
# for a Rails v3 application.
|
|
8
|
+
#
|
|
9
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
|
10
|
+
#
|
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
|
12
|
+
# it under the terms of the GNU General Public License as published by
|
|
13
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
14
|
+
# (at your option) any later version.
|
|
15
|
+
#
|
|
16
|
+
# Modifications 2010-2015 by Preston Lee.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
20
|
+
require 'prailroady'
|
|
21
|
+
|
|
22
|
+
APP_NAME = 'prailroady'
|
|
23
|
+
APP_HUMAN_NAME = 'PrailRoady'
|
|
24
|
+
# APP_VERSION = [PrailRoady::VERSION::MAJOR, PrailRoady::VERSION::MINOR, PrailRoady::VERSION::PATCH]
|
|
25
|
+
APP_VERSION = PrailRoady::VERSION
|
|
26
|
+
COPYRIGHT = 'Copyright (C) 2007-2008 Javier Smaldone, 2009 Peter Hoeg, 2010-2015 Preston Lee'
|
|
27
|
+
|
|
28
|
+
options = OptionsStruct.new(app_name: APP_NAME, app_human_name: APP_HUMAN_NAME, app_version: APP_VERSION, copyright: COPYRIGHT)
|
|
29
|
+
|
|
30
|
+
options.parse ARGV
|
|
31
|
+
|
|
32
|
+
# puts "OPTS!!!"
|
|
33
|
+
# puts options
|
|
34
|
+
|
|
35
|
+
old_dir = Dir.pwd
|
|
36
|
+
|
|
37
|
+
Dir.chdir(options.root) if options.root != ''
|
|
38
|
+
|
|
39
|
+
case options.command
|
|
40
|
+
when 'models'
|
|
41
|
+
diagram = ModelsDiagram.new options
|
|
42
|
+
when 'controllers'
|
|
43
|
+
diagram = ControllersDiagram.new options
|
|
44
|
+
when 'aasm'
|
|
45
|
+
diagram = AasmDiagram.new(options)
|
|
46
|
+
else
|
|
47
|
+
STDERR.print "#{APP_HUMAN_NAME} v#{APP_VERSION}\n" \
|
|
48
|
+
"Error: You must supply a command\n" \
|
|
49
|
+
" (try #{APP_NAME} -h)\n\n"
|
|
50
|
+
exit 1
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
diagram.process
|
|
54
|
+
diagram.generate
|
|
55
|
+
|
|
56
|
+
Dir.chdir(old_dir)
|
|
57
|
+
|
|
58
|
+
diagram.print
|
data/lib/prailroady.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# PrailRoady - RoR diagrams generator
|
|
2
|
+
# http://railroad.rubyforge.org
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
|
5
|
+
# See COPYING for more details
|
|
6
|
+
|
|
7
|
+
# AASM code provided by Ana Nelson (http://ananelson.com/)
|
|
8
|
+
|
|
9
|
+
require 'prailroady/app_diagram'
|
|
10
|
+
|
|
11
|
+
# Diagram for Acts As State Machine
|
|
12
|
+
class AasmDiagram < AppDiagram
|
|
13
|
+
def initialize(options = OptionsStruct.new)
|
|
14
|
+
# options.exclude.map! {|e| e = "app/models/" + e}
|
|
15
|
+
super options
|
|
16
|
+
@graph.diagram_type = 'Models'
|
|
17
|
+
# Processed habtm associations
|
|
18
|
+
@habtm = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Process model files
|
|
22
|
+
def generate
|
|
23
|
+
STDERR.print "Generating AASM diagram\n" if @options.verbose
|
|
24
|
+
get_files.each do |f|
|
|
25
|
+
process_class extract_class_name(f).constantize
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_files(prefix = '')
|
|
30
|
+
files = !@options.specify.empty? ? Dir.glob(@options.specify) : Dir.glob(prefix + 'app/models/**/*.rb')
|
|
31
|
+
files += Dir.glob('vendor/plugins/**/app/models/*.rb') if @options.plugins_models
|
|
32
|
+
files -= Dir.glob(prefix + 'app/models/concerns/**/*.rb') unless @options.include_concerns
|
|
33
|
+
files -= Dir.glob(@options.exclude)
|
|
34
|
+
files
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Load model classes
|
|
40
|
+
def load_classes
|
|
41
|
+
disable_stdout
|
|
42
|
+
get_files.each { |m| require m }
|
|
43
|
+
enable_stdout
|
|
44
|
+
rescue LoadError
|
|
45
|
+
enable_stdout
|
|
46
|
+
print_error 'model classes'
|
|
47
|
+
raise
|
|
48
|
+
end # load_classes
|
|
49
|
+
|
|
50
|
+
# Process a model class
|
|
51
|
+
def process_class(current_class)
|
|
52
|
+
STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
|
|
53
|
+
|
|
54
|
+
# Only interested in acts_as_state_machine models.
|
|
55
|
+
process_acts_as_state_machine_class(current_class) if current_class.respond_to?(:states)
|
|
56
|
+
process_aasm_class(current_class) if current_class.respond_to?(:aasm_states) || current_class.respond_to?(:aasm)
|
|
57
|
+
end # process_class
|
|
58
|
+
|
|
59
|
+
def process_acts_as_state_machine_class(current_class)
|
|
60
|
+
node_attribs = []
|
|
61
|
+
node_type = 'aasm'
|
|
62
|
+
|
|
63
|
+
STDERR.print "\t\tprocessing as acts_as_state_machine\n" if @options.verbose
|
|
64
|
+
current_class.aasm.states.each do |state_name|
|
|
65
|
+
node_shape = (current_class.aasm.initial_state == state_name) ? ', peripheries = 2' : ''
|
|
66
|
+
node_attribs << "#{current_class.name.downcase}_#{state_name} [label=#{state_name} #{node_shape}];"
|
|
67
|
+
end
|
|
68
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
|
69
|
+
|
|
70
|
+
current_class.aasm.events.each do |event|
|
|
71
|
+
event_name = event.name
|
|
72
|
+
event.transitions.each do |transition|
|
|
73
|
+
@graph.add_edge [
|
|
74
|
+
'event',
|
|
75
|
+
current_class.name.downcase + '_' + transition.from.to_s,
|
|
76
|
+
current_class.name.downcase + '_' + transition.to.to_s,
|
|
77
|
+
event_name.to_s
|
|
78
|
+
]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def process_aasm_class(current_class)
|
|
84
|
+
node_attribs = []
|
|
85
|
+
node_type = 'aasm'
|
|
86
|
+
|
|
87
|
+
STDERR.print "\t\tprocessing as aasm\n" if @options.verbose
|
|
88
|
+
current_class.aasm.states.each do |state|
|
|
89
|
+
node_shape = (current_class.aasm.initial_state == state.name) ? ', peripheries = 2' : ''
|
|
90
|
+
node_attribs << "#{current_class.name.downcase}_#{state.name} [label=#{state.name} #{node_shape}];"
|
|
91
|
+
end
|
|
92
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
|
93
|
+
|
|
94
|
+
current_class.aasm.events.each do |event|
|
|
95
|
+
event.transitions.each do |transition|
|
|
96
|
+
@graph.add_edge [
|
|
97
|
+
'event',
|
|
98
|
+
current_class.name.downcase + '_' + transition.from.to_s,
|
|
99
|
+
current_class.name.downcase + '_' + transition.to.to_s,
|
|
100
|
+
event.name.to_s
|
|
101
|
+
]
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end # class AasmDiagram
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# PrailRoady - RoR diagrams generator
|
|
2
|
+
# http://railroad.rubyforge.org
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
|
5
|
+
# See COPYING for more details
|
|
6
|
+
|
|
7
|
+
require 'prailroady/diagram_graph'
|
|
8
|
+
|
|
9
|
+
# Root class for PrailRoady diagrams
|
|
10
|
+
class AppDiagram
|
|
11
|
+
def initialize(options = OptionsStruct.new)
|
|
12
|
+
@options = options
|
|
13
|
+
@graph = DiagramGraph.new
|
|
14
|
+
@graph.show_label = @options.label
|
|
15
|
+
@graph.alphabetize = @options.alphabetize
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Print diagram
|
|
19
|
+
def print
|
|
20
|
+
if @options.output
|
|
21
|
+
old_stdout = STDOUT.dup
|
|
22
|
+
begin
|
|
23
|
+
STDOUT.reopen(@options.output)
|
|
24
|
+
rescue
|
|
25
|
+
STDERR.print "Error: Cannot write diagram to #{@options.output}\n\n"
|
|
26
|
+
exit 2
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if @options.xmi
|
|
31
|
+
STDERR.print "Generating XMI diagram\n" if @options.verbose
|
|
32
|
+
STDOUT.print @graph.to_xmi
|
|
33
|
+
else
|
|
34
|
+
STDERR.print "Generating DOT graph\n" if @options.verbose
|
|
35
|
+
STDOUT.print @graph.to_dot
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
STDOUT.reopen(old_stdout) if @options.output
|
|
39
|
+
end # print
|
|
40
|
+
|
|
41
|
+
def process
|
|
42
|
+
load_environment
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# get all engines
|
|
46
|
+
def engines
|
|
47
|
+
engines = []
|
|
48
|
+
|
|
49
|
+
if defined?(Rails)
|
|
50
|
+
engines = if Rails::Application::Railties.respond_to?(:engines)
|
|
51
|
+
Rails::Application::Railties.engines
|
|
52
|
+
else
|
|
53
|
+
# rails 4 way of getting engines
|
|
54
|
+
Rails::Engine.subclasses.map(&:instance)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
engines
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# Load Rails application's environment
|
|
63
|
+
def load_environment
|
|
64
|
+
STDERR.print "Loading application environment\n" if @options.verbose
|
|
65
|
+
begin
|
|
66
|
+
disable_stdout
|
|
67
|
+
l = File.join(Dir.pwd.to_s, @options.config_file)
|
|
68
|
+
require l
|
|
69
|
+
enable_stdout
|
|
70
|
+
rescue LoadError
|
|
71
|
+
enable_stdout
|
|
72
|
+
print_error 'application environment'
|
|
73
|
+
raise
|
|
74
|
+
end
|
|
75
|
+
STDERR.print "Loading application classes as we go\n" if @options.verbose
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Prevents Rails application from writing to STDOUT
|
|
79
|
+
def disable_stdout
|
|
80
|
+
@old_stdout = STDOUT.dup
|
|
81
|
+
# via Tomas Matousek, http://www.ruby-forum.com/topic/205887
|
|
82
|
+
STDOUT.reopen(::RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ ? 'NUL' : '/dev/null')
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Restore STDOUT
|
|
86
|
+
def enable_stdout
|
|
87
|
+
STDOUT.reopen(@old_stdout)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Print error when loading Rails application
|
|
91
|
+
def print_error(type)
|
|
92
|
+
STDERR.print "Error loading #{type}.\n (Are you running " \
|
|
93
|
+
"#{@options.app_name} on the application's root directory?)\n\n"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Extract class name from filename
|
|
97
|
+
def extract_class_name(filename)
|
|
98
|
+
# filename.split('/')[2..-1].join('/').split('.').first.camelize
|
|
99
|
+
# Fixed by patch from ticket #12742
|
|
100
|
+
# File.basename(filename).chomp(".rb").camelize
|
|
101
|
+
filename.split('/')[2..-1].collect(&:camelize).join('::').chomp('.rb')
|
|
102
|
+
end
|
|
103
|
+
end # class AppDiagram
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# PrailRoady - RoR diagrams generator
|
|
2
|
+
# http://railroad.rubyforge.org
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
|
5
|
+
# See COPYING for more details
|
|
6
|
+
|
|
7
|
+
require 'prailroady/app_diagram'
|
|
8
|
+
|
|
9
|
+
# PrailRoady controllers diagram
|
|
10
|
+
class ControllersDiagram < AppDiagram
|
|
11
|
+
# as of Rails 2.3 the file is no longer application.rb but instead
|
|
12
|
+
# application_controller.rb
|
|
13
|
+
APP_CONTROLLER = File.exist?('app/controllers/application.rb') ? 'app/controllers/application.rb' : 'app/controllers/application_controller.rb'
|
|
14
|
+
|
|
15
|
+
def initialize(options = OptionsStruct.new)
|
|
16
|
+
# options.exclude.map! {|e| "app/controllers/" + e}
|
|
17
|
+
super options
|
|
18
|
+
@graph.diagram_type = 'Controllers'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Process controller files
|
|
22
|
+
def generate
|
|
23
|
+
STDERR.print "Generating controllers diagram\n" if @options.verbose
|
|
24
|
+
files = get_files
|
|
25
|
+
# only add APP_CONTROLLER if it isn't already included from the glob above
|
|
26
|
+
files << APP_CONTROLLER unless files.include? APP_CONTROLLER
|
|
27
|
+
files.each do |f|
|
|
28
|
+
class_name = extract_class_name(f)
|
|
29
|
+
# ApplicationController's file is 'application.rb' in Rails < 2.3
|
|
30
|
+
class_name += 'Controller' if class_name == 'Application'
|
|
31
|
+
begin
|
|
32
|
+
process_class class_name.constantize
|
|
33
|
+
rescue Exception
|
|
34
|
+
STDERR.print "Warning: exception #{$ERROR_INFO} raised while trying to load controller class #{f}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end # generate
|
|
38
|
+
|
|
39
|
+
def get_files(prefix = '')
|
|
40
|
+
files = !@options.specify.empty? ? Dir.glob(@options.specify) : Dir.glob(prefix << 'app/controllers/**/*_controller.rb')
|
|
41
|
+
files += engine_files if @options.engine_controllers
|
|
42
|
+
files -= Dir.glob(@options.exclude)
|
|
43
|
+
files
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def engine_files
|
|
47
|
+
engines.collect { |engine| Dir.glob("#{engine.root}/app/controllers/**/*_controller.rb") }.flatten
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def extract_class_name(filename)
|
|
51
|
+
filename.match(/.*\/controllers\/(.*).rb$/)[1].camelize
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Load controller classes
|
|
57
|
+
def load_classes
|
|
58
|
+
disable_stdout
|
|
59
|
+
# ApplicationController must be loaded first
|
|
60
|
+
require APP_CONTROLLER
|
|
61
|
+
get_files.each { |c| require "./#{c}" }
|
|
62
|
+
enable_stdout
|
|
63
|
+
rescue LoadError
|
|
64
|
+
enable_stdout
|
|
65
|
+
print_error 'controller classes'
|
|
66
|
+
raise
|
|
67
|
+
end # load_classes
|
|
68
|
+
|
|
69
|
+
# Proccess a controller class
|
|
70
|
+
def process_class(current_class)
|
|
71
|
+
STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
|
|
72
|
+
|
|
73
|
+
if @options.brief
|
|
74
|
+
@graph.add_node ['controller-brief', current_class.name]
|
|
75
|
+
elsif current_class.is_a? Class
|
|
76
|
+
# Collect controller's methods
|
|
77
|
+
node_attribs = { public: [],
|
|
78
|
+
protected: [],
|
|
79
|
+
private: [] }
|
|
80
|
+
current_class.public_instance_methods(false).sort.each do |m|
|
|
81
|
+
node_attribs[:public] << m
|
|
82
|
+
end unless @options.hide_public
|
|
83
|
+
current_class.protected_instance_methods(false).sort.each do |m|
|
|
84
|
+
node_attribs[:protected] << m
|
|
85
|
+
end unless @options.hide_protected
|
|
86
|
+
current_class.private_instance_methods(false).sort.each do |m|
|
|
87
|
+
node_attribs[:private] << m
|
|
88
|
+
end unless @options.hide_private
|
|
89
|
+
@graph.add_node ['controller', current_class.name, node_attribs]
|
|
90
|
+
elsif @options.modules && current_class.is_a?(Module)
|
|
91
|
+
@graph.add_node ['module', current_class.name]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Generate the inheritance edge (only for transitive subclasses of ApplicationControllers)
|
|
95
|
+
if @options.inheritance && (transitive_subclasses_of(ApplicationController).include? current_class)
|
|
96
|
+
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
|
|
97
|
+
end
|
|
98
|
+
end # process_class
|
|
99
|
+
|
|
100
|
+
def transitive_subclasses_of(klass)
|
|
101
|
+
klass.subclasses | klass.subclasses.map { |subklass| transitive_subclasses_of(subklass) }.flatten
|
|
102
|
+
end
|
|
103
|
+
end # class ControllersDiagram
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# PrailRoady - RoR diagrams generator
|
|
2
|
+
# http://railroad.rubyforge.org
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
|
5
|
+
# See COPYING for more details
|
|
6
|
+
|
|
7
|
+
# PrailRoady diagram structure
|
|
8
|
+
class DiagramGraph
|
|
9
|
+
def initialize
|
|
10
|
+
@diagram_type = ''
|
|
11
|
+
@show_label = false
|
|
12
|
+
@alphabetize = false
|
|
13
|
+
@nodes = []
|
|
14
|
+
@edges = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_node(node)
|
|
18
|
+
@nodes << node
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_edge(edge)
|
|
22
|
+
@edges << edge
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_writer :diagram_type
|
|
26
|
+
|
|
27
|
+
attr_writer :show_label
|
|
28
|
+
|
|
29
|
+
attr_writer :alphabetize
|
|
30
|
+
|
|
31
|
+
# Generate DOT graph
|
|
32
|
+
def to_dot
|
|
33
|
+
dot_header +
|
|
34
|
+
@nodes.map { |n| dot_node n[0], n[1], n[2], n[3] }.join +
|
|
35
|
+
@edges.map { |e| dot_edge e[0], e[1], e[2], e[3] }.join +
|
|
36
|
+
dot_footer
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Generate XMI diagram (not yet implemented)
|
|
40
|
+
def to_xmi
|
|
41
|
+
STDERR.print "Sorry. XMI output not yet implemented.\n\n"
|
|
42
|
+
''
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Build DOT diagram header
|
|
48
|
+
def dot_header
|
|
49
|
+
result = "@startuml\n"
|
|
50
|
+
result += "\tskinparam linetype ortho\n"
|
|
51
|
+
result += "\tskinparam packageStyle rectangle\n"
|
|
52
|
+
result += "\tskinparam shadowing false\n"
|
|
53
|
+
result += "\tskinparam class {\n"
|
|
54
|
+
result += "\t\tBackgroundColor White\n"
|
|
55
|
+
result += "\t\tBorderColor Black\n"
|
|
56
|
+
result += "\t\tArrowColor Black\n"
|
|
57
|
+
result += "\t}\n"
|
|
58
|
+
result += "\thide members\n" if !@show_label
|
|
59
|
+
result += "\thide circle\n"
|
|
60
|
+
result
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Build DOT diagram footer
|
|
64
|
+
def dot_footer
|
|
65
|
+
"\n@enduml\n"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Build a DOT graph node
|
|
69
|
+
def dot_node(type, name, attributes = nil, custom_options = '')
|
|
70
|
+
options = ''
|
|
71
|
+
case type
|
|
72
|
+
when 'model'
|
|
73
|
+
options += attributes.sort_by { |s| @alphabetize ? s : nil }.map{|s| "\t\t#{s}"}.join("\n")
|
|
74
|
+
when 'model-brief'
|
|
75
|
+
options += ""
|
|
76
|
+
when 'class'
|
|
77
|
+
options += ""
|
|
78
|
+
when 'class-brief'
|
|
79
|
+
options += ""
|
|
80
|
+
when 'controller'
|
|
81
|
+
options += ""
|
|
82
|
+
options += attributes[:public].sort_by { |s| @alphabetize ? s : nil }.map{|s| "\t\t#{s}"}.join("\n")
|
|
83
|
+
options += attributes[:protected].sort_by { |s| @alphabetize ? s : nil }.map{|s| "\t\t#{s}"}.join("\n")
|
|
84
|
+
options += attributes[:private].sort_by { |s| @alphabetize ? s : nil }.map{|s| "\t\t#{s}"}.join("\n")
|
|
85
|
+
when 'controller-brief'
|
|
86
|
+
options += ""
|
|
87
|
+
when 'module'
|
|
88
|
+
options += ""
|
|
89
|
+
when 'aasm'
|
|
90
|
+
return "aasm: \\n #{attributes.join("\\n")}"
|
|
91
|
+
end
|
|
92
|
+
# options = [options, custom_options].compact.reject{|o| o.empty?}.join(', ')
|
|
93
|
+
attr_options = "{\n#{options}\n\t}" if @show_label && options.length > 0
|
|
94
|
+
return "\tclass #{quote(name)} as #{noquote(name)} #{attr_options}\n"
|
|
95
|
+
end # dot_node
|
|
96
|
+
|
|
97
|
+
# Build a DOT graph edge
|
|
98
|
+
def dot_edge(type, from, to, name = '')
|
|
99
|
+
ret = ""
|
|
100
|
+
case type
|
|
101
|
+
when 'one-one'
|
|
102
|
+
ret = "\t#{quote(from)} -- #{quote(to)}\n"
|
|
103
|
+
when 'one-many'
|
|
104
|
+
ret = "\t#{quote(from)} --|{ #{quote(to)}\n"
|
|
105
|
+
when 'many-many'
|
|
106
|
+
ret = "\t#{quote(from)} }|--|{ #{quote(to)}\n"
|
|
107
|
+
when 'belongs-to'
|
|
108
|
+
ret = "\t#{quote(from)} }|--|{ #{quote(to)}\n"
|
|
109
|
+
when 'is-a'
|
|
110
|
+
ret = "\t#{quote(from)} }|--|{ #{quote(to)}\n"
|
|
111
|
+
when 'event'
|
|
112
|
+
ret = "\t#{quote(from)} }|--|{ #{quote(to)}\n"
|
|
113
|
+
end
|
|
114
|
+
ret
|
|
115
|
+
end # dot_edge
|
|
116
|
+
|
|
117
|
+
# Quotes a class name
|
|
118
|
+
def quote(name)
|
|
119
|
+
('"' + name.to_s + '"').gsub(":","")
|
|
120
|
+
end
|
|
121
|
+
def noquote(name)
|
|
122
|
+
name.to_s.gsub(":","")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end # class DiagramGraph
|