umlify 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +17 -0
- data/lib/umlify.rb +2 -0
- data/lib/umlify/diagram.rb +31 -3
- data/lib/umlify/extension.rb +3 -0
- data/lib/umlify/parser_sexp.rb +7 -3
- data/lib/umlify/runner.rb +17 -7
- data/lib/umlify/uml_class.rb +33 -2
- data/lib/umlify/version.rb +1 -1
- data/test/diagram_test.rb +19 -11
- data/test/runner_test.rb +19 -0
- data/test/string_test.rb +15 -0
- data/test/uml_class_test.rb +53 -0
- metadata +8 -2
data/README.md
CHANGED
@@ -29,6 +29,9 @@ How to use
|
|
29
29
|
2. type: `umlify lib/*/*`
|
30
30
|
3. Open uml.html
|
31
31
|
|
32
|
+
If you want umlify to try to guess the types of the associations, use
|
33
|
+
`umlify -s lib/*/*` at step 2.
|
34
|
+
|
32
35
|
Example
|
33
36
|
-------
|
34
37
|
|
@@ -39,6 +42,7 @@ Here is umlify umlified:
|
|
39
42
|
Features
|
40
43
|
--------
|
41
44
|
|
45
|
+
* Tries to guess the types of the instance variables (smart mode)
|
42
46
|
* __new__ Use RubyParser instead of regular expression as of v1.0.0
|
43
47
|
* __new__ supports inhertiance (v0.4.2)
|
44
48
|
* supports associations (see "How to add associations to a diagram)
|
@@ -76,6 +80,19 @@ classes. However, if you want an association to be shown on your diagram simply
|
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
83
|
+
Smart mode
|
84
|
+
----------
|
85
|
+
|
86
|
+
If you use umlify with the `-s` or `--smart` option, it'll try to guess
|
87
|
+
the types of the associations based on the name of the instance
|
88
|
+
variables.
|
89
|
+
|
90
|
+
If you class variable is @duck, then it will try to create an
|
91
|
+
association with the "Duck" class, if it exists.
|
92
|
+
|
93
|
+
If your variable is @ducks, it will try to create an association with a
|
94
|
+
cardinality of '*' with a class called "Duck", if such a class exists.
|
95
|
+
|
79
96
|
Contribute
|
80
97
|
----------
|
81
98
|
|
data/lib/umlify.rb
CHANGED
@@ -11,6 +11,8 @@
|
|
11
11
|
# umlify is tool that generates uml from your ruby source files. It
|
12
12
|
# works using yUML.me web api.
|
13
13
|
#
|
14
|
+
# Copyright 2011 Michael Sokol
|
15
|
+
#
|
14
16
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
15
17
|
# of this software and associated documentation files (the "Software"), to deal
|
16
18
|
# in the Software without restriction, including without limitation the rights
|
data/lib/umlify/diagram.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Umlify
|
2
2
|
|
3
3
|
# Creates and store a yUML api string for generating diagram
|
4
|
-
# type of @statements: 1..* String
|
4
|
+
# * type of @statements: 1..* String
|
5
5
|
class Diagram
|
6
6
|
|
7
7
|
attr_accessor :statements
|
@@ -12,6 +12,7 @@ module Umlify
|
|
12
12
|
|
13
13
|
def create &blk
|
14
14
|
instance_eval &blk
|
15
|
+
self
|
15
16
|
end
|
16
17
|
|
17
18
|
# Adds the given statement to the @diagram array
|
@@ -24,8 +25,10 @@ module Umlify
|
|
24
25
|
|
25
26
|
@statements << statement.to_s
|
26
27
|
|
27
|
-
if statement.
|
28
|
-
|
28
|
+
if statement.children
|
29
|
+
statement.children.each do |child|
|
30
|
+
@statements << "[#{statement.name}]^[#{child.name}]"
|
31
|
+
end
|
29
32
|
end
|
30
33
|
|
31
34
|
unless statement.associations.empty?
|
@@ -42,6 +45,31 @@ module Umlify
|
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
48
|
+
# Sorts the statements array so that
|
49
|
+
# 1. Class definitions
|
50
|
+
# 2. Inheritance
|
51
|
+
# 3. Associations
|
52
|
+
# Otherwise, strange behavior can happen in the downloadd graph
|
53
|
+
def compute!
|
54
|
+
class_def = /\[[\w;?|!]*\]/
|
55
|
+
inheritance = /\[(.*?)\]\^\[(.*?)\]/
|
56
|
+
association = /\[.*\]-.*>\[.*\]/
|
57
|
+
|
58
|
+
@statements.sort! do |x, y|
|
59
|
+
|
60
|
+
if x =~ class_def and y =~ inheritance
|
61
|
+
-1
|
62
|
+
elsif x =~ class_def and y =~ association
|
63
|
+
-1
|
64
|
+
elsif x =~ inheritance and y =~ association
|
65
|
+
-1
|
66
|
+
else
|
67
|
+
1
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
45
73
|
# Returns the yuml.me uri
|
46
74
|
def get_uri
|
47
75
|
uri = '/diagram/class/'+@statements.join(", ")
|
data/lib/umlify/extension.rb
CHANGED
data/lib/umlify/parser_sexp.rb
CHANGED
@@ -4,7 +4,7 @@ module Umlify
|
|
4
4
|
|
5
5
|
# Parses files using S-Expressions given by the RubyParser gem
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# * type of @classes: 0..* UmlClass
|
8
8
|
#
|
9
9
|
class ParserSexp
|
10
10
|
|
@@ -28,7 +28,8 @@ module Umlify
|
|
28
28
|
(parse_file File.read(file)).each {|c| @classes << c}
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
# Removes duplicates between variables and associations in the class
|
32
|
+
@classes.each {|c| c.chomp! @classes}
|
32
33
|
end
|
33
34
|
|
34
35
|
# Parse the given string, and return the parsed classes
|
@@ -60,8 +61,11 @@ module Umlify
|
|
60
61
|
|
61
62
|
# Searching for a s(:const, :Const) right after the class name, which
|
62
63
|
# means the class inherits from a parents class, :Const
|
63
|
-
if class_s_exp[2] and class_s_exp[2][
|
64
|
+
if class_s_exp[2] and class_s_exp[2][0] == :const
|
64
65
|
uml_class.parent = class_s_exp[2][1].to_s
|
66
|
+
elsif class_s_exp[2] and class_s_exp[2][0] == :colon2
|
67
|
+
# If the parent class belongs to a module
|
68
|
+
uml_class.parent = class_s_exp[2][2].to_s
|
65
69
|
end
|
66
70
|
|
67
71
|
# Looks-up for instance methods
|
data/lib/umlify/runner.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
require 'net/http'
|
2
|
+
require 'optparse'
|
2
3
|
|
3
4
|
module Umlify
|
4
5
|
|
5
6
|
# Class to run to execute umlify. It parses the ruby sources provided
|
6
7
|
# and generates and save a uml diagram using yUML API.
|
7
|
-
#
|
8
|
-
# type of @diagram: Diagram
|
9
|
-
# type of @args: 0..* String
|
10
|
-
# type of @parser: ParserSexp
|
11
|
-
#
|
12
8
|
class Runner
|
13
9
|
|
10
|
+
attr_reader :smart_mode
|
11
|
+
|
14
12
|
# Takes as input an array with file names
|
15
13
|
def initialize args
|
16
14
|
@args = args
|
15
|
+
@smart_mode = false
|
17
16
|
end
|
18
17
|
|
19
18
|
# Runs the application
|
20
19
|
def run
|
20
|
+
parse_options
|
21
21
|
|
22
22
|
if @args.empty?
|
23
23
|
puts "Usage: umlify [source directory]"
|
@@ -29,16 +29,20 @@ module Umlify
|
|
29
29
|
if classes = @parser.parse_sources!
|
30
30
|
@diagram = Diagram.new
|
31
31
|
|
32
|
+
if @smart_mode
|
33
|
+
classes.each {|c| c.infer_types! classes}
|
34
|
+
end
|
35
|
+
|
32
36
|
@diagram.create do
|
33
37
|
classes.each {|c| add c}
|
34
|
-
end
|
38
|
+
end.compute!
|
35
39
|
|
36
40
|
puts "Downloading the image from yUML, it shouldn't be long."
|
37
41
|
|
38
42
|
image = download_image
|
39
43
|
save_to_file image
|
40
44
|
|
41
|
-
puts
|
45
|
+
puts 'http://yuml.me'+@diagram.get_uri
|
42
46
|
puts "Saved in uml.png"
|
43
47
|
else
|
44
48
|
puts "No ruby files in the directory"
|
@@ -47,6 +51,12 @@ module Umlify
|
|
47
51
|
|
48
52
|
end
|
49
53
|
|
54
|
+
def parse_options
|
55
|
+
OptionParser.new do |opts|
|
56
|
+
opts.on("-s", "--smart") { @smart_mode = true }
|
57
|
+
end.parse! @args
|
58
|
+
end
|
59
|
+
|
50
60
|
# Downloads the image of the uml diagram from yUML
|
51
61
|
def download_image
|
52
62
|
Net::HTTP.start("yuml.me", 80) do |http|
|
data/lib/umlify/uml_class.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Umlify
|
2
2
|
|
3
|
-
#
|
3
|
+
# Represents a parsed uml class
|
4
4
|
class UmlClass
|
5
|
-
attr_accessor :name, :variables, :methods, :associations, :parent
|
5
|
+
attr_accessor :name, :variables, :methods, :associations, :parent, :children
|
6
6
|
|
7
7
|
def initialize name
|
8
8
|
@name = name
|
@@ -11,6 +11,37 @@ module Umlify
|
|
11
11
|
@associations = {}
|
12
12
|
end
|
13
13
|
|
14
|
+
# Deletes variables from the @variables array if they appear
|
15
|
+
# in an association.
|
16
|
+
# Sets the @children variable
|
17
|
+
def chomp! classes
|
18
|
+
@variables = @variables - @associations.keys unless @associations.nil?
|
19
|
+
@children = classes.select do |c|
|
20
|
+
if c.parent == @name
|
21
|
+
c.parent = nil
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Tries to create an association with the attributes in @variables.
|
28
|
+
def infer_types! classes
|
29
|
+
class_names = classes.collect {|c| c.name}
|
30
|
+
@variables.each do |attribute|
|
31
|
+
|
32
|
+
if class_names.include? attribute.classify
|
33
|
+
# A type has match with the attribute's name
|
34
|
+
@associations[attribute] = attribute.classify
|
35
|
+
|
36
|
+
# If it's a plural, adds a cardinality
|
37
|
+
if attribute == attribute.pluralize
|
38
|
+
@associations[attribute+'-n'] = '*'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
chomp! classes
|
43
|
+
end
|
44
|
+
|
14
45
|
def to_s
|
15
46
|
'['+@name+'|'+
|
16
47
|
@variables.collect{|var| var}.join(";")+'|'+
|
data/lib/umlify/version.rb
CHANGED
data/test/diagram_test.rb
CHANGED
@@ -62,19 +62,19 @@ class DiagramTest < Test::Unit::TestCase
|
|
62
62
|
assert @diagram.statements.include? '[Unicorn]-foo 1..*>[Bar]'
|
63
63
|
end
|
64
64
|
|
65
|
-
should "add UmlClass with parent to diagrams" do
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
# should "add UmlClass with parent to diagrams" do
|
66
|
+
# test_uml_class = Umlify::UmlClass.new 'Unicorn'
|
67
|
+
# test_uml_class.variables << 'foo_variable'
|
68
|
+
# test_uml_class.methods << 'bar_method'
|
69
|
+
# test_uml_class.parent = "Foo"
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
# @diagram.create do
|
72
|
+
# add test_uml_class
|
73
|
+
# end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
end
|
75
|
+
# assert @diagram.statements.include? '[Unicorn|foo_variable|bar_method]'
|
76
|
+
# assert @diagram.statements.include? '[Foo]^[Unicorn]'
|
77
|
+
# end
|
78
78
|
|
79
79
|
should "get the yuml uri" do
|
80
80
|
test_uml_class = Umlify::UmlClass.new 'Unicorn'
|
@@ -89,6 +89,14 @@ class DiagramTest < Test::Unit::TestCase
|
|
89
89
|
@diagram.get_uri
|
90
90
|
end
|
91
91
|
|
92
|
+
should "sort the statements so that the class declarations are first, then
|
93
|
+
the inheritance, then the associations" do
|
94
|
+
|
95
|
+
@diagram.statements = ['[Foo]^[Unicorn]', '[Unicorn]-foo 1..*>[Bar]', '[Unicorn|foo_variable|bar_method]']
|
96
|
+
@diagram.compute!
|
97
|
+
assert_equal ['[Unicorn|foo_variable|bar_method]', '[Foo]^[Unicorn]', '[Unicorn]-foo 1..*>[Bar]'], @diagram.statements
|
98
|
+
end
|
99
|
+
|
92
100
|
end
|
93
101
|
end
|
94
102
|
|
data/test/runner_test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'shoulda'
|
2
|
+
require 'umlify'
|
3
|
+
|
4
|
+
class RunnerTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "Runner" do
|
7
|
+
|
8
|
+
should "be in smart mode when passed -s or --smart as an option" do
|
9
|
+
r = Umlify::Runner.new(["-s"])
|
10
|
+
r.run
|
11
|
+
assert r.smart_mode
|
12
|
+
r = Umlify::Runner.new(["--smart"])
|
13
|
+
r.run
|
14
|
+
assert r.smart_mode
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/test/string_test.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'shoulda'
|
2
|
+
require 'umlify'
|
3
|
+
|
4
|
+
class StringTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "String" do
|
7
|
+
|
8
|
+
should "should respond to #classify" do
|
9
|
+
assert_equal "Duck", "ducks".classify
|
10
|
+
assert_equal "PepperoniPizza", "pepperoni_pizzas".classify
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'shoulda'
|
2
|
+
require 'umlify'
|
3
|
+
|
4
|
+
class UmlClassTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "UmlClass" do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@class = Umlify::UmlClass.new 'Farm'
|
10
|
+
@class.variables = ['ducks', 'some_cows', 'farm_house']
|
11
|
+
@class.associations['ducks'] = 'Duck'
|
12
|
+
end
|
13
|
+
|
14
|
+
should "delete variables from the @variables array if they exist in association" do
|
15
|
+
@class.chomp! []
|
16
|
+
assert_equal false, @class.variables.include?('ducks')
|
17
|
+
end
|
18
|
+
|
19
|
+
should "create a list of children, given all the types available" do
|
20
|
+
foo = Umlify::UmlClass.new "Foo"
|
21
|
+
foo.parent = "Umlify"
|
22
|
+
|
23
|
+
bar = Umlify::UmlClass.new "Bar"
|
24
|
+
bar.parent = "Umlify"
|
25
|
+
|
26
|
+
umlify = Umlify::UmlClass.new "Umlify"
|
27
|
+
|
28
|
+
classes = [foo, bar, umlify]
|
29
|
+
classes.each {|c| c.chomp!(classes)}
|
30
|
+
|
31
|
+
assert umlify.children.include? foo
|
32
|
+
assert umlify.children.include? bar
|
33
|
+
assert_equal nil, foo.parent
|
34
|
+
assert_equal nil, bar.parent
|
35
|
+
end
|
36
|
+
|
37
|
+
should "be able to infer types for associations with the variables in @variables" do
|
38
|
+
classes = [Umlify::UmlClass.new("Foo"), Umlify::UmlClass.new("Bar")]
|
39
|
+
foo_bar = Umlify::UmlClass.new "FooBar"
|
40
|
+
foo_bar.variables = ["foo", "bars", "args"]
|
41
|
+
classes << foo_bar
|
42
|
+
|
43
|
+
foo_bar.infer_types! classes
|
44
|
+
|
45
|
+
assert_equal 'Foo', foo_bar.associations['foo']
|
46
|
+
assert_equal 'Bar', foo_bar.associations['bars']
|
47
|
+
assert_equal '*', foo_bar.associations['bars-n']
|
48
|
+
assert_equal nil, foo_bar.associations['args']
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: umlify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.
|
5
|
+
version: 1.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Michael Sokol
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-03-
|
13
|
+
date: 2011-03-10 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -36,6 +36,9 @@ files:
|
|
36
36
|
- lib/umlify/diagram.rb
|
37
37
|
- test/parser_test.rb
|
38
38
|
- test/diagram_test.rb
|
39
|
+
- test/uml_class_test.rb
|
40
|
+
- test/runner_test.rb
|
41
|
+
- test/string_test.rb
|
39
42
|
- test/parser_sexp_test.rb
|
40
43
|
has_rdoc: true
|
41
44
|
homepage: https://github.com/mikaa123/umlify
|
@@ -67,5 +70,8 @@ specification_version: 3
|
|
67
70
|
summary: umlify is a tool that creates class diagrams from your code.
|
68
71
|
test_files:
|
69
72
|
- test/parser_test.rb
|
73
|
+
- test/uml_class_test.rb
|
70
74
|
- test/parser_sexp_test.rb
|
71
75
|
- test/diagram_test.rb
|
76
|
+
- test/runner_test.rb
|
77
|
+
- test/string_test.rb
|