Home

Awesome

stevia: Natural sweetener for Javascript Objects

Stevia is an experimental module that provides natural sweetening for javascript objects. It can be used to add functionality to existing objects in an elegant, safe, and versatile way.

What do you mean "natural sweetening"?

Imagine you're working with a module that does something like this:

module.doSomethingWithElement = function(el) {
  // Do some stuff...
  document.body.appendChild(el);
}

Now imagine you want to use this method, but you're also working with jQuery and therefore all the elements you work with in your code are in the form of jQuery objects. This is great because as is obvious jQuery makes a lot of DOM operations trivial, but it also means that in order to work with the above module, you have to do something like

module.doSomethingWithElement($el.get(0));

This is kind of a pain, and it means that you constantly have to unwrap elements whenever you want to use this module's api. What's more is that maybe you also want to work with the plain element in your code sometimes, in which case you have to unwrap it and then what's the point of initially using the jQuery wrapper in the first place?

stevia allows you to do something like this:

var $el = stevia.sweeten(document.querySelector('.element'), function(el) {
  return $(el);
});

$el.addClass('foo'); // This will work
module.doSomethingWithElement($el); // This will also work

In the example above, a plain DOM element is passed in as the first argument to stevia.sweeten, and a function as the second argument which takes an object and returns a jQuery object which wraps its argument. The returned value $el looks, feels, and acts like a plain DOM element. However, when a jQuery method is called on it, it will be able to respond to it as if it were a jQuery object, even though nothing seems to have changed with respect to the original object passed in. This is the meaning of "naturally" sweeten; using this method one can extend objects in such a way that it can function as if it were extended yet still be consumed by APIs that expect it to act like it's not extended.

Here's another example of naturally sweetening an object using lodash

var a = stevia.sweeten({foo: 'bar', baz: 'bing'}, function(o) {
  return _(o)
});

console.log(JSON.stringify(o)); // '{"foo":"bar","baz":"bing"}'
console.log(o.values().tail().value()); // ['bing']

You can also add functions to the stevia.ingredients object, and then pass in string identifiers to stevia.sweeten which will use the function corresponding to the property name on stevia.ingredients. Here's an example of doing this with Dates and the amazing momentjs library.

var date, amazingDate;

stevia.ingredients.moment = function(date) {
	var klass = Object.prototype.toString.call(date).slice(8, -1);
	if (klass !== 'Date') {
		throw new Error('need a Date instance and you gave me a ' + klass);
	}
	return moment(date);
};

amazingDate = stevia.sweeten(new Date('Wed Nov 27 2013 18:28:31 GMT-0500 (EST)'), 'moment');
console.log(amazingDate.getHours()); // 27
console.log(amazingDate.format('DD/MM/YYYY')); // 27/11/2013

Installation

npm install stevia

This is an experimental module

This module relies on ES6 Proxies and the ES6 Reflect API in order to function correctly.

In NodeJS, you can simply run node --harmony, and the module should work. In CommonJS environments the module internally requires harmony-reflect so you don't have to worry about including it yourself.

You can check Kangax's Compatibility Table to see if the browser you're using supports proxies (note that in chrome you have to make sure you enable experimental javascript on the chrome://flags page).

In addition, you also need to provide the harmony-reflect api within browsers. You can use Tom Van Cutsem's Shim for this. A nice side-effect to this is that it allows direct proxies to be used in Chrome, meaning that this module will work in Chrome even though older versions use the old-style proxies.

Known Issues

Methods on Native objects currently have to be bound to that object as a receiver. This is the only way that this can work on objects like DOM Elements and Dates, which will complain if the receiver is not an instance of that constructor. Hopefully I can get rid of this once invoke() gets implemented, but in the meantime any suggestions by way of issues or pull requests would be awesome.

Feedback!

This module is an experimental idea. I'd love to get some feedback on it good, bad, otherwise. Open up an issue or submit a PR!

License

(The MIT License)

Copyright (c) 2013 Travis Kaufman <travis.kaufman@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.