Add #fetch Utility function

The #fetch utility acts in (roughly) the same way as Ruby's Hash#fetch function.
This addition should hopefully make constructors more rigorous and help to
prevent a few classes of errors.
This commit is contained in:
Andrew Stewart 2014-06-17 16:00:36 -07:00
parent af8cfbe333
commit 6f42b061b3
2 changed files with 103 additions and 0 deletions

View File

@ -183,6 +183,61 @@ var Utils = module.exports = {
};
},
// Public: Analogue to Ruby's Hash#fetch method for looking up object
// properties.
//
// obj - object to do property lookup on
// property - string property name to attempt to look up
// fallback - either:
// - a fallback value to return if `property` can't be found
// - a function to be executed if `property` can't be found. The function
// will be passed `property` as an argument.
//
// Examples
//
// var object = { property: "hello world" };
// fetch(object, "property");
// //=> "hello world"
//
// fetch(object, "notaproperty", "default value");
// //=> "default value"
//
// var notFound = function(prop) { return prop + " not found!" };
// fetch(object, "notaproperty", notFound)
// // "notaproperty not found!"
//
// var badFallback = function(prop) { prop + " not found!" };
// fetch(object, "notaproperty", badFallback)
// // Error: no return value from provided callback function
//
// fetch(object, "notaproperty");
// // Error: key not found: "notaproperty"
//
// Returns the value of obj[property], a fallback value, or the results of
// running 'fallback'. If the property isn't found, and 'fallback' hasn't been
// provided, will throw an error.
fetch: function(obj, property, fallback) {
if (obj.hasOwnProperty(property)) {
return obj[property];
}
if (fallback === void 0) {
throw new Error('key not found: "' + property + '"');
}
if (typeof(fallback) === 'function') {
var value = fallback(property);
if (value === void 0) {
throw new Error('no return value from provided fallback function');
}
return value;
}
return fallback;
},
// Public: Adds necessary utils to global namespace, along with base class
// extensions.
//

View File

@ -222,4 +222,52 @@ describe("Utils", function() {
expect(proxy.boundMethod("Hello", "World")).to.eql(["Hello", "World"]);
})
});
describe("#fetch", function() {
var fetch = utils.fetch,
obj = { property: 'hello world', 'false': false, 'null': null };
context("if the property exists on the object", function() {
it("returns the value", function() {
expect(fetch(obj, 'property')).to.be.eql('hello world');
expect(fetch(obj, 'false')).to.be.eql(false);
expect(fetch(obj, 'null')).to.be.eql(null);
});
});
context("if the property doesn't exist on the object", function() {
context("and no fallback value has been provided", function() {
it("throws an Error", function() {
var fn = function() { return fetch(obj, "notaproperty"); };
expect(fn).to.throw(Error, 'key not found: "notaproperty"');
});
});
context("and a fallback value has been provided", function() {
it('returns the fallback value', function() {
expect(fetch(obj, 'notakey', 'fallback')).to.be.eql('fallback');
});
});
context("and a fallback function has been provided", function() {
context("if the function has no return value", function() {
it("throws an Error", function() {
var fn = function() { fetch(obj, 'notakey', function() {}); },
str = 'no return value from provided fallback function';
expect(fn).to.throw(Error, str);
});
});
context("if the function returns a value", function() {
it("returns the value returned by the fallback function", function() {
var fn = function(key) { return "Couldn't find " + key },
value = "Couldn't find notakey";
expect(fetch(obj, 'notakey', fn)).to.be.eql(value);
});
});
});
});
});
});