umlify 1.0.0 → 1.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.
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