Add explicit chaining support to Lo-Dash. [closes #325]

Former-commit-id: 58d01723ddc40f636af9954e5a9f6370e0c88aac
This commit is contained in:
John-David Dalton
2013-07-28 10:13:56 -07:00
parent c80c59c692
commit cc0c2f1b3c
4 changed files with 93 additions and 93 deletions

View File

@@ -653,72 +653,6 @@
* @returns {String} Returns the modified source.
*/
function addUnderscoreChaining(source) {
// add `_.chain`
source = source.replace(matchFunction(source, 'tap', true), function(match) {
var indent = getIndent(match);
return match && (indent + [
'',
'/**',
' * Creates a `lodash` object that wraps the given `value`.',
' *',
' * @static',
' * @memberOf _',
' * @category Chaining',
' * @param {Mixed} value The value to wrap.',
' * @returns {Object} Returns the wrapper object.',
' * @example',
' *',
' * var stooges = [',
" * { 'name': 'moe', 'age': 40 },",
" * { 'name': 'larry', 'age': 50 },",
" * { 'name': 'curly', 'age': 60 }",
' * ];',
' *',
' * var youngest = _.chain(stooges)',
' * .sortBy(function(stooge) { return stooge.age; })',
" * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; })",
' * .first();',
" * // => 'moe is 40'",
' */',
'function chain(value) {',
' value = new lodashWrapper(value);',
' value.__chain__ = true;',
' return value;',
'}',
'',
match
].join('\n' + indent));
});
// add `wrapperChain`
source = source.replace(matchFunction(source, 'wrapperToString', true), function(match) {
var indent = getIndent(match);
return match && (indent + [
'',
'/**',
' * Enables method chaining on the wrapper object.',
' *',
' * @name chain',
' * @memberOf _',
' * @category Chaining',
' * @returns {Mixed} Returns the wrapper object.',
' * @example',
' *',
' * var sum = _([1, 2, 3])',
' * .chain()',
' * .reduce(function(sum, num) { return sum + num; })',
' * .value()',
' * // => 6`',
' */',
'function wrapperChain() {',
' this.__chain__ = true;',
' return this;',
'}',
'',
match
].join('\n' + indent));
});
// remove `lodash.prototype.toString` and `lodash.prototype.valueOf` assignments
source = source.replace(/^ *lodash\.prototype\.(?:toString|valueOf) *=.+\n/gm, '');
@@ -783,11 +717,6 @@
].join('\n' + indent);
});
// replace `_.chain` assignment
source = source.replace(getMethodAssignments(source), function(match) {
return match.replace(/^( *lodash\.chain *= *)[\s\S]+?(?=;\n)/m, '$1chain')
});
// move `mixin(lodash)` to after the method assignments
source = source.replace(/(?:\s*\/\/.*)*\n( *)mixin\(lodash\).+/, '');
source = source.replace(getMethodAssignments(source), function(match) {
@@ -800,11 +729,6 @@
].join('\n' + indent);
});
// move the `lodash.prototype.chain` assignment to after `mixin(lodash)`
source = source
.replace(/^ *lodash\.prototype\.chain *=[\s\S]+?;\n/m, '')
.replace(/^( *)lodash\.prototype\.value *=/m, '$1lodash.prototype.chain = wrapperChain;\n$&');
return source;
}

View File

@@ -585,7 +585,6 @@
*
* @name _
* @constructor
* @alias chain
* @category Chaining
* @param {Mixed} value The value to wrap in a `lodash` instance.
* @returns {Object} Returns a `lodash` instance.
@@ -622,9 +621,11 @@
*
* @private
* @param {Mixed} value The value to wrap in a `lodash` instance.
* @param {Boolean} chainAll A flag to enable chaining for all methods
* @returns {Object} Returns a `lodash` instance.
*/
function lodashWrapper(value) {
function lodashWrapper(value, chainAll) {
this.__chain__ = !!chainAll;
this.__wrapped__ = value;
}
// ensure `new lodashWrapper` is an instance of `lodash`
@@ -5956,6 +5957,34 @@
/*--------------------------------------------------------------------------*/
/**
* Creates a `lodash` object that wraps the given `value`.
*
* @static
* @memberOf _
* @category Chaining
* @param {Mixed} value The value to wrap.
* @returns {Object} Returns the wrapper object.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 },
* { 'name': 'curly', 'age': 60 }
* ];
*
* var youngest = _.chain(stooges)
* .sortBy(function(stooge) { return stooge.age; })
* .map(function(stooge) { return stooge.name + ' is ' + stooge.age; })
* .first();
* // => 'moe is 40'
*/
function chain(value) {
value = new lodashWrapper(value);
value.__chain__ = true;
return value;
}
/**
* Invokes `interceptor` with the `value` as the first argument, and then
* returns `value`. The purpose of this method is to "tap into" a method chain,
@@ -5982,6 +6011,26 @@
return value;
}
/**
* Enables method chaining on the wrapper object.
*
* @name chain
* @memberOf _
* @category Chaining
* @returns {Mixed} Returns the wrapper object.
* @example
*
* var sum = _([1, 2, 3])
* .chain()
* .reduce(function(sum, num) { return sum + num; })
* .value()
* // => 6`
*/
function wrapperChain() {
this.__chain__ = true;
return this;
}
/**
* Produces the `toString` result of the wrapped value.
*
@@ -6024,6 +6073,7 @@
lodash.bind = bind;
lodash.bindAll = bindAll;
lodash.bindKey = bindKey;
lodash.chain = chain;
lodash.compact = compact;
lodash.compose = compose;
lodash.countBy = countBy;
@@ -6095,10 +6145,6 @@
// add functions to `lodash.prototype`
mixin(lodash);
// add Underscore compat
lodash.chain = lodash;
lodash.prototype.chain = function() { return this; };
/*--------------------------------------------------------------------------*/
// add functions that return unwrapped values when chaining
@@ -6162,9 +6208,14 @@
forOwn(lodash, function(func, methodName) {
if (!lodash.prototype[methodName]) {
lodash.prototype[methodName] = function() {
var args = [this.__wrapped__];
var args = [this.__wrapped__],
chainAll = this.__chain__;
push.apply(args, arguments);
return func.apply(lodash, args);
var result = func.apply(lodash, args);
return chainAll
? new lodashWrapper(result, chainAll)
: result;
};
}
});
@@ -6182,10 +6233,12 @@
forOwn(lodash, function(func, methodName) {
if (!lodash.prototype[methodName]) {
lodash.prototype[methodName]= function(callback, thisArg) {
var result = func(this.__wrapped__, callback, thisArg);
return callback == null || (thisArg && typeof callback != 'function')
var chainAll = this.__chain__,
result = func(this.__wrapped__, callback, thisArg);
return !chainAll && (callback == null || (thisArg && typeof callback != 'function'))
? result
: new lodashWrapper(result);
: new lodashWrapper(result, chainAll);
};
}
});
@@ -6202,6 +6255,7 @@
lodash.VERSION = '1.3.1';
// add "Chaining" functions to the wrapper
lodash.prototype.chain = wrapperChain;
lodash.prototype.toString = wrapperToString;
lodash.prototype.value = wrapperValueOf;
lodash.prototype.valueOf = wrapperValueOf;
@@ -6210,7 +6264,12 @@
baseEach(['join', 'pop', 'shift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
return func.apply(this.__wrapped__, arguments);
var chainAll = this.__chain__,
result = func.apply(this.__wrapped__, arguments);
return chainAll
? new lodashWrapper(result, chainAll)
: result;
};
});
@@ -6227,7 +6286,7 @@
baseEach(['concat', 'slice', 'splice'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
return new lodashWrapper(func.apply(this.__wrapped__, arguments));
return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
};
});
@@ -6239,13 +6298,16 @@
isSplice = methodName == 'splice';
lodash.prototype[methodName] = function() {
var value = this.__wrapped__,
var chainAll = this.__chain__,
value = this.__wrapped__,
result = func.apply(value, arguments);
if (value.length === 0) {
delete value[0];
}
return isSplice ? new lodashWrapper(result) : result;
return (chainAll || isSplice)
? new lodashWrapper(result, chainAll)
: result;
};
});
}

View File

@@ -1214,8 +1214,9 @@
vm.runInContext(data.source, context);
var lodash = context._;
ok(lodash.chain(1) instanceof lodash, '_.chain: ' + basename);
ok(lodash(1).chain() instanceof lodash, '_#chain: ' + basename);
var array = ['abc'];
ok(lodash.chain(array).first().first() instanceof lodash, '_.chain: ' + basename);
ok(lodash(array).chain().first().first() instanceof lodash, '_#chain: ' + basename);
var wrapped = lodash(1);
strictEqual(wrapped.identity(), 1, '_(...) wrapped values are not chainable by default: ' + basename);

View File

@@ -408,6 +408,19 @@
var wrapper = _({ 'a': 0 });
equal(wrapper.chain(), wrapper);
});
test('should enable chaining of methods that return unwrapped values by default', function() {
var array = ['abc'];
ok(_.chain(array).first() instanceof _);
ok(_(array).chain().first() instanceof _);
ok(_.chain(array).isArray() instanceof _);
ok(_(array).chain().isArray() instanceof _);
ok(_.chain(array).first().first() instanceof _);
ok(_(array).chain().first().first() instanceof _);
});
}());
/*--------------------------------------------------------------------------*/