ruby-uml 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,61 @@
1
+ = Goal
2
+
3
+ <tt>ruby-uml</tt> tries to trace different aspects of an existing application,
4
+ which is intended to provide support for refactorisations by generating
5
+ UML-graphs.
6
+
7
+ <tt>ruby-uml</tt> is able to generate textual representations of the gathered
8
+ informations.
9
+
10
+ These representations include pic- or dot-code that can be converted to images
11
+ by their corresponding applications.
12
+
13
+ At this time <tt>ruby-uml</tt> is able to generate sequence- and class-diagrams.
14
+
15
+ As you won't get that much information out of the trace, the resulting dot- or
16
+ pic-files are intended to be as readable as possible, so that manual
17
+ corrections/additions are made as easy as possible.
18
+
19
+ = Dependencies
20
+ == Gem dependencies:
21
+
22
+ diff-lcs:: >= 1.1.2
23
+
24
+ == Other dependencies:
25
+
26
+ ruby:: ruby-uml is tested with 1.8.5
27
+
28
+ aspectr:: There are three aspectr projects known to me:
29
+
30
+ 0.3.5:: http://sourceforge.net/projects/aspectr/
31
+
32
+ 0.3.6:: http://rubyforge.org/projects/aspectr/
33
+
34
+ 0.4.0:: http://sourceforge.net/projects/aspectr-fork/
35
+
36
+ - None of them seem to be maintained anymore.
37
+ - Everyone provides the functionality needed for ruby-uml.
38
+ - Everyone seems to have issues on ruby 1.8.5.
39
+ In the additional folder i provide a patch for 0.4.0
40
+ which resolved all issues for me on linux and cygwin.
41
+
42
+ UMLGraph:: http://www.spinellis.gr/sw/umlgraph/
43
+
44
+ ruby-uml uses the sequence.pic file from the UMLGraph Project
45
+ for generating sequence-diagrams out of pic files.
46
+
47
+ ruby-uml is tested with 4.6.
48
+
49
+ GNU Plotutils:: http://www.gnu.org/software/plotutils/
50
+
51
+ Plotutils 'pic2plot' is used for generating
52
+ sequence-diagram-images out of pic files.
53
+
54
+ ruby-uml is tested with 2.4.1.
55
+
56
+ Graphviz:: http://graphviz.org/
57
+
58
+ Grapphviz 'dot' command is used to generate class-diagram-images
59
+ out of dot files.
60
+
61
+ ruby-uml is tested with 2.12.
@@ -0,0 +1,53 @@
1
+ diff -uNr aspectr-0-4-0-orig/install.rb aspectr-0-4-0/install.rb
2
+ --- aspectr-0-4-0-orig/install.rb 2007-02-10 15:36:50.229995653 +0100
3
+ +++ aspectr-0-4-0/install.rb 2007-02-10 16:02:55.456951649 +0100
4
+ @@ -8,7 +8,7 @@
5
+ $libdir = File.join(CONFIG["libdir"], "ruby", $version)
6
+
7
+ $bindir = CONFIG["bindir"]
8
+ -$sitedir = CONFIG["sitedir"]
9
+ +$sitedir = CONFIG["sitelibdir"]
10
+
11
+ installdir = "" # Use top-level! Is this good?
12
+
13
+ diff -uNr aspectr-0-4-0-orig/lib/aspectr.rb aspectr-0-4-0/lib/aspectr.rb
14
+ --- aspectr-0-4-0-orig/lib/aspectr.rb 2007-02-10 15:36:50.238995384 +0100
15
+ +++ aspectr-0-4-0/lib/aspectr.rb 2007-02-10 15:38:00.062553460 +0100
16
+ @@ -27,7 +27,7 @@
17
+ AROUND = :AROUND
18
+
19
+ def initialize(never_wrap = "^$ ")
20
+ - @never_wrap = /^__|^send$|^id$|^class$|#{never_wrap}/
21
+ + @never_wrap = /^__|^send$|^id$|^object_id$|^class$|#{never_wrap}/
22
+ end
23
+
24
+ def wrap(target, pre, post, *args, &condition)
25
+ @@ -146,7 +146,7 @@
26
+ aspect.send(advice, method, *args)
27
+ rescue Exception
28
+ a = $!
29
+ - raise AspectRException, "#{a.type} '#{a}' in advice #{advice}"
30
+ + raise AspectRException, "#{a.class} '#{a}' in advice #{advice}"
31
+ end
32
+ end
33
+ end
34
+ @@ -201,8 +201,8 @@
35
+ begin
36
+ exit_status = nil
37
+ #{__aop_advice_call_syntax(PRE, method, args)}
38
+ - exit_status = []
39
+ - return (exit_status.push(#{__aop_around_advice_call_syntax(method, mangled_method, args)}).last)
40
+ + exit_status = #{__aop_around_advice_call_syntax(method, mangled_method, args)}
41
+ + return exit_status
42
+ rescue Exception
43
+ puts \"got exception: \#{$!}\"
44
+ exit_status = true
45
+ @@ -246,7 +246,7 @@
46
+ end
47
+
48
+ def __aop_mangle(method)
49
+ - "__aop__#{self.object_id}_#{method.object_id}"['-'] = '_'
50
+ + "__aop__#{self.object_id}_#{method.object_id}".gsub(/-/, '_')
51
+ end
52
+
53
+ def __aop_alias(new, old, private = true)
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ require 'rubygems'
6
+ gem 'diff-lcs'
7
+ require 'diff/lcs'
8
+
9
+ # limitations:
10
+ # only one command per line
11
+
12
+ # finds used object_names in array
13
+ def find_used_object_names(array)
14
+ result = []
15
+ array.each do |line|
16
+ case line.strip
17
+ when /^(object|placeholder_object|pobject|actor|complete|active|inactive|delete|lifeline_constraint|lconstraint|lconstraint_below|begin_frame|end_frame|comment|connect_to_comment)[ ]*\(([^\), ]+)/
18
+ result << $2
19
+ when /^(message|return_message|rmessage|create_message|cmessage|destroy_message|dmessage)[ ]*\(([^, ]+),([^\), ]+)/
20
+ result << $2
21
+ result << $3
22
+ end
23
+ end
24
+ result.uniq
25
+ end
26
+
27
+ # find left- and rightmost object from used in objects
28
+ # objects is array with order
29
+ def find_frame_objects(used, objects)
30
+ indexes = used.collect { |u| objects.index(u) }
31
+ return objects[indexes.min], objects[indexes.max]
32
+ end
33
+
34
+ def find_lines(array, regexp)
35
+ result = []
36
+ array.each do |line|
37
+ case line.strip
38
+ when regexp
39
+ result << line
40
+ end
41
+ end
42
+ result
43
+ end
44
+
45
+ # finds lines with object_definitions
46
+ def find_object_definitions(array)
47
+ find_lines array, /^(object|placeholder_object|pobject|actor)[ ]*\(/
48
+ end
49
+
50
+ def find_copies(array)
51
+ find_lines array, /^copy/
52
+ end
53
+
54
+ def find_object_completions(array)
55
+ find_lines array, /^complete[ ]*\(/
56
+ end
57
+
58
+ def find_messages(array)
59
+ find_lines array, /^(message|return_message|rmessage|create_message|cmessage|destroy_message|dmessage|active|inactive|delete|lifeline_constraint|lconstraint|lconstraint_below|begin_frame|end_frame|comment|connect_to_comment)[ ]*\(/
60
+ end
61
+
62
+ def find_variables(array)
63
+ find_lines array, /=/
64
+ end
65
+
66
+ def read_file(name)
67
+ IO.readlines(name)
68
+ end
69
+
70
+ def write_file(name, content)
71
+ open(name, 'w') do |f|
72
+ f.write content
73
+ end
74
+ end
75
+
76
+ def make_frame(content, all, name, label)
77
+ result = []
78
+ unless content.empty?
79
+ bonds = find_frame_objects(find_used_object_names(content), all)
80
+ name = "#{name}_#{$frame_count += 1}"
81
+ result << "step();\n"
82
+ result << "begin_frame(#{bonds.first}, #{name}, \"#{label}\");\n"
83
+ result << "step();\n"
84
+ result += content
85
+ result << "step();\n"
86
+ result << "end_frame(#{bonds.last}, #{name});\n"
87
+ result << "step();\n"
88
+ end
89
+ result
90
+ end
91
+
92
+ def prepare_messages(left, right, all_objects)
93
+ result = []
94
+ eins = []
95
+ zwei = []
96
+ Diff::LCS.sdiff(left, right).each do |line|
97
+ case line.action
98
+ when '='
99
+ result << make_frame(eins, all_objects, 'Eins', $options[:left_title])
100
+ result << make_frame(zwei, all_objects, 'Zwei', $options[:right_title])
101
+ eins = []
102
+ zwei = []
103
+ result << line.old_element
104
+ when '!'
105
+ eins << line.old_element
106
+ zwei << line.new_element
107
+ when '+'
108
+ zwei << line.new_element
109
+ when '-'
110
+ eins << line.old_element
111
+ end
112
+ end
113
+ result << make_frame(eins, all_objects, 'Eins', $options[:left_title])
114
+ result << make_frame(zwei, all_objects, 'Zwei', $options[:right_title])
115
+ result
116
+ end
117
+
118
+
119
+
120
+
121
+
122
+ $options = {
123
+ :left_title => 'before',
124
+ :right_title => 'after'
125
+ }
126
+
127
+ OptionParser.new do |opts|
128
+ opts.banner = "Usage: #$0 [options] file1 file2"
129
+
130
+ opts.on('--left-title MANDATORY', 'Title of left file') do |string|
131
+ $options[:left_title] = string
132
+ end
133
+
134
+ opts.on('--right-title MANDATORY', 'Title of right file') do |string|
135
+ $options[:right_title] = string
136
+ end
137
+
138
+ opts.on_tail('-h', '--help', 'Show this message') do
139
+ puts opts
140
+ exit
141
+ end
142
+
143
+ end.parse!
144
+
145
+ if ARGV.length != 2
146
+ puts 'Too view Files given'
147
+ exit
148
+ end
149
+
150
+ $frame_count = 0
151
+
152
+ f1 = read_file(ARGV[0])
153
+ v1 = find_variables(f1)
154
+ c1 = find_copies(f1)
155
+ od1 = find_object_definitions(f1)
156
+ m1 = find_messages(f1)
157
+ oc1 = find_object_completions(f1)
158
+
159
+ f2 = read_file(ARGV[1])
160
+ v2 = find_variables(f2)
161
+ c2 = find_copies(f2)
162
+ od2 = find_object_definitions(f2)
163
+ m2 = find_messages(f2)
164
+ oc2 = find_object_completions(f2)
165
+
166
+ # so werden neue zeilen von File 2 an erste angehängt
167
+ c = (c1 + c2).uniq
168
+ v = (v1 + v2).uniq
169
+ od = (od1 + od2).uniq
170
+ o = find_used_object_names(od)
171
+ oc = (oc1 + oc2).uniq
172
+
173
+ result = []
174
+ result << ".PS\n"
175
+ result += c
176
+ result += v
177
+ result += od
178
+ result << "step();\n"
179
+ result += prepare_messages(m1, m2, o)
180
+ result << "step();\n"
181
+ result += oc
182
+ result << "\n.PE"
183
+ puts result
@@ -0,0 +1,69 @@
1
+ $:.push '../lib'
2
+
3
+ require 'uml/class_diagram'
4
+
5
+ module Automotive
6
+
7
+ class Car
8
+ def drive
9
+ 2
10
+ end
11
+
12
+ def steer
13
+ 'hard'
14
+ end
15
+
16
+ def honk
17
+ 'toot'
18
+ end
19
+ end
20
+
21
+ module FourWheelDrive
22
+ def drive
23
+ 4
24
+ end
25
+ end
26
+
27
+ module ServoSteering
28
+ def steer
29
+ 'easy'
30
+ end
31
+ end
32
+
33
+ class Rover < Car
34
+ include FourWheelDrive
35
+ include ServoSteering
36
+
37
+ def honk
38
+ 'roar'
39
+ end
40
+ end
41
+
42
+ class Mini < Car
43
+ end
44
+
45
+ class Garage < Array
46
+ def impress_neighbours
47
+ each do |car|
48
+ car.honk
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ cd = UML::ClassDiagram.new :show_private_methods => false,
56
+ :show_protected_methods => false,
57
+ :show_public_methods => true,
58
+ :cluster_packages => true,
59
+ :include => [/^Automotive/]
60
+
61
+ cd.include Automotive::Mini
62
+
63
+ garage = Automotive::Garage.new
64
+ garage << Automotive::Rover.new
65
+ garage.impress_neighbours
66
+
67
+ File.open('class_diagram_example.dot', 'w') { |file|
68
+ file.write cd.to_dot
69
+ }
@@ -0,0 +1,47 @@
1
+ $:.push '../lib'
2
+
3
+ require 'uml/highlevel_backtracer'
4
+ require 'temperature'
5
+
6
+ ts = TempSensor.new
7
+ ta = TempAlarm.new(ts)
8
+
9
+ class HighlevelTest
10
+
11
+ def initialize
12
+ @tracer = UML::HighlevelBacktracer.new
13
+ @tracer.add_observer self
14
+ end
15
+
16
+ def include(*args)
17
+ @tracer.include(*args)
18
+ end
19
+
20
+ def update(event, tracer)
21
+ right = tracer.call_stack[-1]
22
+ left = tracer.call_stack[-2]
23
+ case event
24
+ when :call
25
+ if left
26
+ print "#{left[:real_klass]}.#{left[:method_symbol]}"
27
+ end
28
+ print " -> "
29
+ print "#{right[:real_klass]}.#{right[:method_symbol]}"
30
+ puts "(#{right[:args].join(',')})"
31
+ when :return
32
+ if left
33
+ print "#{left[:real_klass]}.#{left[:method_symbol]}"
34
+ end
35
+ print " <- "
36
+ print "#{right[:real_klass]}.#{right[:method_symbol]}"
37
+ puts
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ tracer = HighlevelTest.new
44
+ tracer.include ts, :run, :changed, :notify_observers
45
+ tracer.include ta, :update
46
+
47
+ ts.run(25)
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+
3
+ # generate sequence-diagram with old version of a library
4
+ ruby -rtemperature_old sequence_diagram_generator.rb >sequence_diagram_example_old.pic
5
+ pic2plot -Tps sequence_diagram_example_old.pic >sequence_diagram_example_old.ps
6
+
7
+ # generate sequence-diagram with new version of a library
8
+ ruby -rtemperature_new sequence_diagram_generator.rb >sequence_diagram_example_new.pic
9
+ pic2plot -Tps sequence_diagram_example_new.pic >sequence_diagram_example_new.ps
10
+
11
+ # generate combined sequence_diagram out the two
12
+ ../bin/sequence_diagram_diff sequence_diagram_example_old.pic sequence_diagram_example_new.pic >sequence_diagram_example.pic
13
+ pic2plot -Tps sequence_diagram_example.pic >sequence_diagram_example.ps
@@ -0,0 +1,41 @@
1
+ $:.push '../lib'
2
+
3
+ require 'uml/sequence_diagram'
4
+
5
+ def callback(p)
6
+ case p
7
+ when Fixnum, TrueClass, FalseClass
8
+ p.to_s
9
+ when NilClass
10
+ 'nil'
11
+ when Array
12
+ '[' << p.collect(&method(:callback)).join(',') << ']'
13
+ else
14
+ p.class
15
+ end
16
+ end
17
+
18
+ sd = UML::SequenceDiagram.new(
19
+ :split_inherited => true,
20
+ :multiple_activation => true,
21
+ :use_create_message => true,
22
+ :object_sequence => ['Actor', 'TempSensor', 'Observable', 'Dummy', 'TempAlarm'],
23
+ :pic_variables => {
24
+ :boxwid => 0.85,
25
+ :movewid => 0.85,
26
+ :maxpswid => 50,
27
+ :maxpsht => 50,
28
+ },
29
+ :parameter_callback => method(:callback)
30
+ )
31
+
32
+ ts = TempSensor.new
33
+
34
+ sd.include ts, :run, :changed, :notify_observers, :add_observer
35
+ sd.include TempAlarm, :update, :initialize
36
+ sd.include IO, :puts
37
+
38
+ ta = TempAlarm.new ts
39
+ ts.run 27
40
+
41
+ puts sd.to_pic