1.4.0

Back | Minified | Changelog | Cautionlog
2013-08-24

This update brings a lot of changes, so please have a look at the Cautionlog above to make upgrading a lot less painful!

Also, if you are unable to upgrade, please look here for a patch that will provide better future-proofing for older versions.

Performance Enhancements

By far the biggest change in v1.4 is an across the board look at performance. Some of the more dramatic changes include:

Although some methods have been given special attention, many of the refactorings were on internal utility methods, which means that the performance enhancements will have a very wide affect. To demonstrate this I looked at simple date creation, an area that on the surface hasn't changed since the last version:

// v1.3.9 Date.create('the day after tomorrow'); // 1000 iterations @ 74ms
// v1.4.0 Date.create('the day after tomorrow'); // 1000 iterations @ 60ms

This method, for this format, is now 23% faster thanks to things like speeding up internal type checking etc. Different methods for different cases should also show similar gains.

Natural Sorting

Array sorting using Array#sortBy now has an added feature to its string collation system which will perform a natural sort by default. This means that if it encounters numeric values it will sort them by their numericality instead of as a string. This means that "25" will be sorted after "7", etc. This option can be set by changing the flag Array.AlphanumericSortNatural. Additionally, the string collation function itself is now exposed globally as Array.AlphanumericSort, which means it can be passed directly into Javasript native Array#sort as well.

Ranges

Ranges were previously a concept that only existed for dates and was a dependency of the Date package. It is now moved out into its own package, simplified, and now works with numbers and strings as well! Syntax for creating a range is the same:

Date.range('yesterday', 'tomorrow'); Number.range(1, 100); String.range('a','z');

New to ranges is the ability to deal with inverted ranges (a higher number/date can iterate down to a lower one), and the clamp method which will contain a value to within the range:

var range = Number.range(1, 100); range.clamp(50); // 50 range.clamp(500); // 100 range.clamp(-500); // 1

The clamp method as well as cap (which only limits an upper number) are also now aliases on the numbers:

(5).clamp(50, 100); // 50 (5).clamp(1, 10); // 5 (5).cap(1) // 1

Additionally, the previous method duration is now renamed to span. Finally, Array.create can understand ranges and convert them to arrays when needed.

Array.find and Array.findIndex

These methods that are defined in the ES6 spec previously existed but worked slightly differently. They have now been aligned with spec and the previous functionality has been moved:

// v1.3.9 Second argument was previously // the index from which to start searching [1,2,3].find(function(el) { return el === 3; }, 1);
// v1.4.0 Second argument is now the context // that the iterator function will be run with. // This is similar to methods like forEach, etc. [1,2,3].find(function(el) { return el === 3; }, {});
// For previous behavior use Array#findFrom // and Array#findIndexFrom instead. [1,2,3].findFrom(function(el) { return el === 3; }, 1);

Additionally these methods will act as polyfills, so when they are implemented in later browser versions they will fall back to native implementations. Which brings us to...

Better future-proofing

Sugar previously had a very simple policy. It would never overwrite methods in the global namespace if they were there first. Although this seems to make sense on the surface, it presents a very pernicious problem: if browsers change to add a method that collides, it will effectively overwrite ("underwrite"?) the Sugar implementation. It means that a browser update initiated by the user could potentially cause a script to break.

Of course staying on top of the spec and anticipating such changes is the real name of the game (with the above changes to Array#find and Array#findIndex and a couple others, Sugar is now fully in line with the ES6 spec for the foreseeable future). However there may be many reasons that a user can't upgrade, and a browser update should not cause breakages.

For this reason, Sugar has made the reluctant decision to instead default to overriding those methods that it has not deemed polyfills (which it will fall back to, conversely). Although this unfortunately makes Sugar feel less "friendly", real world usage has shown this to be a much better solution. In the end, if a user explicitly includes the Sugar library, they need to be guaranteed that the methods they are expecting will actually be there and not suddenly change.

More natural padding

Previously String#pad, String#padLeft, and String#padRight worked in a rather counter-intuitive manner -- if 20 was passed they would put 20 characters on the ends of the string. They have now changed to be analagous to Number#pad and will add only enough padding to reach the specified length. If they string is already larger than this length, the method effectively does nothing.

More control over date creation

New to this version is Date.SugarNewDate. This hook is a way to override the Sugar internal date creation mechanism, which is now consolidated into one place. The common use case for this is timezones. The topic gets a bit complicated -- Javascript dates have no concept of timezones except the one of the current browser locale, which makes having a date in another timezone tricky. Often to get around this dates are created and then shifted to act as though they were in another timezone on the surface (even though the underlying zone is the same). While this method may work, there are many cases when Sugar creates, uses, and disposes of dates before a user can get at them to shift the zone. This often happens in date comparison date.is('the day after tomorrow'). Date.SugarNewDate serves as a hook to allow any new date created to pass through this function before being used:

Date.SugarNewDate = function() { var d = new Date(); // Effectively removes the current locale's timezone, // putting the date in UTC time. d.setTime(d.getTime() + (d.getTimezoneOffset() * 60 * 1000)); return d; }

Exposing this as a function gives the user maximum control, allowing even use of full timezone libraries that take into account Daylight Savings Time, etc, for maximum precision:

Date.SugarNewDate = function() { return someAwesomeTimezoneLib.createDateInZone('America/New_York'); }

Other changes