umlify 0.3.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.
- data/README +19 -0
- data/Rakefile +9 -0
- data/bin/umlify +5 -0
- data/lib/umlify/diagram.rb +44 -0
- data/lib/umlify/extension.rb +13 -0
- data/lib/umlify/parser.rb +79 -0
- data/lib/umlify/runner.rb +72 -0
- data/lib/umlify/uml_class.rb +23 -0
- data/lib/umlify/version.rb +3 -0
- data/lib/umlify.rb +6 -0
- data/test/diagram_test.rb +68 -0
- data/test/parser_test.rb +67 -0
- metadata +68 -0
data/README
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
_ _ __
|
|
2
|
+
| (_)/ _|
|
|
3
|
+
_ _ _ __ ___ | |_| |_ _ _
|
|
4
|
+
| | | | '_ ` _ \| | | _| | | |
|
|
5
|
+
| |_| | | | | | | | | | | |_| |
|
|
6
|
+
\__,_|_| |_| |_|_|_|_| \__, |
|
|
7
|
+
__/ |
|
|
8
|
+
|___/
|
|
9
|
+
|
|
10
|
+
umlify is a tool that creates
|
|
11
|
+
uml class diagrams from your code
|
|
12
|
+
|
|
13
|
+
<https://github.com/mikaa123/umlify>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
Introduction
|
|
17
|
+
------------
|
|
18
|
+
|
|
19
|
+
umlify takes your project's source code and creates an uml class diagram out of it.
|
data/Rakefile
ADDED
data/bin/umlify
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Umlify
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Creates and store a yUML api string for generating diagram
|
|
5
|
+
#
|
|
6
|
+
class Diagram
|
|
7
|
+
|
|
8
|
+
# Array containing yUML DSL statements
|
|
9
|
+
attr_accessor :statements
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@statements = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create &blk
|
|
16
|
+
instance_eval &blk
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Adds the given statement to the @diagram array
|
|
20
|
+
def add statement
|
|
21
|
+
# TODO: Add some sort of validation
|
|
22
|
+
|
|
23
|
+
@statements << statement if statement.is_a? String
|
|
24
|
+
if statement.is_a? UmlClass
|
|
25
|
+
|
|
26
|
+
if statement.associations.empty?
|
|
27
|
+
@statements << statement.to_s
|
|
28
|
+
else
|
|
29
|
+
@statements << statement.to_s
|
|
30
|
+
|
|
31
|
+
statement.associations.each do |name, type|
|
|
32
|
+
@statements << "[#{statement.name}]-#{name}>[#{type}]"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Dumps the html of the diagram
|
|
40
|
+
def export
|
|
41
|
+
'<img src="http://yuml.me/diagram/class/'+@statements.join(", ")+'" />'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Umlify
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Parser is responsible for parsing ruby source files and building an array
|
|
5
|
+
# of uml classes
|
|
6
|
+
#
|
|
7
|
+
class Parser
|
|
8
|
+
|
|
9
|
+
# An array containing all the parsed classes
|
|
10
|
+
# type: UmlClass
|
|
11
|
+
attr_accessor :classes
|
|
12
|
+
|
|
13
|
+
# files should be an array containing file names with the correct path
|
|
14
|
+
def initialize files
|
|
15
|
+
@files = files
|
|
16
|
+
@classes = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Parses the source code of the files in @files
|
|
20
|
+
# to build uml classes. Returns an array containing all the
|
|
21
|
+
# parsed classes or nil if no ruby file were found in the
|
|
22
|
+
# @files array.
|
|
23
|
+
def parse_sources!
|
|
24
|
+
|
|
25
|
+
@source_files = @files.select {|f| f.match /\.rb/}
|
|
26
|
+
return nil if @source_files.empty?
|
|
27
|
+
|
|
28
|
+
@source_files.each do |file|
|
|
29
|
+
f = File.open file, 'r'
|
|
30
|
+
(parse_file f).each {|c| @classes << c}
|
|
31
|
+
f.close
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@classes
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Parse the given file or string, and return the parsed classes
|
|
38
|
+
def parse_file file
|
|
39
|
+
classes_in_file = []
|
|
40
|
+
current_class = nil
|
|
41
|
+
type_annotation = nil
|
|
42
|
+
|
|
43
|
+
file.each do |line|
|
|
44
|
+
|
|
45
|
+
# This parses the classes
|
|
46
|
+
line.match(/^\s*class ([\w]*)\b/) do |m|
|
|
47
|
+
current_class = UmlClass.new m[1]
|
|
48
|
+
classes_in_file << current_class
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if current_class
|
|
52
|
+
|
|
53
|
+
# This parses the @variables
|
|
54
|
+
line.match(/@([\w]*)\b/) do |m|
|
|
55
|
+
current_class.variables << m[1] unless current_class.variables.include? m[1]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# This parses the methods
|
|
59
|
+
line.match(/def ([\w]*)\b/) do |m|
|
|
60
|
+
current_class.methods << m[1] unless current_class.methods.include? m[1]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# This raises the type_annotation flag
|
|
64
|
+
line.match(/# type: ([\w]*)\b/) {|m| type_annotation = m[1]}
|
|
65
|
+
|
|
66
|
+
# This adds an association to the current class, using the type_annotation
|
|
67
|
+
# if type_annotation has been set
|
|
68
|
+
line.match(/attr_accessor :([\w]*)\b/) do |m|
|
|
69
|
+
current_class.associations[m[1]] = type_annotation
|
|
70
|
+
type_annotation = nil
|
|
71
|
+
end if type_annotation
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
classes_in_file
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
3
|
+
module Umlify
|
|
4
|
+
|
|
5
|
+
###
|
|
6
|
+
# This class is instanciated and run by the bin file
|
|
7
|
+
#
|
|
8
|
+
class Runner
|
|
9
|
+
|
|
10
|
+
# Contains the options from the command line
|
|
11
|
+
attr_reader :args
|
|
12
|
+
|
|
13
|
+
# type: Parser
|
|
14
|
+
attr_accessor :parser
|
|
15
|
+
|
|
16
|
+
# type: Diagram
|
|
17
|
+
attr_accessor :diagram
|
|
18
|
+
|
|
19
|
+
# Takes as input an array with the command line options
|
|
20
|
+
def initialize args
|
|
21
|
+
@args = args
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Runs the application
|
|
25
|
+
def run
|
|
26
|
+
@args.push "-h" if @args.empty?
|
|
27
|
+
|
|
28
|
+
if @args[0][0] == '-'
|
|
29
|
+
|
|
30
|
+
OptionParser.new do |opts|
|
|
31
|
+
opts.banner = "Usage: umlify [option]"
|
|
32
|
+
opts.on("-h", "--help", "Shows this") do
|
|
33
|
+
puts opts
|
|
34
|
+
end
|
|
35
|
+
end.parse! @args
|
|
36
|
+
|
|
37
|
+
else
|
|
38
|
+
puts "umlifying"
|
|
39
|
+
if files = get_files_from_dir(ARGV[0])
|
|
40
|
+
puts "about to parse..."
|
|
41
|
+
@parser = Parser.new files
|
|
42
|
+
|
|
43
|
+
if classes = p.parse_sources!
|
|
44
|
+
@diagram = Diagram.new
|
|
45
|
+
|
|
46
|
+
diagram.create do
|
|
47
|
+
classes.each {|c| add c}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
File.open("uml.html", 'w') do |file|
|
|
51
|
+
file << diagram.export
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
puts "Saved in uml.html"
|
|
55
|
+
else
|
|
56
|
+
puts "No ruby files in the directory"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
else
|
|
60
|
+
puts "empty directory"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def get_files_from_dir directory
|
|
69
|
+
Dir[directory] unless Dir[directory].empty?
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Umlify
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Represent a parsed uml class
|
|
5
|
+
#
|
|
6
|
+
class UmlClass
|
|
7
|
+
attr_accessor :name, :variables, :methods, :associations
|
|
8
|
+
|
|
9
|
+
def initialize name
|
|
10
|
+
@name = name
|
|
11
|
+
@variables = []
|
|
12
|
+
@methods = []
|
|
13
|
+
@associations = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
'['+@name+'|'+
|
|
18
|
+
@variables.collect{|var| var}.join(";")+'|'+
|
|
19
|
+
@methods.collect{|met| met}.join(";")+']'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
data/lib/umlify.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'shoulda'
|
|
2
|
+
require 'umlify'
|
|
3
|
+
|
|
4
|
+
class DiagramTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
context "Diagram" do
|
|
7
|
+
|
|
8
|
+
setup do
|
|
9
|
+
@diagram = Umlify::Diagram.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
should "respond to create" do
|
|
13
|
+
assert_respond_to @diagram, :create
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
should "add Strings statements to diagram" do
|
|
17
|
+
test_statement = '[foo]->[bar]'
|
|
18
|
+
@diagram.create do
|
|
19
|
+
add test_statement
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
assert_equal ['[foo]->[bar]'], @diagram.statements
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should "add UmlClass without associations to diagrams" do
|
|
26
|
+
test_uml_class = Umlify::UmlClass.new 'Unicorn'
|
|
27
|
+
test_uml_class.variables << 'foo_variable'
|
|
28
|
+
test_uml_class.methods << 'bar_method'
|
|
29
|
+
|
|
30
|
+
@diagram.create do
|
|
31
|
+
add test_uml_class
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
assert @diagram.statements.include? '[Unicorn|foo_variable|bar_method]'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
should "add UmlClass with associations to diagrams" do
|
|
38
|
+
test_uml_class = Umlify::UmlClass.new 'Unicorn'
|
|
39
|
+
test_uml_class.variables << 'foo_variable'
|
|
40
|
+
test_uml_class.methods << 'bar_method'
|
|
41
|
+
test_uml_class.associations['foo'] = 'Bar'
|
|
42
|
+
test_uml_class.associations['chunky'] = 'Bacon'
|
|
43
|
+
|
|
44
|
+
@diagram.create do
|
|
45
|
+
add test_uml_class
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
assert @diagram.statements.include? '[Unicorn|foo_variable|bar_method]'
|
|
49
|
+
assert @diagram.statements.include? '[Unicorn]-foo>[Bar]'
|
|
50
|
+
assert @diagram.statements.include? '[Unicorn]-chunky>[Bacon]'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
should "export th yUML html" do
|
|
54
|
+
test_uml_class = Umlify::UmlClass.new 'Unicorn'
|
|
55
|
+
test_uml_class.variables << 'foo_variable'
|
|
56
|
+
test_uml_class.methods << 'bar_method'
|
|
57
|
+
|
|
58
|
+
@diagram.create do
|
|
59
|
+
add test_uml_class
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
assert_equal '<img src="http://yuml.me/diagram/class/[Unicorn|foo_variable|bar_method]" />',
|
|
63
|
+
@diagram.export
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
data/test/parser_test.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'shoulda'
|
|
2
|
+
require 'umlify'
|
|
3
|
+
|
|
4
|
+
class ParserTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
context "Parser" do
|
|
7
|
+
|
|
8
|
+
setup do
|
|
9
|
+
fixture = ["somefile.rb", "someotherfile.rb"]
|
|
10
|
+
@p = Umlify::Parser.new fixture
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
should "respond to parse_sources!" do
|
|
14
|
+
assert_respond_to @p, :parse_sources!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
should "return nil if no source file in the given directory" do
|
|
18
|
+
parser = Umlify::Parser.new ["not_source", "still_not_source"]
|
|
19
|
+
assert_equal nil, parser.parse_sources!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
should "parse class names" do
|
|
23
|
+
test = <<-END_FILE
|
|
24
|
+
class AClassName
|
|
25
|
+
end
|
|
26
|
+
END_FILE
|
|
27
|
+
assert_equal 'AClassName', @p.parse_file(test)[0].name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
should "parse @variables in classes" do
|
|
31
|
+
test = <<-END_FILE
|
|
32
|
+
class Bar
|
|
33
|
+
def foo
|
|
34
|
+
@a_variable = "foo"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
END_FILE
|
|
38
|
+
bar = @p.parse_file(test)[0]
|
|
39
|
+
assert_instance_of Umlify::UmlClass, bar
|
|
40
|
+
assert bar.variables.include? "a_variable"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
should "parse attr_accessor when preceeded by a # type: Type annotation" do
|
|
44
|
+
test = <<-END_FILE
|
|
45
|
+
class Bar
|
|
46
|
+
|
|
47
|
+
# type: FooBar
|
|
48
|
+
attr_accessor :unicorn
|
|
49
|
+
|
|
50
|
+
def foo
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
END_FILE
|
|
54
|
+
bar = @p.parse_file(test)[0]
|
|
55
|
+
assert_instance_of Umlify::UmlClass, bar
|
|
56
|
+
assert_equal "FooBar", bar.associations['unicorn']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
should "return an array of UmlClasses when the parsing is done" do
|
|
60
|
+
p = Umlify::Parser.new Dir[File.dirname(__FILE__)+'/fixtures/*']
|
|
61
|
+
parsed_classes = p.parse_sources!
|
|
62
|
+
puts "here : #{parsed_classes}"
|
|
63
|
+
assert_equal 3, parsed_classes.count
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: umlify
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 0.3.0
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Michael Sokol
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-03-02 00:00:00 -05:00
|
|
14
|
+
default_executable:
|
|
15
|
+
dependencies: []
|
|
16
|
+
|
|
17
|
+
description:
|
|
18
|
+
email: mikaa123@gmail.com
|
|
19
|
+
executables:
|
|
20
|
+
- umlify
|
|
21
|
+
extensions: []
|
|
22
|
+
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
|
|
25
|
+
files:
|
|
26
|
+
- Rakefile
|
|
27
|
+
- README
|
|
28
|
+
- bin/umlify
|
|
29
|
+
- lib/umlify.rb
|
|
30
|
+
- lib/umlify/version.rb
|
|
31
|
+
- lib/umlify/runner.rb
|
|
32
|
+
- lib/umlify/parser.rb
|
|
33
|
+
- lib/umlify/extension.rb
|
|
34
|
+
- lib/umlify/uml_class.rb
|
|
35
|
+
- lib/umlify/diagram.rb
|
|
36
|
+
- test/parser_test.rb
|
|
37
|
+
- test/diagram_test.rb
|
|
38
|
+
has_rdoc: true
|
|
39
|
+
homepage: https://github.com/mikaa123/umlify
|
|
40
|
+
licenses: []
|
|
41
|
+
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options: []
|
|
44
|
+
|
|
45
|
+
require_paths:
|
|
46
|
+
- lib
|
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
|
+
none: false
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: "0"
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
none: false
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: "0"
|
|
59
|
+
requirements: []
|
|
60
|
+
|
|
61
|
+
rubyforge_project:
|
|
62
|
+
rubygems_version: 1.5.0
|
|
63
|
+
signing_key:
|
|
64
|
+
specification_version: 3
|
|
65
|
+
summary: umlify is a tool that creates class diagrams from your code.
|
|
66
|
+
test_files:
|
|
67
|
+
- test/parser_test.rb
|
|
68
|
+
- test/diagram_test.rb
|