Adds CascadingModel base model, for looking up model attributes on a chain of diction...
authordsc <dsc@wikimedia.org>
Thu, 12 Apr 2012 00:34:41 +0000 (17:34 -0700)
committerdsc <dsc@wikimedia.org>
Thu, 12 Apr 2012 00:38:09 +0000 (17:38 -0700)
lib/base.co [deleted file]
lib/base/base-mixin.co [new file with mode: 0644]
lib/base/base-model.co [new file with mode: 0644]
lib/base/base-view.co [new file with mode: 0644]
lib/base/cascading-model.co [new file with mode: 0644]
lib/base/index.co [new file with mode: 0644]
lib/graph/graph-model.co
lib/util/cascade.co
www/modules.yaml

diff --git a/lib/base.co b/lib/base.co
deleted file mode 100644 (file)
index 1a818a3..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-{ _, op,
-} = require 'kraken/util'
-
-Backbone = require 'backbone'
-
-
-
-BaseBackboneMixin = exports.BaseBackboneMixin =
-    
-    initialize: ->
-        @__apply_bind__()
-    
-    
-    ### Auto-Bound methods
-    
-    /**
-     * A list of method-names to bind on `initialize`; set this on a subclass to override.
-     * @type Array<String>
-     */
-    __bind__ : []
-    
-    /**
-     * Applies the contents of `__bind__`.
-     */
-    __apply_bind__: ->
-        names = _ @pluckSuperAndSelf '__bind__' .chain().flatten().compact().unique().value()
-        _.bindAll this, ...names if names.length
-    
-    
-    
-    ### Synchronization
-    
-    /**
-     * Count of outstanding tasks.
-     * @type Number
-     */
-    waitingOn : 0
-    
-    
-    /**
-     * Increment the waiting task counter.
-     * @returns {this}
-     */
-    wait: ->
-        count = @waitingOn
-        @waitingOn += 1
-        console.log "#this.wait! #count --> #{@waitingOn}"
-        @trigger('start-waiting', this) if count is 0 and @waitingOn > 0
-        this
-    
-    /**
-     * Decrement the waiting task counter.
-     * @returns {this}
-     */
-    unwait: ->
-        count = @waitingOn
-        @waitingOn -= 1
-        console.warn "#this.unwait! #{@waitingOn} < 0" if @waitingOn < 0
-        console.log "#this.unwait! #count --> #{@waitingOn}"
-        @trigger('stop-waiting', this) if @waitingOn is 0 and count > 0
-        this
-    
-    /**
-     * @param {Function} fn Function to wrap.
-     * @returns {Function} A function wrapping the passed function with a call
-     *  to `unwait()`, then delegating with current context and arguments.
-     */
-    unwaitAnd: (fn) ->
-        self = this
-        ->
-            console.log "#self.unwaitAnd( function #{fn.name or fn.displayName}() )"
-            self.unwait()
-            fn ...
-    
-    
-
-mixinBase = exports.mixinBase = (body) ->
-    _.clone(BaseBackboneMixin) import body
-
-
-/**
- * @class Base model, extending Backbone.Model, used by scaffold and others.
- * @extends Backbone.Model
- */
-BaseModel = exports.BaseModel = Backbone.Model.extend mixinBase do # {{{
-    
-    constructor : function BaseModel
-        @__class__      = @constructor
-        @__superclass__ = @..__super__.constructor
-        @waitingOn      = 0
-        Backbone.Model ...
-        @trigger 'create', this
-    
-    
-    
-    
-    ### Accessors
-    
-    has: (key) ->
-        @get(key)?
-    
-    get: (key) ->
-        _.getNested @attributes, key
-    
-    # set: (key, value, opts) ->
-    #     if _.isObject(key) and key?
-    #         [values, opts] = [key, value]
-    #     else
-    #         values = { "#key": value }
-    #     
-    #     # TODO: Validation
-    #     @_changed or= {}
-    #     
-    #     for key, value in values
-    #         if _.str.contains key, '.'
-    #             _.setNested @attributes, key, value, opts
-    #         else
-    #             Backbone.Model::set.call this, key, value, opts
-    #     
-    #     this
-    # 
-    # unset : (key, opts) ->
-    #     
-    
-    
-    
-    
-    
-    ### Serialization
-    
-    serialize: (v) ->
-        # if v!?
-        #     v = ''
-        if _.isBoolean v
-            v =  Number v
-        else if _.isObject v
-            v = JSON.stringify v
-        String v
-    
-    /**
-     * Like `.toJSON()` in that it should return a plain object with no functions,
-     * but for the purpose of `.toKV()`, allowing you to customize the values
-     * included and keys used.
-     * @returns {Object}
-     */
-    toKVPairs: ->
-        kvo = _.collapseObject @toJSON()
-        for k, v in kvo
-            kvo[k] = @serialize v
-        kvo
-    
-    /**
-     * Serialize the model into a `www-form-encoded` string suitable for use as
-     * a query string or a POST body.
-     * @returns {String}
-     */
-    toKV: (item_delim='&', kv_delim='=') ->
-        _.toKV @toKVPairs(), item_delim, kv_delim
-    
-    /**
-     * @returns {String} URL identifying this model.
-     */
-    toURL: ->
-        "?#{@toKV ...}"
-    
-    toString: -> "#{@..name or @..displayName}(cid=#{@cid}, id=#{@id})"
-
-
-# Class Methods
-BaseModel import do
-    /**
-     * Factory method which constructs an instance of this model from a string of KV-pairs.
-     * This is a class method inherited by models which extend {BaseModel}.
-     * @static
-     * @param {String|Object} o Serialized KV-pairs (or a plain object).
-     * @returns {BaseModel} An instance of this model.
-     */
-    fromKV: (o, item_delim='&', kv_delim='=') ->
-        o   = _.fromKV o, item_delim, kv_delim if typeof o is 'string'
-        Cls = if typeof this is 'function' then this else this.constructor
-        new Cls _.uncollapseObject o
-
-# }}}
-
-/**
- * @class Base collection, extending Backbone.Collection, used by scaffold and others.
- * @extends Backbone.Collection
- */
-BaseList = exports.BaseList = Backbone.Collection.extend mixinBase do # {{{
-    
-    
-    constructor : function BaseList
-        @__class__      = @constructor
-        @__superclass__ = @..__super__.constructor
-        @waitingOn      = 0
-        Backbone.Collection ...
-        @trigger 'create', this
-    
-    
-    ### Serialization
-    
-    toKVPairs: ->
-        _.collapseObject @toJSON()
-    
-    toKV: (item_delim='&', kv_delim='=') ->
-        _.toKV @toKVPairs(), item_delim, kv_delim
-    
-    toURL: (item_delim='&', kv_delim='=') ->
-        "?#{@toKV ...}"
-    
-    toString: -> "#{@..name or @..displayName}(length=#{@length})"
-# }}}
-
-
-/**
- * @class Base view, extending Backbone.View, used by scaffold and others.
- * @extends Backbone.View
- */
-BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{
-    tagName : 'section'
-    
-    /**
-     * Array of [view, selector]-pairs.
-     * @type Array<[BaseView, String]>
-     */
-    subviews : []
-    
-    
-    
-    constructor : function BaseView
-        @__class__      = @constructor
-        @__superclass__ = @..__super__.constructor
-        @waitingOn      = 0
-        @subviews       = []
-        Backbone.View ...
-        @trigger 'create', this
-    
-    initialize: ->
-        @__apply_bind__()
-        
-        @setModel @model
-        @build()
-    
-    setModel: (model) ->
-        if @model
-            @model.off 'change',  @render, this
-            @model.off 'destroy', @remove, this
-            delete @model.view
-            data = @$el.data()
-            delete data.model
-            delete data.view
-        if @model = model
-            @model.view = this
-            @$el.data { @model, view:this }
-            @model.on 'change',  @render, this
-            @model.on 'destroy', @remove, this
-        @model
-    
-    
-    
-    ### Subviews
-    
-    addSubview: (selector, view) ->
-        [view, selector] = [selector, null] unless view
-        @subviews.push [view, selector]
-        view
-    
-    removeSubview: (view) ->
-        for [v, sel], idx of @subviews
-            if v is view
-                @subviews.splice(idx, 1)
-                return [v, sel]
-        null
-    
-    hasSubview: (view) ->
-        _.any @subviews, ([v]) -> v is view
-    
-    attachSubviews: ->
-        for [view, selector] of @subviews
-            return unless view
-            view.undelegateEvents()
-            return unless el = view.render()?.el
-            if selector
-                @$el.find selector .append el
-            else
-                @$el.append el
-            view.delegateEvents()
-        this
-    
-    
-    ### Rendering Chain
-    
-    toTemplateLocals: ->
-        json = {value:v} = @model.toJSON()
-        if _.isArray(v) or _.isObject(v)
-            json.value = JSON.stringify v
-        json
-    
-    $template: (locals={}) ->
-        $ @template do
-            { $, _, op, @model, view:this } import @toTemplateLocals() import locals
-    
-    build: ->
-        return this unless @template
-        outer = @$template()
-        @$el.html outer.html()
-            .attr do
-                id    : outer.attr 'id'
-                class : outer.attr('class')
-        @attachSubviews()
-        this
-    
-    render: ->
-        @build()
-        @trigger 'render', this
-        this
-    
-    renderSubviews: ->
-        _.invoke _.pluck(@subviews, 0), 'render'
-        this
-    
-    
-    
-    
-    ### UI Utilities
-    
-    hide   : -> @$el.hide();      this
-    show   : -> @$el.show();      this
-    remove : -> @$el.remove();    this
-    clear  : -> @model.destroy(); @remove()
-    
-    
-    # remove : ->
-    #     if (p = @$el.parent()).length
-    #         @$parent or= p
-    #         # @parent_index = p.children().indexOf @$el
-    #     @$el.remove()
-    #     this
-    # 
-    # reparent : (parent=@$parent) ->
-    #     parent = $ parent
-    #     @$el.appendTo parent if parent?.length
-    #     this
-    
-    toString : -> "#{@..name or @..displayName}(model=#{@model})"
-
-
-# Proxy model methods
-<[ get set unset toJSON toKV toURL ]>
-    .forEach (methodname) ->
-        BaseView::[methodname] = -> @model[methodname].apply @model, arguments
-
-# }}}
-
diff --git a/lib/base/base-mixin.co b/lib/base/base-mixin.co
new file mode 100644 (file)
index 0000000..00d25ce
--- /dev/null
@@ -0,0 +1,82 @@
+Backbone = require 'backbone'
+
+{ _, op,
+} = require 'kraken/util'
+
+
+
+BaseBackboneMixin = exports.BaseBackboneMixin =
+    
+    initialize: ->
+        @__apply_bind__()
+    
+    
+    ### Auto-Bound methods
+    
+    /**
+     * A list of method-names to bind on `initialize`; set this on a subclass to override.
+     * @type Array<String>
+     */
+    __bind__ : []
+    
+    /**
+     * Applies the contents of `__bind__`.
+     */
+    __apply_bind__: ->
+        names = _ @pluckSuperAndSelf '__bind__' .chain().flatten().compact().unique().value()
+        _.bindAll this, ...names if names.length
+    
+    
+    
+    ### Synchronization
+    
+    /**
+     * Count of outstanding tasks.
+     * @type Number
+     */
+    waitingOn : 0
+    
+    
+    /**
+     * Increment the waiting task counter.
+     * @returns {this}
+     */
+    wait: ->
+        count = @waitingOn
+        @waitingOn += 1
+        # console.log "#this.wait! #count --> #{@waitingOn}"
+        # console.trace()
+        @trigger('start-waiting', this) if count is 0 and @waitingOn > 0
+        this
+    
+    /**
+     * Decrement the waiting task counter.
+     * @returns {this}
+     */
+    unwait: ->
+        count = @waitingOn
+        @waitingOn -= 1
+        # console.warn "#this.unwait! #{@waitingOn} < 0" if @waitingOn < 0
+        # console.log "#this.unwait! #count --> #{@waitingOn}"
+        # console.trace()
+        @trigger('stop-waiting', this) if @waitingOn is 0 and count > 0
+        this
+    
+    /**
+     * @param {Function} fn Function to wrap.
+     * @returns {Function} A function wrapping the passed function with a call
+     *  to `unwait()`, then delegating with current context and arguments.
+     */
+    unwaitAnd: (fn) ->
+        self = this
+        ->
+            # console.log "#self.unwaitAnd( function #{fn.name or fn.displayName}() )"
+            # console.trace()
+            self.unwait(); fn ...
+    
+    
+
+mixinBase = exports.mixinBase = (body) ->
+    _.clone(BaseBackboneMixin) import body
+
+
diff --git a/lib/base/base-model.co b/lib/base/base-model.co
new file mode 100644 (file)
index 0000000..5eb7c18
--- /dev/null
@@ -0,0 +1,144 @@
+Backbone = require 'backbone'
+
+{ _, op,
+} = require 'kraken/util'
+{ BaseBackboneMixin, mixinBase,
+} = require 'kraken/base/base-mixin'
+
+
+
+/**
+ * @class Base model, extending Backbone.Model, used by scaffold and others.
+ * @extends Backbone.Model
+ */
+BaseModel = exports.BaseModel = Backbone.Model.extend mixinBase do # {{{
+    
+    constructor : function BaseModel
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        @waitingOn      = 0
+        Backbone.Model ...
+        @trigger 'create', this
+    
+    
+    
+    
+    ### Accessors
+    
+    has: (key) ->
+        @get(key)?
+    
+    get: (key) ->
+        _.getNested @attributes, key
+    
+    # set: (key, value, opts) ->
+    #     if _.isObject(key) and key?
+    #         [values, opts] = [key, value]
+    #     else
+    #         values = { "#key": value }
+    #     
+    #     # TODO: Validation
+    #     @_changed or= {}
+    #     
+    #     for key, value in values
+    #         if _.str.contains key, '.'
+    #             _.setNested @attributes, key, value, opts
+    #         else
+    #             Backbone.Model::set.call this, key, value, opts
+    #     
+    #     this
+    # 
+    # unset : (key, opts) ->
+    #     
+    
+    
+    
+    
+    
+    ### Serialization
+    
+    serialize: (v) ->
+        # if v!?
+        #     v = ''
+        if _.isBoolean v
+            v =  Number v
+        else if _.isObject v
+            v = JSON.stringify v
+        String v
+    
+    /**
+     * Like `.toJSON()` in that it should return a plain object with no functions,
+     * but for the purpose of `.toKV()`, allowing you to customize the values
+     * included and keys used.
+     * @returns {Object}
+     */
+    toKVPairs: ->
+        kvo = _.collapseObject @toJSON()
+        for k, v in kvo
+            kvo[k] = @serialize v
+        kvo
+    
+    /**
+     * Serialize the model into a `www-form-encoded` string suitable for use as
+     * a query string or a POST body.
+     * @returns {String}
+     */
+    toKV: (item_delim='&', kv_delim='=') ->
+        _.toKV @toKVPairs(), item_delim, kv_delim
+    
+    /**
+     * @returns {String} URL identifying this model.
+     */
+    toURL: ->
+        "?#{@toKV ...}"
+    
+    toString: -> "#{@..name or @..displayName}(cid=#{@cid}, id=#{@id})"
+
+
+# Class Methods
+BaseModel import do
+    /**
+     * Factory method which constructs an instance of this model from a string of KV-pairs.
+     * This is a class method inherited by models which extend {BaseModel}.
+     * @static
+     * @param {String|Object} o Serialized KV-pairs (or a plain object).
+     * @returns {BaseModel} An instance of this model.
+     */
+    fromKV: (o, item_delim='&', kv_delim='=') ->
+        o   = _.fromKV o, item_delim, kv_delim if typeof o is 'string'
+        Cls = if typeof this is 'function' then this else this.constructor
+        new Cls _.uncollapseObject o
+
+# }}}
+
+
+/**
+ * @class Base collection, extending Backbone.Collection, used by scaffold and others.
+ * @extends Backbone.Collection
+ */
+BaseList = exports.BaseList = Backbone.Collection.extend mixinBase do # {{{
+    
+    
+    constructor : function BaseList
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        @waitingOn      = 0
+        Backbone.Collection ...
+        @trigger 'create', this
+    
+    
+    ### Serialization
+    
+    toKVPairs: ->
+        _.collapseObject @toJSON()
+    
+    toKV: (item_delim='&', kv_delim='=') ->
+        _.toKV @toKVPairs(), item_delim, kv_delim
+    
+    toURL: (item_delim='&', kv_delim='=') ->
+        "?#{@toKV ...}"
+    
+    toString: -> "#{@..name or @..displayName}(length=#{@length})"
+# }}}
+
+
diff --git a/lib/base/base-view.co b/lib/base/base-view.co
new file mode 100644 (file)
index 0000000..9c4e5ec
--- /dev/null
@@ -0,0 +1,149 @@
+Backbone = require 'backbone'
+
+{ _, op,
+} = require 'kraken/util'
+{ BaseBackboneMixin, mixinBase,
+} = require 'kraken/base/base-mixin'
+
+
+
+/**
+ * @class Base view, extending Backbone.View, used by scaffold and others.
+ * @extends Backbone.View
+ */
+BaseView = exports.BaseView = Backbone.View.extend mixinBase do # {{{
+    tagName : 'section'
+    
+    /**
+     * Array of [view, selector]-pairs.
+     * @type Array<[BaseView, String]>
+     */
+    subviews : []
+    
+    
+    
+    constructor : function BaseView
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        @waitingOn      = 0
+        @subviews       = []
+        Backbone.View ...
+        @trigger 'create', this
+    
+    initialize: ->
+        @__apply_bind__()
+        
+        @setModel @model
+        @build()
+    
+    setModel: (model) ->
+        if @model
+            @model.off 'change',  @render, this
+            @model.off 'destroy', @remove, this
+            delete @model.view
+            data = @$el.data()
+            delete data.model
+            delete data.view
+        if @model = model
+            @model.view = this
+            @$el.data { @model, view:this }
+            @model.on 'change',  @render, this
+            @model.on 'destroy', @remove, this
+        @model
+    
+    
+    
+    ### Subviews
+    
+    addSubview: (selector, view) ->
+        [view, selector] = [selector, null] unless view
+        @subviews.push [view, selector]
+        view
+    
+    removeSubview: (view) ->
+        for [v, sel], idx of @subviews
+            if v is view
+                @subviews.splice(idx, 1)
+                return [v, sel]
+        null
+    
+    hasSubview: (view) ->
+        _.any @subviews, ([v]) -> v is view
+    
+    attachSubviews: ->
+        for [view, selector] of @subviews
+            return unless view
+            view.undelegateEvents()
+            return unless el = view.render()?.el
+            if selector
+                @$el.find selector .append el
+            else
+                @$el.append el
+            view.delegateEvents()
+        this
+    
+    
+    ### Rendering Chain
+    
+    toTemplateLocals: ->
+        json = {value:v} = @model.toJSON()
+        if _.isArray(v) or _.isObject(v)
+            json.value = JSON.stringify v
+        json
+    
+    $template: (locals={}) ->
+        $ @template do
+            { $, _, op, @model, view:this } import @toTemplateLocals() import locals
+    
+    build: ->
+        return this unless @template
+        outer = @$template()
+        @$el.html outer.html()
+            .attr do
+                id    : outer.attr 'id'
+                class : outer.attr('class')
+        @attachSubviews()
+        this
+    
+    render: ->
+        @build()
+        @trigger 'render', this
+        this
+    
+    renderSubviews: ->
+        _.invoke _.pluck(@subviews, 0), 'render'
+        this
+    
+    
+    
+    
+    ### UI Utilities
+    
+    hide   : -> @$el.hide();      this
+    show   : -> @$el.show();      this
+    remove : -> @$el.remove();    this
+    clear  : -> @model.destroy(); @remove()
+    
+    
+    # remove : ->
+    #     if (p = @$el.parent()).length
+    #         @$parent or= p
+    #         # @parent_index = p.children().indexOf @$el
+    #     @$el.remove()
+    #     this
+    # 
+    # reparent : (parent=@$parent) ->
+    #     parent = $ parent
+    #     @$el.appendTo parent if parent?.length
+    #     this
+    
+    toString : -> "#{@..name or @..displayName}(model=#{@model})"
+
+
+# Proxy model methods
+<[ get set unset toJSON toKV toURL ]>
+    .forEach (methodname) ->
+        BaseView::[methodname] = -> @model[methodname].apply @model, arguments
+
+# }}}
+
diff --git a/lib/base/cascading-model.co b/lib/base/cascading-model.co
new file mode 100644 (file)
index 0000000..e16f4d3
--- /dev/null
@@ -0,0 +1,50 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList,
+} = require 'kraken/base/base-model'
+
+Cascade = require 'kraken/util/cascade'
+
+
+
+/**
+ * @class A model that implements cascading lookups for its attributes.
+ */
+CascadingModel = exports.CascadingModel = BaseModel.extend do # {{{
+    /**
+     * The lookup cascade.
+     * @type Cascade
+     */
+    cascade : null
+    
+    
+    constructor: function CascadingModel (attributes={}, opts)
+        @cascade = new Cascade attributes
+        BaseModel.call this, attributes, opts
+    
+    initialize: ->
+        BaseModel::initialize ...
+    
+    get: (key) ->
+        @cascade.get key
+    
+    toJSON: (opts={}) ->
+        opts = {-collapseCascade, ...opts}
+        if opts.collapseCascade
+            @cascade.toJSON()
+        else
+            BaseModel::toJSON()
+    
+
+
+# Proxy Cascade methods
+<[
+    addLookup removeLookup popLookup shiftLookup unshiftLookup
+    isOwnProperty isOwnValue isInheritedValue isModifiedValue
+]>.forEach (methodname) ->
+    CascadingModel::[methodname] = -> @cascade[methodname].apply @cascade, arguments
+
+# }}}
+
+
+
diff --git a/lib/base/index.co b/lib/base/index.co
new file mode 100644 (file)
index 0000000..eb20dbb
--- /dev/null
@@ -0,0 +1,5 @@
+mixins    = require 'kraken/base/base-mixin'
+models    = require 'kraken/base/base-model'
+views     = require 'kraken/base/base-view'
+cascading = require 'kraken/base/cascading-model'
+exports import mixins import models import views import cascading
index ee33f02..21d9d5f 100644 (file)
@@ -241,7 +241,7 @@ Graph = exports.Graph = BaseModel.extend do # {{{
      * @returns {Boolean}
      */
     isChangedOption: (k) ->
-        @optionCascade.isChangedValue k
+        @optionCascade.isModifiedValue k
         and not @isDefaultOption k
     
     
index fb1cdb7..2f62caf 100644 (file)
@@ -53,6 +53,13 @@ class Cascade
         @_data = @_lookups[0] = data
         this
     
+    
+    /**
+     * @returns {Number} Number of lookup dictionaries.
+     */
+    size: ->
+        @_lookups.length
+    
     /**
      * Adds a new lookup dictionary to the chain.
      * @returns {this}
@@ -64,18 +71,46 @@ class Cascade
         this
     
     /**
-     * Removes a lookup dictionary from the chain.
+     * Removes a lookup dictionary from the chain (but will not remove the data object).
      * @returns {this}
      */
     removeLookup: (dict) ->
-        _.remove @_lookups, dict if dict
+        _.remove @_lookups, dict if dict and dict is not @_data
         this
     
     /**
+     * Pops the last dictionary off the lookup chain and returns it.
+     * @returns {*} The last dictionary, or `undefined` if there are no additional lookups.
+     */
+    popLookup: ->
+        return if @size() <= 1
+        @_lookups.pop()
+    
+    /**
+     * Shifts the first additional lookup dictionary off the chain and returns it.
+     * @returns {*} The first dictionary, or `undefined` if there are no additional lookups.
+     */
+    shiftLookup: ->
+        return if @size() <= 1
+        @_lookups.splice(1, 1)[0]
+    
+    /**
+     * Adds a lookup dictionary to the front of the chain, just after the Cascade's own data
+     * object.
+     * @returns {this}
+     */
+    unshiftLookup: (dict) ->
+        return this unless dict?
+        throw new Error "Lookup dictionary must be an object! dict=#dict" unless _.isObject dict
+        @_lookups.splice 1, 0, dict
+        this
+    
+    
+    /**
      * @returns {Boolean} Whether `key` belongs to this object (not inherited
      *  from the cascade).
      */
-    hasOwnProperty: (key) ->
+    isOwnProperty: (key) ->
         meta = _.getNestedMeta(@_data, key)
         meta?.obj and hasOwn.call meta.obj, key
     
@@ -84,32 +119,26 @@ class Cascade
      *  from the cascade) and is defined.
      */
     isOwnValue: (key) ->
-        @hasOwnProperty(key) and _.getNested(@_data, key, MISSING) is not MISSING
+        @isOwnProperty(key) and _.getNested(@_data, key, MISSING) is not MISSING
     
     /**
      * @returns {Boolean} Whether the value at `key` is different from that
      *  inherited by from the cascade.
      */
-    isChangedValue: (key, strict=false) ->
-        val = @get key
-        cVal = @_getInCascade key, MISSING, 1
-        if strict
-            val is not cVal
-        else
-            not _.isEqual val, cVal
+    isModifiedValue: (key, strict=false) ->
+        not @isInheritedValue key, strict
     
     /**
      * @returns {Boolean} Whether the value at `key` is the same as that
      *  inherited by from the cascade.
      */
     isInheritedValue: (key, strict=false) ->
-        not @isChangedValue key, strict
-    
-    /**
-     * @returns {Number} Number of lookup dictionaries.
-     */
-    size: ->
-        @_lookups.length
+        val = @get key
+        cVal = @_getInCascade key, MISSING, 1
+        if strict
+            val is cVal
+        else
+            _.isEqual val, cVal
     
     
     
index 93bb9d9..1e965c5 100644 (file)
@@ -53,7 +53,12 @@ dev:
                 - parser
                 - cascade
                 - index
-            - base
+            - base:
+                - base-mixin
+                - base-model
+                - base-view
+                - cascading-model
+                - index
             - scaffold:
                 - scaffold-model
                 - scaffold-view