A classe Mixin:
class Mixin
@attachTo: (klass) ->
klass::[k] = v for k,v of this.prototype when k isnt 'constructor'
@included? klass
Editar : a versão original não testou a chave de propriedade, o que resultou no construtor original não sendo mais acessível.
Um mixin simples:
class Suspendable extends Mixin
start: ->
# lazy context binding of handleFrameRequest
unless @handleFrameRequest.bound
f = @handleFrameRequest
@handleFrameRequest = => f.apply this, argument
@handleFrameRequest.bound = true
# animation start
unless @animated
@lastTime = new Date()
requestAnimationFrame @handleFrameRequest
@animated = true
stop: -> @animated = false if @animated
handleFrameRequest: ->
if @animated
currentTime = new Date()
delta = currentTime - @lastTime
@animate delta
requestAnimationFrame @handleFrameRequest
@lastTime = currentTime
animate: -> # override to create your animations
Mixins parametrizados:
Equatable = (properties...) ->
class extends Mixin
equals: (o) -> o? and properties.every (p) =>
if @[p].equals? then @[p].equals o[p] else o[p] is @[p]
Formattable = (classname, properties...) ->
class extends Mixin
toString: ->
if properties.length is 0
"[#{classname}]"
else
formattedProperties = ("#{p}=#{@[p]}" for p in properties)
"[#{classname}(#{formattedProperties.join ', '})]"
classname: -> classname
Decorar uma aula:
class Dummy
Suspendable.attachTo Dummy
Equatable('position', 'speed').attachTo Dummy
Formattable('Dummy','position', 'speed').attachTo Dummy
constructor: (@position, @speed) ->
animate: (delta) ->
@position = @position.add(@speed.scale(delta / 1000))
# ...
Ótima maneira de embrulhar muitas inclusões de mixin:
class Rectangle
PROPERTIES = ['x','y','width','height','rotation']
[
Equatable.apply(null, PROPERTIES)
Formattable.apply(null, ['Rectangle'].concat PROPERTIES)
Sourcable.apply(null, ['geomjs.Rectangle'].concat PROPERTIES)
Parameterizable('rectangleFrom', {
x: NaN
y: NaN
width: NaN
height: NaN
rotation: NaN
})
Cloneable, Geometry, Surface
Path, Triangulable, Intersections
].forEach (mixin) -> mixin.attachTo Rectangle
#....
Edit : Para responder à sugestão de @sheerun , alguma outra maneira de embrulhar inclusões de mixins:
Por meio de uma classe de módulo base:
class Module
@include: (mixins...) ->
if Object::toString.call(mixins[0]).indexOf('Array') >= 0
mixins = mixins[0]
mixins.forEach (mixin) -> mixin.attachTo this
class Dummy extends Module
@include Suspendable,
Equatable('position', 'speed'),
Formattable('Dummy','position', 'speed')
Ou se você não puder ou não quiser usar herança:
include = (mixins...) ->
if Object::toString.call(mixins[0]).indexOf('Array') >= 0
mixins = mixins[0]
in: (klass) -> mixins.forEach (mixin) -> mixin.attachTo klass
class Dummy extends Module
include(
Suspendable,
Equatable('position', 'speed'),
Formattable('Dummy','position', 'speed')
).in Dummy