Ruby to UML mapping
I'm not a Ruby expert, but what I understand from your snippet given its syntax is:
- There's a Ruby class
Synchronization
: That's one UML class
- The Ruby class has 4 methods
initialize
, perform
, detect_outdated_documents
, and update_documents
, the two last being private. These would be 4 UML operations.
initialize
is the constructor, and since it's empty, you have not mentioned it in your UML class diagram, and that's ok.
- The Ruby class has 1 instance variable
@documents
. In UML, that would be a property, or a role of an association end.
- The Ruby class has a getter created with
attr_reader
. But since it is in a private section, its visibility should be -
. This other answer explains how to work with getters and setters elegantly and accurately in UML (big thanks to @engineersmnky for the explanations on getters in Ruby, and for having corrected my initial misunderstanding in this regard)
- I understand that
SomeClass.new
creates in Ruby a new object of class SomeClass
.
Ruby and dynamic typing in UML
UML class diagrams are based on well-defined types/classes. You would normally indicate associations, aggregations and compositions only with known classes with whom there’s for sure a stable relation. Ruby is dynamically typed, and all what is known for sure about an instance variable is that it's of type Object
, the highest generalization possible in Ruby.
Moreover, Ruby methods return the value of the latest statement/expression in its execution path. If you did not care about a return value of an object, you'd just mark it as being Object
(Thanks engineersmnky for the explanation).
Additional remarks:
- There is no
void
type in UML (see also this SO question). An UML operation that does not return anything, would just be an operation with no return type indicated.
- Keep also in mind that the use of types that do not belong to the UML standard (such as
Array
, Hash
, Object
, ...) would suppose the use of a language specific UML profile.
Based on all this, and considering that an array is also an Object
, your code would lead to a very simple UML diagram, with 3 classes, that are all specializations of Object
, and a one-to-many association between Synchronization
and Object
, with the role @documents
at the Object
end.
Is it all what we can hope for?
The very general class diagram, may perhaps match very well the implementation. But it might not accurately represent the design.
It's your right to model in UML a design independently of the implementation. Hence, if the types of instance variables are known by design (e.g. you want it to be of some type and make sure via the initialization and the API design that the type will be enforced), you may well show this in your diagram even if it deviates from the code:
- You have done some manual type inferencing to deduce the return type of the UML operations. Since all Ruby methods return something, we'd expect for all Ruby methods at least an
Object
return type. But it would be ok for you not to indicate any return type (the UML equivalent to void
) to express taht the return value is not important.
- You also have done some type inference for the instance variable (UML property): you clarify that the only value it can take is the value return by
DetectOutdatedDocument.new.perform
.
- Your diagram indicates that the class is related to an unspecified number of
DetectOutdatedDocument
objects, and we guess it's becaus of the possible values of @documents
. And the property is indicated as an array of objects. It's very misleading to have both on the diagram. So I recommend to remove the document
property. Instead, prefer a document
role at the association end on the side of DetectOutdatedDocument
. This would greatly clarify for the non-Ruby-native readers why there is a second class on the diagram. :-) (It took me a while)
- Now you should not use the black diamond for composition. Because
documents
has a public reader; so other objects could also be assigned to the same documents. Since Ruby seems to have reference semantic for objects, the copy would then refer to the same objects. That's shared aggregation (white diamond) at best. And since UML has not defined very well the aggregation semantic, you could even show a simple association.
A last remark: from the code you show, we cannot confirm that there is an aggregation between UpdateOutdatedDocument
and DetectOutdatedDocument
. If you are sure there is such a relationship, you may keep it. But if it's only based on the snippet you showed us, remove the aggregation relation. You could at best show a usage dependency. But normally in UML you would not show such a dependency if it is about the body of a method, since the operation could be implemented very differently without being obliged to have this dependency.