src/define.coffee in luca-0.9.4 vs src/define.coffee in luca-0.9.6

- old
+ new

@@ -9,21 +9,19 @@ # _.def, or Luca.define returns a chainable object which allows you to define # your components with a readable syntax. For example: # _.def("Luca.View").extends("Backbone.View").with the_good:"shit" # _.def("MyView").extends("Luca.View").with the_custom:"shit" +_.mixin + def: Luca.component = Luca.define = Luca.register = (componentName)-> new DefineProxy(componentName) -Luca.define = (componentName)-> - new DefineProxy(componentName) - -Luca.component = Luca.define - # The define proxy chain sets up a call to Luca.extend, which is a wrapper around Luca and Backbone component class' extend function. class DefineProxy constructor:(componentName)-> @namespace = Luca.util.namespace() @componentId = @componentName = componentName + @superClassName = 'Luca.View' if componentName.match(/\./) @namespaced = true parts = componentName.split('.') @componentId = parts.pop() @@ -38,28 +36,45 @@ # allow for multiple ways of saying the same thing for readability purposes from: (@superClassName)-> @ extends: (@superClassName)-> @ extend: (@superClassName)-> @ - # an alias for with, or a readability helper in multi-line definitions - enhance: (properties)-> - return @with(properties) if properties? + triggers: (hooks...)-> + _.defaults(@properties ||= {}, hooks: []) + for hook in hooks + @properties.hooks.push(hook) + @properties.hooks = _.uniq(@properties.hooks) @ - # which properties, methods, etc will you be extending the base class with? - with: (properties)-> + includes: (includes...)-> + _.defaults(@properties ||= {}, include: []) + for include in includes + @properties.include.push(include) + @properties.include = _.uniq(@properties.include) + @ + + mixesIn: (mixins...)-> + _.defaults(@properties ||= {}, mixins: []) + for mixin in mixins + @properties.mixins.push(mixin) + @properties.mixins = _.uniq(@properties.mixins) + @ + + defaultProperties: (properties={})-> + _.defaults((@properties||={}), properties) + at = if @namespaced Luca.util.resolve(@namespace, (window || global)) else (window||global) # automatically create the namespace if @namespaced and not at? eval("(window||global).#{ @namespace } = {}") at = Luca.util.resolve(@namespace,(window || global)) - at[@componentId] = Luca.extend(@superClassName,@componentName, properties) + at[@componentId] = Luca.extend(@superClassName,@componentName, @properties) if Luca.autoRegister is true componentType = "view" if Luca.isViewPrototype( at[@componentId] ) if Luca.isCollectionPrototype( at[@componentId] ) @@ -68,43 +83,100 @@ componentType = "collection" componentType = "model" if Luca.isModelPrototype( at[@componentId] ) # automatically register this with the component registry - Luca.register( _.string.underscored(@componentId), @componentName, componentType) + Luca.registerComponent( _.string.underscored(@componentId), @componentName, componentType) at[@componentId] +# Aliases for the mixin definition +DefineProxy::behavesAs = DefineProxy::uses = DefineProxy::mixesIn + +# Aliases for the final call on the define proxy +DefineProxy::defines = DefineProxy::defaults = DefineProxy::exports = DefineProxy::defaultProperties +DefineProxy::defaultsTo = DefineProxy::enhance = DefineProxy::with = DefineProxy::defaultProperties + # The last method of the DefineProxy chain is always going to result in # a call to Luca.extend. Luca.extend wraps the call to Luca.View.extend, # or Backbone.Collection.extend, and accepts the names of the extending, # and extended classes as strings. This allows us to maintain information # and references to the classes and their prototypes, mainly for the purposes # of introspection and development tools Luca.extend = (superClassName, childName, properties={})-> superClass = Luca.util.resolve( superClassName, (window || global) ) + superClass.__initializers ||= [] + unless _.isFunction(superClass?.extend) - throw "#{ superClassName } is not a valid component to extend from" + throw "Error defining #{ childName }. #{ superClassName } is not a valid component to extend from" properties.displayName = childName properties._superClass = ()-> superClass.displayName ||= superClassName superClass - properties._super = (method, context, args)-> + properties._super = (method, context=@, args=[])-> + # protect against a stack too deep error in weird cases + # TODO: debug this better + @_superClass().prototype[method]?.apply(context, args) definition = superClass.extend(properties) # _.def("MyView").extends("View").with # include: ['Luca.Events'] if _.isArray( properties?.include ) - for mixin in properties.include - mixin = Luca.util.resolve(mixin) if _.isString(mixin) - _.extend(definition::, mixin) + for include in properties.include + include = Luca.util.resolve(include) if _.isString(include) + _.extend(definition::, include) definition -_.mixin - def: Luca.define + +Luca.mixin = (mixinName)-> + namespace = _( Luca.mixin.namespaces ).detect (space)-> + Luca.util.resolve(space)?[ mixinName ]? + + namespace ||= "Luca.modules" + + resolved = Luca.util.resolve(namespace)[ mixinName ] + + console.log "Could not find #{ mixinName } in ", Luca.mixin.namespaces unless resolved? + + resolved + +Luca.mixin.namespaces = [ + "Luca.modules" +] + +Luca.mixin.namespace = (namespace)-> + Luca.mixin.namespaces.push(namespace) + Luca.mixin.namespaces = _( Luca.mixin.namespaces ).uniq() + +# Luca.decorate('Luca.View').with('Luca.modules.MyCustomMixin') +Luca.decorate = (componentPrototype)-> + componentPrototype = Luca.util.resolve(componentPrototype).prototype if _.isString(componentPrototype) + + return with: (mixin)-> + mixinDefinition = Luca.mixin(mixin) + + mixinPrivates = _( mixinDefinition ).chain().keys().select (key)-> + "#{ key }".match(/^__/) + + sanitized = _( mixinDefinition ).omit( mixinPrivates.value() ) + + _.extend(componentPrototype, sanitized) + + # When a mixin is included, we may want to do things + mixinDefinition?.__included?.call(mixinDefinition, mixin) + + superclassMixins = componentPrototype._superClass()::mixins + + componentPrototype.mixins ||= [] + componentPrototype.mixins.push( mixin ) + componentPrototype.mixins = componentPrototype.mixins.concat( superclassMixins ) + + componentPrototype.mixins = _( componentPrototype.mixins ).chain().uniq().compact().value() + + componentPrototype