2.0.0
◀Back | Minified | Changelog | Cautionlog2016-07-17
After a major hiatus, Sugar 2.0.0 is finally here! This version has been a labor of love. It boasts a completely new way of interacting with the library, massive new features, code modularization, tons of performance optimizations, and a brand new site to match.
- Upgrading
- Extending in 2.0.0
- Core Rewrite
- Chainables
- Chainables as Extended Objects
- Modularized Builds
- Sugar as an Ecosystem
- Deep Property Notation
- Date strftime Format
- Array Iteration from an Index
- Performance
- Semver
- New Methods
- Renamed Methods
- Removed Methods
- Other Changes
- Bugfixes
Upgrading
To kick off the new features, beginning in v2.0.0 a companion upgrade script is available to make upgrading easier. This script will monitor your method calls and give information about breaking changes in real-time. See here for more.
Extending in 2.0.0
Although Sugar will continue to have the ability to modify native objects as before, v2.0.0 fundamentally changes this behavior to make it opt-in, and provides two new alternate ways of calling methods. Until now, Sugar was essentially off-limits to library or middleware developers, as extending global objects was something that could not be opted out of. Making this behavior opt-in means that it is no longer a deal breaker for writers of third-party code. Sugar will continue to endorse the safe extension of natives, but in a way that leaves it up to the end user as a choice.
Core Rewrite
To accomplish this change, Sugar's core has been fundamentally rewritten to
expose a single global object. All interactions with the library now happen
through this object. To begin, all methods are defined as static functions in
namespaces equivalent to their native counterparts, i.e. Sugar.Array
for
Array methods. Instance methods always accept the instance object as their
first argument, while static methods are called the same as before:
The previous behavior of extending natives is now controlled through a global
method called extend
. This method has a number of different options to allow
fine grained control over what methods get extended. Additionally, each
namespace has its own extend
method as well, allowing them to be called
individually:
Chainables
The second new method of interacting with Sugar methods comes in the form of
the "chainable" object. Chainables are constructors that have the exact same
prototype methods as those mapped onto natives with extend
, and so can call
instance methods in the same way. Chainables are easy to remember, because
they are the Sugar namespaces themselves. For example, Sugar.Array
is both
the namespace to call static array methods and also a chainable constructor:
When a chainable is created, it will wrap the native object passed to it as the
first argument. This object can be accessed at any time as the property raw
.
All methods called on the chainable object will operate on this object and
return a new chainable that wraps the result, allowing methods to be chained
together. When the time comes to get the result, simply access it with the raw
property:
// All user profiles.
var profiles = users.map('profile');
// Sum of all the profile likes.
var sum = profiles.sum('likes');
// Return the value
sum.raw;
Chainables can even use standard Javascript operators, as their underlying
value is exposed with valueOf
. Be aware however that this will unwrap the
chainable:
Chainables as Extended Objects
Previous versions of Sugar included a concept known as "extended objects".
These were a way to circumvent the fact that Sugar does not extend
Object.prototype
by having an internal type that mapped Object instance
methods to its own prototype instead. Chainables now replace these – in
fact they are the same concept applied across all native classes. In addition
to previous functionality, chainables also map native methods and are even more
versatile. They include three new methods: get
, set
, and has
. These
methods by default only return the object's own properties, while excluding
inherited properties. Combined with their new ability to handle deep property
notation (more below), this makes the Object chainable perfect for working with
complex data structures:
Modularized Builds
Modules in previous versions of Sugar were entire blocks of code that were added or removed together, and only through the site's rather primitive build system. v2.0.0 features a full package dependency model that allows custom builds at the method level. This site will host a build system that makes it easy to create a bundle of any method or date locale available. Even better, npm packages are now fully modularized as well and can also be used to create custom builds with the help of browserify or similar build tools:
// ... which have the module's methods defined on them:
Sugar.Array.unique(arr);
Sugar.Array.flatten(arr);
Sugar as an Ecosystem
As part of Sugar's modularization, its core functionality has been pulled out into its own npm package. In addition to making the library more modular, the core also exposes an API that allows new methods to be defined. Defining a method will allow interaction in all three forms of use – as a static method on the global, an instance method on a chainable, or in the global namespace in extended mode. All other Sugar modules now go through this API. However, separating the core is even more exciting as it means that third-party developers can author plugins too. Custom Sugar builds can now be composed not only of the Sugar library itself, but other plugins as well. This means that specialized use cases can be delegated to plugins and no longer have to belong to the main library itself.
Sugar.String.defineInstance('hello', function() {
return 'hello!';
});
Deep Property Notation
Sugar methods that accept a string as a reference to an object property can now
refer to deep properties using the dot .
or square bracket []
operators.
This ability begins with the new Object.get
and Object.set
methods but also
includes methods that allow mapping function shortcuts such as Array#map
,
Array#unique
, Array#groupBy
, Array#sortBy
, and more. Square bracket
syntax also allows for negative array indexes counted from the end of an array,
and range syntax with ..
to retrieve a subset of an array.
Date strftime format
Date#format
now allows strf tokens in addition to
standard "ldml" tokens.
Array Iteration from an Index
Previous versions of Sugar had the ability to iterate over an array starting
from a specific index and, optionally, looping again from the start. However the
implementation was scattered across Array#each
, Array#findFrom
, and usage
was confusing. v2.0.0 cleans this up by providing all ES5 and ES6 methods with
consistently named aliases ending in FromIndex
. All methods are their
"enhanced" versions, and so included mapping/matching shortcuts.
Performance
Although performance changes in 2.0.0 vary from method to method, overall this
version has seen the highest attention to performance so far. Date parsing has
seen especially major gains due to major refactoring, and a number of core
methods were reworked as well, including string formatting that feeds
String#format
(previously String#assign
) and Date#format
.
Semver
As Sugar is now far more relevant to library and middleware developers than it was before, it will begin strictly following Semver. Previously, MINOR verisions would occasionally have breaking changes. Going forward, a roadmap will be made available on Github, and breaking changes will always prompt a MAJOR version increment.
New Methods
- Date#get - New date creation with a context date for reference.
- Range#dateUnit - Range date units like Range#years, Range#months, etc.
- Function#memoize - Memoizing expensive function calls
- Object.isArguments
- Array#sample
- String#replaceAll
- String#removeAll
- Array#median
- Array#isEqual
- Object.remove
- Object.exclude
- Object.invert
Renamed Methods
String#escapeRegExp
is nowRegExp.escape
Date#utc
is nowDate#setUTC
Object.equal
andObject.equals
are now bothObject.isEqual
Function#fill
is nowFunction#partial
Array#randomize
is nowArray#shuffle
RegExp#addFlag
is nowRegExp#addFlags
and works on multiple.RegExp#removeFlag
is nowRegExp#removeFlags
and works on multiple.Object.each
is nowObject.forEach
String#each
is nowString#forEach
String#has
is now nativeString#includes
(polyfilled)Object.isNaN
is now nativeNumber.isNaN
(polyfilled)Array#findAll
is now aligned with nativeArray#filter
Object.findAll
is nowObject.filter
Removed Methods
String#normalize
was removed.Object.watch
andObject.unwatch
were removed.Array#include
was removed (nowArray#add
).Object.extended
is now gone in favor of Object chainables which are much more versatile.Array#all
was removed in favor ofArray#every
.Array#any
was removed in favor ofArray#some
.Object.all
was removed in favor ofObject.every
.Object.any
was removed in favor ofObject.some
.String#add
was removed.String#paragraphs
was removed.Object.keys
andObject.values
no longer accept a callback.Object.forEach
should be preferred method of iteration.
Other Changes
Callbacks in object iteration methods like Object.forEach
(previously Object.each
), Object.filter
, etc. are now value, key where they were previously key, value. This change helps keep Sugar in line with other libaries as well as keep parity with these methods on Arrays.
Object.isEqual
now handles ES6 objects like Maps, Sets, and Typed Arrays, and is generally more robust.Object.merge
updated to handle a number of new options.Array#sortBy
handles sorting on multiple properties.Number#bytes
now allows normal SI units.Number.setOption
allows custom thousands and decimal markers.Date.create
takes a number of new options and replaces clunky APIs likeDate.utc.create
, etc.Object.fromQueryString
andObject.toQueryString
now accept an options object that allow customization.Object.has
now is much more useful when using objects as data.String#stripTags
andStrip#removeTags
now allow a callback.String#capitalize
has an extra parameter to downcase the rest of the string.
Bugfixes
- Fixed
String#escapeHTML
to double-escape entities. - Fixed issue with the digit
ten
in date creation (Issue #431). - Fixed issue with the
Date#[unit]Since
improperly applying error margins. - Fixed issue with dates shifting in "monthsFromNow" (and consequently "relative") when traversing into a month that doesn't have enough days.
- Fixed issue with advance/rewind using an object with both a "week" and "day" parameter (Issue #492).
- Fixed issue
Function#every
not being able to cancel itself (Issue #488). - Fixed issue with German dates not allowing abbreviated weekdays with 2 characters.
- Fixed many issues with DST and simplified month traversal.
- Fixed various date parsing issues (#524, #468, #431, #420).
- Fixed issue with
Object.clone
not handling accessors (#396). - Fixed issue with
fromQueryString
and empty values (#438). - Fixed issue with
Array#compact
(#462). - Fixed issue with
Function#cancel
(#488). - Fixed issues with Rhino (#447).
- Fixed issue with
isBetween
(#425).