ruby-uml 0.2.2

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.
@@ -0,0 +1,56 @@
1
+ require 'singleton'
2
+ require 'observer'
3
+
4
+ module UML
5
+
6
+ class LowlevelBacktracer
7
+ include Singleton
8
+ include Observable
9
+
10
+ attr_reader :call_stack
11
+
12
+ def initialize
13
+ @call_stack = []
14
+ set_trace_func method(:tracer).to_proc
15
+ end
16
+
17
+ private
18
+
19
+ def tracer(event, filename, linenumber, method_symbol, binding, klass)
20
+ begin
21
+
22
+ case event
23
+ when 'call', 'c-call'
24
+ # args describe the endpoint of call
25
+
26
+ actual_information = {
27
+ :filename => filename,
28
+ :linenumber => linenumber,
29
+ :method_symbol => method_symbol,
30
+ :binding => binding,
31
+ :klass => klass,
32
+ }
33
+ @call_stack.push actual_information
34
+
35
+ changed
36
+ notify_observers :call, self
37
+
38
+ when 'return', 'c-return'
39
+ # args describe the startingpoint of return,
40
+ # should be equal to last stack-element
41
+
42
+ changed
43
+ notify_observers :return, self
44
+
45
+ @call_stack.pop
46
+
47
+ end
48
+ rescue Exception => exception
49
+ puts exception
50
+ puts exception.backtrace
51
+ end
52
+
53
+ end
54
+
55
+ end # class Backtracer
56
+ end # module UML
@@ -0,0 +1,138 @@
1
+ module UML
2
+
3
+ # Contains helper methods to work with receivers and methods.
4
+ #
5
+ # These methods would better match in class Method, if it had access to its receiver.
6
+ # (Waiting for Ruby v1.9)
7
+ module MethodHelper
8
+
9
+ # :section: Receiver
10
+
11
+ # Find out, if +receiver+ is an instance of some class.
12
+ #
13
+ # :call-seq:
14
+ # is_receiver_instance_of_class?(receiver) -> true or false
15
+ #
16
+ def is_receiver_instance_of_class?(receiver)
17
+ not(is_receiver_class?(receiver) or is_receiver_module?(receiver))
18
+ end
19
+
20
+ # Find out, if +receiver is a Class
21
+ #
22
+ # :call-seq:
23
+ # is_receiver_class?(receiver) -> true or false
24
+ #
25
+ def is_receiver_class?(receiver)
26
+ receiver.instance_of? Class
27
+ end
28
+
29
+ # Find out, if +receiver is a Module
30
+ #
31
+ # :call-seq:
32
+ # is_receiver_module?(receiver) -> true | false
33
+ #
34
+ def is_receiver_module?(receiver)
35
+ receiver.instance_of? Module
36
+ end
37
+
38
+ # Find real receiver of method.
39
+ #
40
+ # Searches included and extended Modules and Superclasses.
41
+ #
42
+ # :call-seq:
43
+ # get_real_receiver_of_method(top_receiver, method_symbol) -> class | module | nil
44
+ #
45
+ def get_real_receiver_of_method(*args)
46
+ klass, message = prepare_args(*args)
47
+
48
+ # As Module::instance_methods just returns public instance methods, all three
49
+ # visibilities have to be tested
50
+
51
+ # test all included modules in topmost class
52
+ klass.included_modules.each do |mod|
53
+ return mod if mod.singleton_methods(false).include? message or
54
+ mod.public_instance_methods(false).include? message or
55
+ mod.protected_instance_methods(false).include? message or
56
+ mod.private_instance_methods(false).include? message
57
+ end
58
+
59
+ # also check extended modules
60
+ class << klass; included_modules; end.each do |mod|
61
+ return mod if mod.singleton_methods(false).include? message or
62
+ mod.public_instance_methods(false).include? message or
63
+ mod.protected_instance_methods(false).include? message or
64
+ mod.private_instance_methods(false).include? message
65
+ end
66
+
67
+ # then check class and superclasses
68
+ while klass
69
+ return klass if klass.singleton_methods(false).include? message or
70
+ klass.public_instance_methods(false).include? message or
71
+ klass.protected_instance_methods(false).include? message or
72
+ klass.private_instance_methods(false).include? message
73
+
74
+ klass = klass.respond_to?(:superclass) ? klass.superclass : nil
75
+ end
76
+ nil
77
+ end
78
+
79
+ # :section: Method
80
+
81
+ # Find out if method is singleton
82
+ #
83
+ # :call-seq:
84
+ # is_method_singleton?(top_receiver, method_symbol) -> true | false
85
+ #
86
+ def is_method_singleton?(*args)
87
+ klass, message = prepare_args(*args)
88
+ klass.singleton_methods.include? message
89
+ end
90
+
91
+ # Get visibility of an instance method
92
+ #
93
+ # :call-seq:
94
+ # get_visibility_of_instance_method(top_receiver, method_symbol) -> :public | :protected | :private
95
+ #
96
+ def get_visibility_of_instance_method(*args)
97
+ klass, message = prepare_args(*args)
98
+ case
99
+ when klass.public_instance_methods.include?(message) : :public
100
+ when klass.protected_instance_methods.include?(message) : :protected
101
+ when klass.private_instance_methods.include?(message) : :private
102
+ else nil
103
+ end
104
+ end
105
+
106
+ # Get visibility of a singleton method
107
+ #
108
+ # :call-seq:
109
+ # get_visibility_of_singleton_method(top_receiver, method_symbol) -> :public | :protected | :private
110
+ #
111
+ def get_visibility_of_singleton_method(*args)
112
+ klass, message = prepare_args(*args)
113
+ case
114
+ when klass.public_methods.include?(message) : :public
115
+ when klass.protected_methods.include?(message) : :protected
116
+ when klass.private_methods.include?(message) : :private
117
+ else nil
118
+ end
119
+ end
120
+
121
+ # Get visibility of a method.
122
+ #
123
+ # :call-seq:
124
+ # get_visibility_of_method(top_receiver, method_symbol) -> :public | :protected | :private
125
+ #
126
+ def get_visibility_of_method(*args)
127
+ is_method_singleton?(*args)? get_visibility_of_singleton_method(*args) : get_visibility_of_instance_method(*args)
128
+ end
129
+
130
+ private
131
+
132
+ def prepare_args(klass, method_symbol)
133
+ return [is_receiver_instance_of_class?(klass) ? klass.class : klass, method_symbol.to_s]
134
+ end
135
+
136
+
137
+ end
138
+ end
@@ -0,0 +1,157 @@
1
+ require 'uml/highlevel_backtracer'
2
+ require 'uml/sequence_diagram_helper'
3
+
4
+ module UML
5
+
6
+ # Generates pic representation of program execution.
7
+ class SequenceDiagram
8
+ include SequenceDiagramHelper
9
+
10
+ # Options:
11
+ # basename:: You can give the actor a name. Default is 'Actor'.
12
+ # multiple_activation:: If +true+, and an activated object calls method with receiver +self+,
13
+ # object can be activated multiple times. Default is +false+.
14
+ # object_sequence:: You can sort the sequence of objects in output, if you give their names here.
15
+ #
16
+ # Every object not included, will be printed after given ones in random order.
17
+ #
18
+ # Defaults to <tt>['Actor']</tt>, which ensures that 'Actor' is always the leftmost object.
19
+ # pic_variables:: Hash with variables for sequence.pic. See UMLGraph's documentation
20
+ # for valid variables. Default +{}+.
21
+ # sequence_pic:: give the position and name of UMLGraph's sequence.pic file.
22
+ #
23
+ # Default is 'sequence.pic', which means that the file has to
24
+ # be in the directory where pic2plot is called.
25
+ # split_inherited:: If +true+ included modules and superclasses are printed as discrete objects.
26
+ #
27
+ # Default is +false+.
28
+ # use_create_message:: If method initialize is caught, a create_message is built
29
+ # instead of a normal call. Have in mind, that initialize is
30
+ # not allways used as constructor.
31
+ #
32
+ # Default is +false+.
33
+ # parameter_callback:: To get a pretty print of method args and returnvalues,
34
+ # you can give a proc here, that gets called for each argument
35
+ # or returnvalue.
36
+ #
37
+ # Default is <tt>lambda { |p| p.to_s }</tt>, but have a look
38
+ # at method callback in examples/sequence_diagram_generator.rb for a
39
+ # more sophisticated and recursive example.
40
+ def initialize(config = nil)
41
+ @config = {
42
+ :basename => 'Actor',
43
+ :split_inherited => false,
44
+ :use_create_message => false,
45
+ :multiple_activation => false,
46
+ :sequence_pic => 'sequence.pic',
47
+ :pic_variables => {},
48
+ :object_sequence => ['Actor'],
49
+ :parameter_callback => lambda { |p| p.to_s }
50
+ }
51
+ @config.update(config) if config
52
+
53
+ @object_definitions = {}
54
+ @object_definitions[@config[:basename]] = ObjectDefinition.new(@config[:basename], :actor)
55
+ @messages = ''
56
+
57
+ @tracer = HighlevelBacktracer.new
58
+ @tracer.add_observer self
59
+ end
60
+
61
+ # Include methods of class or instance.
62
+ #
63
+ # The parameters are handed on to <tt>AspectR::wrap</tt>, so refer to the according
64
+ # documentation for how you can e.g. use Regexp.
65
+ #
66
+ # :call-seq: include(klass, *methods)
67
+ #
68
+ def include(*args)
69
+ @tracer.include(*args)
70
+ end
71
+
72
+ def to_pic
73
+ result = ''
74
+ result << ".PS\n"
75
+ result << "copy \"#{@config[:sequence_pic]}\";\n"
76
+ result << pic_variable_definitions(@config[:pic_variables])
77
+ result << pic_object_definitions(object_definitions)
78
+ result << pic_message_exchange(@messages)
79
+ result << pic_object_completions(@object_definitions.values)
80
+ result << '.PE'
81
+ result
82
+ end
83
+
84
+ def update(event, tracer) # :nodoc: #
85
+ begin
86
+ right = tracer.call_stack[-1]
87
+ left = tracer.call_stack[-2]
88
+
89
+ used_object = @config[:split_inherited] ? :real_object : :object
90
+ right_label = right[used_object].name
91
+ left_label = (left) ? left[used_object].name : @config[:basename]
92
+
93
+ case event
94
+ when :call
95
+
96
+ if @config[:use_create_message] and right[:method_symbol] == :initialize
97
+ @object_definitions[right_label] ||= ObjectDefinition.new(right_label,
98
+ :placeholder_object
99
+ )
100
+ @messages << pic_create_message(@object_definitions[left_label],
101
+ @object_definitions[right_label]
102
+ )
103
+ else
104
+ @object_definitions[right_label] ||= ObjectDefinition.new(right_label)
105
+ @messages << pic_message(@object_definitions[left_label],
106
+ @object_definitions[right_label],
107
+ right[:method_symbol],
108
+ right[:args].collect(&@config[:parameter_callback]).join(',')
109
+ )
110
+ end
111
+
112
+ when :return
113
+ @messages << pic_return_message(@object_definitions[left_label],
114
+ @object_definitions[right_label],
115
+ @config[:parameter_callback].call(right[:returns])
116
+ )
117
+ end
118
+ rescue Exception => e
119
+ puts e
120
+ puts e.backtrace
121
+ exit 1
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def object_definitions
128
+ result = []
129
+ labels = @config[:object_sequence] + (@object_definitions.keys - @config[:object_sequence])
130
+ labels.each do |label|
131
+ result << @object_definitions[label] if @object_definitions[label]
132
+ end
133
+ result
134
+ end
135
+
136
+ end
137
+ end
138
+
139
+ class UML::SequenceDiagram::ObjectDefinition # :nodoc:
140
+
141
+ attr_reader :type, :label
142
+ attr_accessor :active
143
+
144
+ def initialize(label, type = :object)
145
+ @label = label
146
+ @type = type
147
+ @active = 0
148
+ end
149
+
150
+ # does nothing more, than ensure that label can be used as name for pic
151
+ def name
152
+ @label.gsub(/::/, '_')
153
+ end
154
+
155
+ alias to_s name
156
+
157
+ end
@@ -0,0 +1,100 @@
1
+ module UML
2
+ module SequenceDiagramHelper
3
+
4
+ def pic_object_definitions(definitions)
5
+ result = "\n# Object definition\n"
6
+ definitions.each do |definition|
7
+ result << pic_object_definition(definition)
8
+ end
9
+ result << pic_step
10
+ end
11
+
12
+ def pic_message_exchange(string)
13
+ result = "\n# Message exchange\n"
14
+ result << string
15
+ result << pic_step
16
+ end
17
+
18
+ def pic_object_completions(definitions)
19
+ result = "\n# Object lifeline completion\n"
20
+ definitions.each do |definition|
21
+ result << pic_complete(definition)
22
+ end
23
+ result
24
+ end
25
+
26
+ def pic_object_definition(definition)
27
+ case definition.type
28
+ when :placeholder_object
29
+ pic_placeholder_object(definition)
30
+ when :actor
31
+ pic_actor(definition)
32
+ else
33
+ pic_object(definition)
34
+ end
35
+ end
36
+
37
+ def pic_actor(definition)
38
+ "actor(#{definition},\"#{definition.label}\");\n"
39
+ end
40
+
41
+ def pic_complete(definition)
42
+ "complete(#{definition});\n"
43
+ end
44
+
45
+ def pic_object(definition)
46
+ "object(#{definition},\"#{definition.label}\");\n"
47
+ end
48
+
49
+ def pic_placeholder_object(definition)
50
+ "placeholder_object(#{definition});\n"
51
+ end
52
+
53
+ def pic_step
54
+ "step();\n"
55
+ end
56
+
57
+ def pic_variable(variable, value)
58
+ "#{variable} = #{value};\n"
59
+ end
60
+
61
+ # hash format :variable, value
62
+ def pic_variable_definitions(hash)
63
+ result = "\n# Variable definitions\n"
64
+ hash.each do |variable, value|
65
+ result << pic_variable(variable, value)
66
+ end
67
+ result
68
+ end
69
+
70
+ def pic_active(definition)
71
+ definition.active += 1
72
+ (@config[:multiple_activation] or definition.active == 1) ? "active(#{definition});\n" : ''
73
+ end
74
+
75
+ def pic_inactive(definition)
76
+ definition.active -= 1
77
+ (@config[:multiple_activation] or definition.active == 0) ? "inactive(#{definition});\n" : ''
78
+ end
79
+
80
+ def pic_message(from, to, message, value)
81
+ result = "message(#{from},#{to},\"#{message}(#{value})\");\n"
82
+ result << pic_active(to)
83
+ end
84
+
85
+ def pic_return_message(left, right, value)
86
+ result = "return_message(#{right},#{left},\"#{value}\");\n"
87
+ result << pic_inactive(right)
88
+ end
89
+
90
+ def pic_create_message(from, to)
91
+ result = "create_message(#{from},#{to},\"#{to.label}\");\n"
92
+ result << pic_active(to)
93
+ end
94
+
95
+ # def escape(string)
96
+ # string.gsub(/(["])/, '\\\\\1')
97
+ # end
98
+
99
+ end
100
+ end