/// Saturday, October 29
Boo is mixin it up
What is a mixin? A mixin is an interface with implemented methods. When a class includes a mixin, the class implements the interface and includes, not inherits, all the mixin's attributes and methods. They become part of the class during compilation. Interestingly enough mixins don't need to implement an interface. The advantage of implementing an interface is obvious so that the class may be passed to methods requiring that interface.
Ayende's idea about using code injection to implement static mixins motivated me. How would you define a mixin? You could try this, but Boo wouldn't like it. It's not an interface:
interface IMath: def Add(a as int, b as int): return a + b
Here is what I've come up with. To create a mixin define the interface as usual, then define mixin attributes and methods in a separate file and inject them during compilation.
# IMath.boo - interface to our math mixin interface IMath: def Add(a as int, b as int) as int: pass # IMath.mxn - implementation of the interface with a bonus [property(Result)] _result as int def Add(a as int, b as int) as int: _result = a + b return _result
The first file is self explanatory. The second is interesting in that it implements the IMath.Add() method and a Result accessor. To mix this in with a class use the InjectCode attribute using MixinCodeBuilder.
# english.boo import MGutz.CodeInjector from "MGutz.CodeInjector" [InjectCode(MixinCodeBuilder, "IMath.mxn")] class English(IMath): def construtor(): pass def Noun() as string: return "Boo"
When booc compiles english.boo, MixinCodeBuilder comes alive and parses the code in "IMath.mxn" and adds Add() and Result to English. Here is the test:
mixin = English() as IMath print mixin.Add(2, 3) o = English() o.Add(6,6) print o.Result
As you can see, English implements IMath as well as has a Result accessor. I like this approach as you have the option to implement the mixin methods by simply not injecting the mixin code. The downside is declaring the interface and injecting the code are two steps.
This wasn't that difficult to implement. To be honest the CodeInjector is probably less than 400 lines of code and that may be an exaggerated number. Boo just does so much. OK, I haven't really tested other scenarios but so far, so good!