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 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
 
@@ -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
@@ -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.parent
28
- @statements << "[#{statement.parent}]^[#{statement.name}]"
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(", ")
@@ -1,3 +1,5 @@
1
+ require 'active_support/inflector'
2
+
1
3
  # Extends the String to add a String#each method so that
2
4
  # strings can be read just like files, line-by-line.
3
5
 
@@ -10,4 +12,5 @@ class String
10
12
  end
11
13
 
12
14
  end
15
+
13
16
  end
@@ -4,7 +4,7 @@ module Umlify
4
4
 
5
5
  # Parses files using S-Expressions given by the RubyParser gem
6
6
  #
7
- # type of @classes: 0..* UmlClass
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
- @classes
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][1] and class_s_exp[2][0] == :const
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
@@ -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 @diagram.get_uri
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|
@@ -1,8 +1,8 @@
1
1
  module Umlify
2
2
 
3
- # Represent a parsed uml class
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(";")+'|'+
@@ -1,3 +1,3 @@
1
1
  module Umlify
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -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
- 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"
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
- @diagram.create do
72
- add test_uml_class
73
- end
71
+ # @diagram.create do
72
+ # add test_uml_class
73
+ # end
74
74
 
75
- assert @diagram.statements.include? '[Unicorn|foo_variable|bar_method]'
76
- assert @diagram.statements.include? '[Foo]^[Unicorn]'
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
 
@@ -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
+
@@ -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.0.0
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-06 00:00:00 -05:00
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