From d7a4524e70a5ddb6ec4d6d5c50542b3f6468c39e Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 29 Mar 2015 00:28:01 -0400 Subject: [PATCH] Add `_.method` and `_.methodOf`. --- lodash.src.js | 30 +++++++++++++ test/test.js | 121 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/lodash.src.js b/lodash.src.js index e1ae11f10..224ce5449 100644 --- a/lodash.src.js +++ b/lodash.src.js @@ -2614,6 +2614,19 @@ } } + function baseMethod(object, path, args) { + if (object == null) { + return undefined; + } + if (!(isKey(path) || (path in toObject(object)))) { + path = toPath(path); + object = baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + } + var func = object[path]; + return isFunction(func) ? func.apply(object, args) : undefined; + } + /** * The base implementation of `_.property` without support for deep paths. * @@ -11227,6 +11240,21 @@ return baseMatchesProperty(path, baseClone(value, true)); } + var method = restParam(function(path, args) { + return function(object) { + return baseMethod(object, path, args); + } + }); + + var methodOf = restParam(function(object, args) { + if (object == null) { + return constant(undefined); + } + return function(path) { + return baseMethod(object, path, args); + }; + }); + /** * Adds all own enumerable function properties of a source object to the * destination object. If `object` is a function then methods are added to @@ -11784,6 +11812,8 @@ lodash.matchesProperty = matchesProperty; lodash.memoize = memoize; lodash.merge = merge; + lodash.method = method; + lodash.methodOf = methodOf; lodash.mixin = mixin; lodash.negate = negate; lodash.omit = omit; diff --git a/test/test.js b/test/test.js index e89b6371d..c1c3181cf 100644 --- a/test/test.js +++ b/test/test.js @@ -10353,6 +10353,125 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.method'); + + (function() { + test('should create a function that calls a property function of a given object', 3, function() { + var object = { 'a': _.constant(1), 'b': _.constant(2) }, + method = _.method('a'); + + strictEqual(method.length, 1); + strictEqual(method(object), 1); + + method = _.method('b'); + strictEqual(method(object), 2); + }); + + test('should work with non-string `prop` arguments', 1, function() { + var array = _.times(3, _.constant), + prop = _.method(1); + strictEqual(prop(array), 1); + }); + + test('should coerce key to a string', 1, function() { + function fn() {} + fn.toString = _.constant('fn'); + + var values = [null, undefined, fn, {}]; + var objects = _.map(values, function(value, index) { + var object = {}; + object[value] = _.constant(index); + return object; + }); + + var actual = _.map(objects, function(object, index) { + var method = _.method(values[index]); + return method(object); + }); + + deepEqual(actual, [0, 1, 2, 3]); + }); + + test('should pluck inherited property values', 1, function() { + function Foo() { this.a = 1; } + Foo.prototype.b = _.constant(2); + + var method = _.method('b'); + strictEqual(method(new Foo), 2); + }); + + test('should work when `object` is nullish', 1, function() { + var values = [, null, undefined], + expected = _.map(values, _.constant(undefined)); + + var actual = _.map(values, function(value, index) { + var method = _.method('a'); + return index ? method(value) : method(); + }); + + deepEqual(actual, expected); + }); + + test('should apply partial arguments to function', function() { + var object = { + 'fn': function() { + return slice.call(arguments); + } + }; + + var method = _.method('fn', 1, 2, 3); + deepEqual(method(object), [1, 2, 3]); + }); + }()); + + /*--------------------------------------------------------------------------*/ + + QUnit.module('lodash.methodOf'); + + (function() { + test('should create a function that calls a method function of a given key', 3, function() { + var object = { 'a': _.constant(1), 'b': _.constant(2) }, + methodOf = _.methodOf(object); + + strictEqual(methodOf.length, 1); + strictEqual(methodOf('a'), 1); + strictEqual(methodOf('b'), 2); + }); + + test('should pluck inherited method values', 1, function() { + function Foo() { this.a = 1; } + Foo.prototype.b = _.constant(2); + + var methodOf = _.methodOf(new Foo); + strictEqual(methodOf('b'), 2); + }); + + test('should work when `object` is nullish', 1, function() { + var values = [, null, undefined], + expected = _.map(values, _.constant(undefined)); + + var actual = _.map(values, function(value, index) { + var methodOf = index ? _.methodOf(value) : _.methodOf(); + return methodOf('a'); + }); + + deepEqual(actual, expected); + }); + + test('should apply partial arguments to function', function() { + var object = { + 'fn': function() { + return slice.call(arguments); + } + }; + + var method = _.methodOf(object, 1, 2, 3); + deepEqual(method('fn'), [1, 2, 3]); + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.min'); (function() { @@ -16838,7 +16957,7 @@ var acceptFalsey = _.difference(allMethods, rejectFalsey); - test('should accept falsey arguments', 215, function() { + test('should accept falsey arguments', 217, function() { var emptyArrays = _.map(falsey, _.constant([])), isExposed = '_' in root, oldDash = root._;