I want to split up a large class by using mixins.
I am using this mixin code from the Little Book
@include: (obj) ->
for key, value of obj when key not in moduleKeywords
# Assign properties to the prototype
@::[key] = value
obj.included?.apply(@)
this
class FooMixin
b: => @something = 2
class Foo extends Module
@include FooMixin
a: => @something = 1
Problem is that @ in FooMixin is FooMixin. I want it to be Foo instead.
I have tried adding the line _.bind(@::[key], @) at the end of @include() but it doesn't help. Any suggestions?
Okay, few things I was doing wrong.
1.
@include from the Little Book takes an object not a class. To get it to work with classes you need to write @include FooMixin::. However, I have since begun using objects instead.
2.
When using an object instead of a class, the fat arrow adds a line inside the CoffeeScript wrapper right at the top which reads _this = this. All methods are bound to the global context which is not what we want. To fix we must convert fat arrows to thin arrows, and bind each function to our Foo instance. Using Underscore I added this to the constructor of Foo:
constructor: ->
for fname in _.functions FooMixin
@[fname] = _.bind @[fname], @
super
I tried _.bindAll @, _.functions FooMixin but it gave me an error saying something like At Function.bind, could not run bind of undefined. Weird error, seeing as the code above is pretty much identical to the _.bindAll method.
So now I can split my classes up for better readability and code sharing.
UPDATE: The problem with _.bindAll is that it takes a splat not an array. Fix is to use _.bindAll @, _.functions(FooMixin)....
UPDATE: Found a better solution.
Same as original post. Use classes for mixins.
Use @include FooMixin:: or change @include to operate on a prototype instead of properties.
In the Foo constructor write FooMixin.call @ which binds the methods correctly.
This works well and is nice and clean.
The only potential issue is that mixins will be overridden by existing properties. The only way to get around this that I can see is to do something like:
after = ->
_.extend Foo, FooMixin::
class Foo
# define...
after()
Or pass the extend method to _.defer but this is so hacky and probably won't work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With