In order to prevent confusion with officialy supported mixin mechanisms that may be added to Groovy in the future, I have used the term 'gynamo' instead of 'mixin'. It is in essence trying to provide the same functionality.
Gynamo is a lightweight mechanism for extending classes with discrete packages of functionality similar to mixins in languages like Ruby.
To make things easier (hopefully) to understand, the following is a definition of terms used:
Hopefully that illustrates the point.
You can also grab the buildable source from the SVN repository.
Gynamo is released under the same license terms as Groovy.
All classes in the Gynamo library are in the package "gynamo".
Gynamos can contain fields (which will be public when injected) and methods (which Gynamos define as closures). All methods and properties are attached via ExpandoMetaClass so all the usual rules apply there.
Methods and properties are attached via the '=' operator so any existing methods or properties with a matching name will be overridden. This is something to watch out for.
A Gynamo is any class that extends the gynamo.Gynamo class. Gynamo inheritance is not supported, all Gynamos must directly extend the Gynamo class.
Methods (as closures) or fields to be injected must be accessible via getters. Groovy makes this too easy by generating getters for any fields that you don't specify the visiblity of.
You can even add fields and methods statically to a class ...
Gynamo class defines the static method
gynamize which takes a Gynamee class and a Gynamo class and performs the injection.
Or alternatively using categories ...
If your Gynamo needs to do anything before or after Gynamization of a class there are optional hooks for it
Sometimes you want to add bean like properties to a Gynamee. This is a little awkward but you can make it work using the provided
GynamoPropertyStorage class. Another caveat is that you have to define the getter and/or setter for each such property. Here is an example ...
You would use the above like so ...
If you have a gynamo that depends on another Gynamee being injected then you can specify that with annotations ...
@GynamoDependency annotation takes a single parameter of type
Class which must be another Gynamo. The specified Gynamo will be injected automatically *before* this Gynamo gets injected. Therefore, the methods and fields of the dependency are available on the Gynamee at time of Gynamization.
If your Gynamo depends on multiple Gynamos, you can use the
@GynamoDependencies annotation to specify a list of Gynamos that need to be injected before.
*Note:* Be careful of circular dependencies. The dependency handling is pretty simple so if you do have a a circular dependency, your code will blow up with a StackOverflowError. If anyone has any suggestions on how this can be improved please let me know.