Archives Posts
Namespacing made easy
Enumerable#inject is one scary method. It took me a while to understand the beauty of this functional programming paradigm. Just in case you were ever wondering about its real life usage - here’s one of them.
We all know that using namespaces is generally a wise thing to do. You don’t have to create a multi level object structure, but nesting your code under a pseudo-namespace is what makes maintenance, upgrades and migrations somewhat easier and error-prone.
Few popular javascript libraries implement some sort of a utility function that automatically creates nested objects. What it means is that instead of writing it manually, you could let the helper handle it for you:
// creating nested structure manually is painful var com = { thinkweb2: { projects: { _prototype: 'a deeply nested property...' } } } com.thinkweb2.projects._prototype // => 'a deeply nested property...'
Well, it appears that inject can handle such task in only 3 lines of code:
'com.thinkweb2.projects.prototype'.split('.').inject(window, function(parent, child) { var o = parent[child] = { }; return o; }) Object.inspect(com.thinkweb2.projects.prototype) // => "[object Object]"
Not bad - a completely generic namespacing snippet.
What’s happening here is not a black magic. Inject accepts initial object as a first argument (in this case - window object) and iterator function as the second argument. It then calls iterator function on each item from the collection (in our case it’s simply a string split into an array). Iterator function receives accumulated result as a first argument and current value as a second. Iterator performs an action (creates a nested object) and returns accumulated result (the newly created parent object) to be used in following iterations.
Alternatively, we could wrap this all nicely and define as a String.prototype method:
String.prototype.namespace = function(separator){ this.split(separator || '.').inject(window, function(parent, child) { var o = parent[child] = { }; return o; }) } foo.bar.baz.quux = 'Nothing special...'; // => ERROR: foo is not defined // Default separator is '.' 'foo.bar.baz.quux'.namespace(); foo.bar.baz.quux = 'Nothing special...'; // => "Nothing special..." // Or using a custom one 'MY_AWESOME_APPLICATION::util::DOM::dimensions'.namespace('::'); 'dimensions' in MY_AWESOME_APPLICATION.util.DOM; // => true
Note how we use “separator” argument (or defaulting to ‘.’) making the function somewhat more flexible.
P.S.
Heavy namespacing could lead to unnecessary complexity.
Lack of it - to buggy behavior.
In the end, the only thing that matters is what works best for you.
Enjoy!