UNPKG

@nodeguy/generic

Version:
266 lines (216 loc) 6.29 kB
"use strict"; const assert = require(`@nodeguy/assert`); const generic = require(`../lib`); const _ = generic._; describe("throws an exception when it doesn't match a pattern", function() { it("has a specific message for single arguments", function() { const futile = generic.function(); assert.throws( () => futile("Why do I bother?"), /Generic function: no method matches arguments \(Why do I bother/ ); }); it("has another message for multiple arguments", function() { const futile = generic.function(); assert.throws( () => futile("Why", "do", "I", "bother?"), /Generic function: no method matches arguments \(Why, do, I, bother\?\)\./ ); }); }); it("includes the function's name in error messages", function() { const futile = generic.function({ name: "futile" }); assert.throws( () => futile("Why do I bother?"), /Generic function 'futile': no method matches arguments \(Why do I bother/ ); }); it("matches the wildcard character", function() { const identity = generic.function(); identity.method(_, x => x); assert.equal(identity("Common Lisp"), "Common Lisp"); }); it("matches and returns values", function() { const fibonacci = generic.function(); fibonacci.method([_], n => fibonacci(n - 1) + fibonacci(n - 2)); fibonacci.method([1], 1); fibonacci.method([0], 0); assert.equal(fibonacci(13), 233); }); it("matches the most recently defined methods first", function() { const orderMatters = generic.function(); orderMatters.method(_, false); orderMatters.method(_, true); assert(orderMatters()); }); describe("matches Regular Expressions", function() { it("succeeds", function() { const parseDate = generic.function(); parseDate.method([/(\d{4})-(\d{2})-(\d{2})/], ([, year, month, day]) => ({ year, month, day })); assert.deepEqual(parseDate("1995-05-23"), { year: "1995", month: "05", day: "23" }); }); it("fails with a non-string", function() { const parseDate = generic.function(); parseDate.method(_, null); parseDate.method([/(\d{4})-(\d{2})-(\d{2})/], ([, year, month, day]) => ({ year, month, day })); assert.deepEqual(parseDate(null), null); }); it("fails with a non-matching string", function() { const parseDate = generic.function(); parseDate.method(_, null); parseDate.method([/(\d{4})-(\d{2})-(\d{2})/], ([, year, month, day]) => ({ year, month, day })); assert.deepEqual(parseDate("May 23, 1995"), null); }); }); it("matches with a predicate function", function() { const retort = generic.function(); retort.method( args => args.length > 2, () => "You are argumentative, aren't you?!" ); assert.equal( retort("and", "another", "thing"), "You are argumentative, aren't you?!" ); }); describe("matches objects", function() { it("succeeds", function() { const published = generic.function(); published.method( [ { ISBN: "1-55558-041-6" } ], ({ year }) => year ); assert.equal( published({ title: "Common Lisp the Language, 2nd edition", author: "Guy L. Steele", year: 1990, ISBN: "1-55558-041-6" }), 1990 ); }); it("fails", function() { const published = generic.function(); published.method(_, "unknown"); published.method( [ { title: "Common Lisp the Language, 2nd edition", ISBN: "1-55558-041-6" } ], ({ year }) => year ); assert.equal( published({ title: "Common Lisp the Language, 2nd edition" }), "unknown" ); }); }); it("matches with nested predicate functions", function() { const isArray = x => x instanceof Array; const isNumber = x => typeof x === "number"; const sum = generic.function(); sum.method([isNumber, isNumber], (x, y) => x + y); sum.method([isArray, isArray], (x, y) => x.concat(y)); assert.equal(sum(1, 2), 3); assert.deepEqual(sum([1], [2]), [1, 2]); }); it("matches deeply nested structures", function() { const fullName = generic.function(); fullName.method( [ "history", { name: _, predecessor: { name: _, predecessor: { name: "Lisp", author: name => name.split(" ").length > 1 } } } ], (type, lineage) => lineage.predecessor.predecessor.author ); assert.equal( fullName("history", { name: "ANSI Common Lisp", predecessor: { name: "Common Lisp", predecessor: { name: "Lisp", author: "John McCarthy" } } }), "John McCarthy" ); assert.throws( () => fullName("history", { name: "ANSI Common Lisp", predecessor: { name: "Common Lisp", predecessor: { name: "Lisp", author: "John" } } }), /Generic function: no method matches arguments \(history, \[object Object\]\)\./ ); }); it("optionally curries", function() { const curried = generic.function({ curry: true }); curried.method([x => typeof x === "boolean"], "boolean"); curried.method([Array.isArray, Array.isArray], "arrays"); assert.equal(curried(true), "boolean"); const partial = curried([1]); assert.equal(partial([2]), "arrays"); }); it("maps Arrays and Sets with mapping functions and objects", function() { const _ = generic._; const map = generic.function(); map.method([m => typeof m === "function", Array.isArray], (callback, array) => array.map(callback) ); map.method( [m => typeof m === "function", c => c instanceof Set], (callback, set) => new Set([...set].map(callback)) ); map.method([m => typeof m === "object", _], (table, collection) => map(value => table[value], collection) ); assert.deepEqual(map(Math.sqrt, [1, 4, 9]), [1, 2, 3]); assert.deepEqual(map(Math.sqrt, new Set([1, 4, 9])), new Set([1, 2, 3])); assert.deepEqual(map({ 1: 1, 4: 2, 9: 3 }, [1, 4, 9]), [1, 2, 3]); assert.deepEqual( map({ 1: 1, 4: 2, 9: 3 }, new Set([1, 4, 9])), new Set([1, 2, 3]) ); });