Skip to content
This repository was archived by the owner on Oct 24, 2023. It is now read-only.

Fn #15

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Fn #15

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/has.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = has

function has(x, key) {
return is(x) && hasprop.call(x, key)
}

const hasprop = Object.prototype.hasOwnProperty
, is = require("./is")
28 changes: 28 additions & 0 deletions lib/meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = meta

const protoOf = Object.getPrototypeOf
, mutable = Object.isExtensible
, define = Object.defineProperty
, create = Object.create
, names = Object.getOwnPropertyNames
, isnt = require("./isnt")
, has = require("./has")

function meta(x, data) {
if (isnt(x)) return null

if (arguments.length < 2) return (x[meta.key] || null)

if (!mutable(x)) throw TypeError("Can't add metadata to non-extensible object.")

const metadata = x[meta.key] || define(x, meta.key, { value: create(null) })[meta.key]

names(data).forEach(function(name) {
if (has(metadata, name)) throw TypeError("Can't change immutable metadata: "+name)
define(metadata, name, { value: data[name], enumerable: true })
})

return metadata
}

define(meta, "key", { value: "^metadata:{}" })
4 changes: 1 addition & 3 deletions lib/not.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@ module.exports = not

function not(x) {
return x === false || x == null
}

const isnt = require('./isnt')
}
14 changes: 4 additions & 10 deletions lib/thunk.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
module.exports = require('./variadic')(thunk)

function thunk(fn, rest) {
if (isnt(Function, fn)) {
rest = [fn]
fn = identity
}
if (isnt(Function, fn)) throw new TypeError('Expected `fn` to be a function.')

if (rest.length > 1) {
if (is(rest)) {
return function thunk() {
return fn.apply(this, rest)
}
} else if (rest.length) {
return function thunk() {
return fn.call(this, rest[0])
}
} else {
return function thunk() {
return fn.call(this)
Expand All @@ -23,4 +16,5 @@ function thunk(fn, rest) {

const identity = require('./identity')
, slice = require('./slice')
, isnt = require('./isnt')
, isnt = require('./isnt')
, is = require('./is')
58 changes: 42 additions & 16 deletions lib/variadic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,49 @@ module.exports = variadic

function variadic(fn) {
if (isnt(Function, fn)) throw new TypeError('Parameter `fn` must be a function.')

const argc = fn.length - 1

const argc = fn.length - 1

if (argc) {
return function variadic() {
const head = slice(arguments, 0, argc)
, tail = slice(arguments, argc)

return fn.apply(this, head.concat([tail]))
}
} else {
return function variadic() {
const rest = slice(arguments)
return fn.call(this, rest)
}
switch (argc) {
case -1 : return fn
case 0 : return function (rest) { return apply(fn, this, argc, slice(arguments)) }
case 1 : return function (a, rest) { return apply(fn, this, argc, slice(arguments)) }
case 2 : return function (a, b, rest) { return apply(fn, this, argc, slice(arguments)) }
case 3 : return function (a, b, c, rest) { return apply(fn, this, argc, slice(arguments)) }
case 4 : return function (a, b, c, d, rest) { return apply(fn, this, argc, slice(arguments)) }
case 5 : return function (a, b, c, d, e, rest) { return apply(fn, this, argc, slice(arguments)) }
case 6 : return function (a, b, c, d, e, f, rest) { return apply(fn, this, argc, slice(arguments)) }
case 7 : return function (a, b, c, d, e, f, g, rest) { return apply(fn, this, argc, slice(arguments)) }
case 8 : return function (a, b, c, d, e, f, g, h, rest) { return apply(fn, this, argc, slice(arguments)) }
case 9 : return function (a, b, c, d, e, f, g, h, i, rest) { return apply(fn, this, argc, slice(arguments)) }
case 10 : return function (a, b, c, d, e, f, g, h, i, j, rest) { return apply(fn, this, argc, slice(arguments)) }
case 11 : return function (a, b, c, d, e, f, g, h, i, j, k, rest) { return apply(fn, this, argc, slice(arguments)) }
case 12 : return function (a, b, c, d, e, f, g, h, i, j, k, l, rest) { return apply(fn, this, argc, slice(arguments)) }
case 13 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, rest) { return apply(fn, this, argc, slice(arguments)) }
case 14 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, rest) { return apply(fn, this, argc, slice(arguments)) }
case 15 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, rest) { return apply(fn, this, argc, slice(arguments)) }
case 16 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, rest) { return apply(fn, this, argc, slice(arguments)) }
case 17 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, rest) { return apply(fn, this, argc, slice(arguments)) }
case 18 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, rest) { return apply(fn, this, argc, slice(arguments)) }
case 19 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, rest) { return apply(fn, this, argc, slice(arguments)) }
case 20 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, rest) { return apply(fn, this, argc, slice(arguments)) }
case 21 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, rest) { return apply(fn, this, argc, slice(arguments)) }
case 22 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, rest) { return apply(fn, this, argc, slice(arguments)) }
case 23 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, rest) { return apply(fn, this, argc, slice(arguments)) }
case 24 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, rest) { return apply(fn, this, argc, slice(arguments)) }
case 25 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, rest) { return apply(fn, this, argc, slice(arguments)) }
default : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, rest) { return apply(fn, this, argc, slice(arguments)) }
}
}

const slice = require('./slice')
, isnt = require('./isnt')
function apply(fn, host, argc, argv) {
const rest = slice(argv, argc)
argv = slice(argv, 0, argc)
return isEmpty(rest)? fn.apply(host, argv) : fn.apply(host, argv.concat([rest]))
}

const isEmpty = require('./isEmpty')
, slice = require('./slice')
, isnt = require('./isnt')
, min = require('./min')
, max = Math.max
85 changes: 85 additions & 0 deletions test/meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const expect = require("chai").expect
, each = require("../lib/each")
, meta = require("../lib/meta")
, $ = require("../lib/partial")

describe("`meta`", function() {
describe("given an object `x`", function() {
describe("when `x` is logically false", function() {
it("should return `null`", function() {
each([null, false, undefined], function(x) {
expect(meta(x)).to.be.null
})
})
})

describe("when called with no other parameters", function() {
describe("and when `x` is associated with metadata", function() {
it("should return the metadata object", function() {
const m = { foo: "wibble" }
, x = {}

meta(x, m)
expect(meta(x)).to.eql(m)
})
})

describe("but when `x` is not associated with metadata", function() {
it("should return null", function() {
expect(meta({})).to.be.null
})
})
})

describe("and also given a map `data`", function() {
describe("when `x` is not extensible", function() {
it("should throw an error", function() {
each(
[ Object.preventExtensions({})
, Object.freeze({})
, Object.seal({})
]
, function(x) {
expect($(meta, x, {})).to.throw()
}
)
})
})

describe("when `x` is not associated with any metadata", function() {
it("should set all values of `data` and return the full metadata object", function() {
const m = { foo: "wibble" }
, x = {}

expect(meta(x, m)).to.eql(m)
expect(meta(x)).to.eql(m)
})
})

describe("when `x` is associated with metadata", function() {
describe("and when no properties of `data` overlap", function() {
it("should set all values of `data` and return the full metadata object", function() {
const a = { foo: "wibble" }
, b = { bob: "wobble" }
, c = { foo: "wibble", bob: "wobble" }
, x = {}

expect(meta(x, a)).to.eql(a)
expect(meta(x, b)).to.eql(c)
})
})

describe("but when any one property of `data` overlap", function() {
it("should throw an error", function() {
const a = { foo: "wibble" }
, b = { foo: "wobble" }
, x = {}

expect(meta(x, a)).to.eql(a)
expect($(meta, x, b)).to.throw()
})
})
})
})
})
})
41 changes: 12 additions & 29 deletions test/thunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,35 +214,18 @@ describe('thunk', function() {
})

describe('when given a non-function `val` as the first parameter', function() {
it('should return a zero arity thunk', function() {
const th = thunk(function() {})

expect(th).to.be.a('function')
expect(th.length).to.equal(0)
expect(th.name).to.equal('thunk')
})

describe('and when called', function() {
it('should return `val` and nothing else', function() {
each(
[ 0
, 1
, true
, false
, ''
, 'hello'
, []
, {}
, null
, undefined
]
,
function(val) {
const th = thunk(val)
expect(th()).to.equal(val)
}
)
})
it('should throw a TypeError', function() {
each(
[ 0, 1
, true, false
, '', 'hello'
, [], {}
, null, undefined
]
, function(val) {
expect(thunk(thunk, val)).to.throw(TypeError)
}
)
})
})
})
Loading