Created
November 6, 2012 18:57
-
-
Save evocateur/4026724 to your computer and use it in GitHub Desktop.
Aliased Attribute Extension
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
<!DOCTYPE html> | |
<!--[if lt IE 7]> <html class="ie ie6" lang="en"> <![endif]--> | |
<!--[if IE 7]> <html class="ie ie7" lang="en"> <![endif]--> | |
<!--[if IE 8]> <html class="ie ie8" lang="en"> <![endif]--> | |
<!--[if gte IE 9]> <html class="ie" lang="en"> <![endif]--> | |
<!--[if !IE]><!--> <html lang="en"> <!--<![endif]--> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<title>z-aliased-attr Test Suite</title> | |
</head> | |
<body id="test" class="yui3-skin-sam"> | |
<script src="http://yui.yahooapis.com/3.7.3/build/yui/yui-debug.js"></script> | |
<script src="./z-aliased-attr-test.js"></script> | |
<script> | |
YUI({ | |
filter: (window.location.search.match(/[?&]filter=([^&]+)/) || [])[1] || 'raw' | |
}).use("z-aliased-attr-test", function (Y) { | |
new Y.Test.Console().render(); | |
Y.Test.Runner.add(Y.Z["z-aliased-attr-test-suite"]); | |
Y.Test.Runner.run(); | |
}); | |
</script> | |
</body> | |
</html> |
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
YUI.add("z-aliased-attr-test", function (Y) { | |
var suite = new Y.Test.Suite("z-aliased-attr"), | |
testATTRS = { | |
a: { | |
aliased: 'alpha', | |
value: 'A' | |
}, | |
b: { | |
value: 'B' | |
}, | |
c: { | |
aliased: 'gamma', | |
lazyAdd: false, | |
value: 'C' | |
} | |
}, | |
TestClass = Y.Base.create('testClass', Y.Base, [Y.Z.AliasedAttr], {}, { ATTRS: testATTRS }), | |
TestModel = Y.Base.create('testModel', Y.Model, [Y.Z.AliasedAttr], {}, { ATTRS: testATTRS }); | |
suite.add(new Y.Test.Case({ | |
name: "Aliased Attribute Lifecycle", | |
"_initAliasedAttrs should setup _attrNames property as an array": function () { | |
var instance = new TestClass(); | |
Y.ObjectAssert.ownsKey('_attrNames', instance, "Didn't cache _attrNames property"); | |
Y.Assert.isArray(instance._attrNames, "_attrNames wasn't an array"); | |
}, | |
"_initAliasedAttrs (Base) should filter protected attributes out of _attrNames array": function () { | |
var instance = new TestClass(), | |
names = instance._attrNames; | |
Y.ArrayAssert.containsItems(['a', 'b', 'c'], names, "Incorrect filtering: " + names); | |
}, | |
"_initAliasedAttrs (Model) should filter protected attributes out of _attrNames array": function () { | |
var instance = new TestModel(), | |
names = instance._attrNames; | |
Y.ArrayAssert.containsItems(['id', 'a', 'b', 'c'], names, "Incorrect filtering: " + names); | |
}, | |
"_initAliasedAttrs (Model) should filter custom idAttribute out of _attrNames array": function () { | |
// override Y.Model's default, 'id' | |
TestModel.prototype.idAttribute = 'a'; | |
var instance = new TestModel(), | |
names = instance._attrNames; | |
Y.ArrayAssert.containsItems(['a', 'b', 'c'], names, "custom idAttribute not removed: " + names); | |
// reset TestModel to Y.Model's default | |
delete TestModel.prototype.idAttribute; | |
}, | |
"_initAliasedAttrs should cache _getAliasedName function": function () { | |
var instance = new TestClass(); | |
Y.ObjectAssert.ownsKey('_getAliasedName', instance, "Didn't cache _getAliasedName function"); | |
Y.Assert.isFunction(instance._getAliasedName, "_getAliasedName function not created"); | |
}, | |
"getAlias() should be provided on instance": function () { | |
var instance = new TestClass(); | |
Y.Assert.isFunction(instance.getAliased, "getAliased() missing on instance"); | |
} | |
})); | |
suite.add(new Y.Test.Case({ | |
name: "Aliased Attribute Getters", | |
"getAliased() should return value when valid alias passed in": function () { | |
var instance = new TestClass(); | |
Y.Assert.areSame('A', instance.getAliased('alpha'), "getAliased('alpha') did not return 'A'"); | |
}, | |
"getAliased() should return undefined when invalid alias passed in": function () { | |
var instance = new TestClass(); | |
Y.Assert.isUndefined(instance.getAliased('beta'), "getAliased('beta') should not return 'B'"); | |
}, | |
"getAliased() should return value when valid non-alias passed in": function () { | |
var instance = new TestClass(); | |
Y.Assert.areSame('B', instance.getAliased('b'), "getAliased('b') did not return 'B'"); | |
}, | |
"getAliased() should return ad-hoc value when valid non-alias passed in": function () { | |
var instance = new TestModel({ | |
delta: 'D' | |
}); | |
Y.Assert.areSame('D', instance.getAliased('delta'), "getAliased('delta') did not return 'D'"); | |
}, | |
"getAliased() should return correct values over several calls": function () { | |
var instance = new TestClass(); | |
Y.Assert.areSame('A', instance.getAliased('alpha'), "getAliased('alpha') did not return 'A'"); | |
Y.ArrayAssert.containsItems(['b', 'c'], instance._attrNames, "_attrNames 'a' not removed after success"); | |
// during development, the search was corrupted by multiple failed searches | |
Y.Assert.isUndefined(instance.getAliased('beta'), "getAliased('beta') should not return 'B'"); | |
Y.Assert.isUndefined(instance.getAliased('beta'), "getAliased('beta') should not loop twice"); | |
Y.ArrayAssert.containsItems(['b', 'c'], instance._attrNames, "_attrNames changed after failures"); | |
Y.Assert.areSame('C', instance.getAliased('gamma'), "getAliased('gamma') did not return 'C'"); | |
Y.ArrayAssert.containsItems(['b'], instance._attrNames, "_attrNames 'c' not removed after success"); | |
}, | |
"stub": function () { | |
// var instance = new TestClass(); | |
} | |
})); | |
Y.namespace("Z")["z-aliased-attr-test-suite"] = suite; | |
}, "@VERSION@", { requires: ["test-console", "z-aliased-attr", "model"] }); |
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
YUI.add('z-aliased-attr', function (Y) { | |
/** | |
Aliased Attribute Extension | |
This extension provides the "aliased" property to Attribute config. | |
This is largely a convenience for referring to attributes internally | |
by more descriptive names than their URL parameter-safe name. | |
var PieModel = Y.Base.create('pieModel', Y.Model, [Y.Z.AliasedAttr], {}, { | |
ATTRS: { | |
c: { | |
aliased: 'crust', | |
value: 'flaky' | |
}, | |
slices: { | |
value: 8 | |
} | |
} | |
}); | |
var applePie = new PieModel({ flavor: 'apple' }); | |
applePie.get('c'); // => 'flaky' | |
applePie.getAliased('crust'); // => 'flaky' | |
applePie.getAliased('slices'); // => 8 (note fall-through) | |
applePie.getAliased('s'); // => undefined | |
applePie.getAliased('flavor'); // => 'apple' (supports ad-hoc attributes) | |
var appleJSON = applePie.toJSON(); | |
// appleJSON = { | |
// flavor: 'apple', | |
// c: 'flaky', | |
// slices: 8 | |
// } | |
@author Daniel Stockman | |
@since 2012/09 | |
**/ | |
var hasOwn = Object.prototype.hasOwnProperty, | |
hashArray = Y.Array.hash, | |
objectKeys = Y.Object.keys, | |
YBase = Y.Base; | |
// add "aliased" to attribute config whitelist | |
YBase._ATTR_CFG = YBase._ATTR_CFG.concat("aliased"); | |
YBase._ATTR_CFG_HASH = hashArray(YBase._ATTR_CFG); | |
/** | |
@class AliasedAttr | |
@namespace Z | |
@extensionfor Base | |
**/ | |
function AliasedAttr() {} | |
AliasedAttr.prototype = { | |
/** | |
Call _initAliasedAttrs during initializer. This must | |
be run after the host class initializer, therefore it | |
cannot be called from this extension's constructor. | |
@method initializer | |
@protected | |
**/ | |
initializer: function () { | |
this._initAliasedAttrs(); | |
}, | |
/** | |
Cache attribute names and bind a memoized _getAliased | |
method into _getAliasedName. | |
@method _initAliasedAttrs | |
@private | |
**/ | |
_initAliasedAttrs: function () { | |
Y.log('_initAliasedAttrs', 'debug', 'AliasedAttr'); | |
this._attrNames = this._getAttrNames(); | |
this._getAliasedName = Y.cached(Y.bind(this._getAliased, this)); | |
}, | |
/** | |
Caches the list of attribute names that we may need | |
to operate on. Protected class attributes are removed | |
from this list, as they will never be aliased. | |
@method _getAttrNames | |
@private | |
**/ | |
_getAttrNames: function () { | |
Y.log('_getAttrNames', 'debug', 'AliasedAttr'); | |
var attrNames = objectKeys(this._state.data); | |
return this._filterAttrNames(attrNames); | |
}, | |
/** | |
Duplicate Model's toJSON() filtering when caching the | |
list of attribute names. The initialized and destroyed | |
attribute names are always removed, and the Model-specific | |
values only if the host is a Model. | |
@method _filterModelAttrs | |
@param {Array} attrNames | |
@return {Array} with the undesired attribute names removed. | |
@private | |
**/ | |
_filterAttrNames: function (attrNames) { | |
Y.log('_filterAttrNames', 'debug', 'AliasedAttr'); | |
var hashed = hashArray(attrNames); | |
// remove default attributes that are never aliased | |
delete hashed.destroyed; | |
delete hashed.initialized; | |
// remove model metadata if necessary | |
if (this._isYUIModel) { | |
delete hashed.clientId; | |
if (this.idAttribute !== 'id') { | |
delete hashed.id; | |
} | |
} | |
return objectKeys(hashed); | |
}, | |
/** | |
Utility method to retrieve the attribute name associated | |
with a given alias. This method is bound to the instance | |
during initialization, wrapped in a Y.Cached() function. | |
Each time an attribute name is successfully matched, that | |
name is removed from the private _attrNames array to prevent | |
redundant searching (the value is cached, anyway). | |
@method _getAliased | |
@param {String} alias | |
@return {String} the aliased attribute name, if any | |
@private | |
**/ | |
_getAliased: function (alias) { | |
Y.log('_getAliased ' + alias, 'debug', 'AliasedAttr'); | |
var stateData = this._state.data, | |
attrNames = this._attrNames, | |
attrName, | |
datum, | |
idx = 0, | |
len = attrNames.length; | |
for (; idx < len; idx += 1) { | |
attrName = attrNames[idx]; | |
// ensure that the state object actually owns the property | |
if (hasOwn.call(stateData, attrName)) { | |
// get the data directly, bypassing State#getAll(name, true) | |
datum = stateData[attrName]; | |
// check for the 'aliased' property, regardless of laziness | |
if (datum && (datum.lazy && datum.lazy.aliased === alias || datum.aliased === alias)) { | |
// remove attrName from filtering array, so it is no longer tested | |
attrNames.splice(idx, 1); | |
return attrName; | |
} | |
} | |
} | |
}, | |
/** | |
Retrieve an aliased attribute value by it's alias | |
instead of the actual name. | |
If the alias does not exist, attempt to retrieve the | |
attribute value with the alias provided. | |
@method getAliased | |
@param {String} alias | |
@return {Mixed} | |
**/ | |
getAliased: function (alias) { | |
Y.log('getAliased ' + alias, 'debug', 'AliasedAttr'); | |
var attrName = this._getAliasedName(alias); | |
if (attrName) { | |
return this.get(attrName); | |
} | |
return this.get(alias); | |
} | |
}; | |
Y.namespace('Z').AliasedAttr = AliasedAttr; | |
}, '3.7.3', { | |
requires: [ | |
'base-build' | |
] | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment