-
-
Save dsci/2036590 to your computer and use it in GitHub Desktop.
Mixins/Modules behavior in coffeescript. Thought of to be used with Backbone.js applications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# define module behavior | |
class Module | |
keywords = ['extended', 'included', 'initialize'] | |
@extend = (obj) -> | |
for key, value of obj when key not in keywords | |
@[key] = value | |
obj.extended?.apply(@) | |
this | |
@include = (obj) -> | |
for key, value of obj when key not in keywords | |
@::[key] = value | |
if obj.initialize? | |
@::['_initialize_callbacks'] or= [] | |
@::['_initialize_callbacks'].push obj.initialize | |
obj.included?.apply(@) | |
this | |
initialize: -> | |
if callbacks = @['_initialize_callbacks'] | |
cb.call(@) for cb in callbacks | |
# define Base classes for the application | |
Base = | |
View: class BaseView extends Backbone.View | |
leave: -> @remove() | |
Model: class BaseModel extends Backbone.Model | |
Collection: class BaseCollection extends Backbone.Collection | |
Router: class BaseRouter extends Backbone.Router | |
# let Base classes be modules | |
for name, klass of Base | |
_(klass).extend Module | |
_(klass.prototype).extend Module.prototype | |
# define module 1 | |
Observer = | |
included: -> console.log "Observer included in #{@name}" | |
initialize: -> console.log "initialize called from Observer module" | |
bindTo: (source, event, cb)-> | |
source.bind(event, cb, @) | |
@bindings or= [] | |
@bindings.push | |
source: source | |
event: event | |
callback: cb | |
unbindAll: -> | |
binding.source.unbind(binding.event, binding.callback) for binding in @bindings | |
@bindings = [] | |
# define module 2 | |
CompositeView = | |
included: -> console.log "CompositeView included in #{@name}" | |
initialize: -> | |
@children = [] | |
console.log 'initialize called from CompositeView module' | |
addChild: (view)-> | |
@children.push view | |
view.parent = this | |
renderChild: (view)-> | |
@addChild(view) unless @hasChild(view) | |
view.render() | |
appendChild: (view)-> | |
@renderChild view | |
($ @el).append view.el | |
renderChildInto: (view, container)-> | |
@renderChild view | |
($ container).empty().append view.el | |
hasChild: (view)-> | |
if (index = @children.indexOf view) != -1 | |
index | |
else | |
false | |
_leaveChildren: -> view.leave?() for view in @children | |
_removeFromParent: -> @parent?._removeChild @ | |
_removeChild: (view)-> @children.splice(index, 1) if index = @hasChild(view) | |
Base.View.include Observer # include module 1 | |
class MyView extends Base.View | |
@include CompositeView # include module 2 | |
initialize: -> | |
super() # important ! | |
console.log 'initialize called from view definition' | |
leave: -> | |
@unbindAll() | |
@remove() | |
@_leaveChildren() | |
@_removeFromParent() | |
myView1 = new MyView | |
myView2 = new MyView | |
myView1.addChild myView2 | |
console.log 'myView1.children', myView1.children #=> contains myView2 | |
console.log 'myView2.children', myView2.children #=> empty | |
console.log 'MyView instance', myView1 #=> module 1 & 2 are in the prototype chain |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment