npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

indeed

v1.1.0

Published

Boolean helpers for node.js

Downloads

205

Readme

Build Status downloads npm Code Climate

Indeed

Boolean helpers for node.js

Why Bother?

Simple booleans are efficient and not that hard to use in javascript, so what's the value of a DDL for booleans? Mainly, I just find booleans one of those things that, if I think too hard about them, then I get lost. The project was born when I had

if (!oneThing && anotherThing) {
  // . . .
}

but then realized I actually needed the negative of anotherThing. So I changed it to

if (!oneThing && !anotherThing) {
  // . . .
}

at which point, I started wondering if that was the same as

if (!(oneThing && anotherThing)) {
  // . . .
}

And then I thought, what I really want is to say

if (neither(oneThing).nor(anotherThing)) {
  // do something
}

and thus the module "neither" was born. And then I later changed it to "indeed", which was more globally usable as a boolean helper. A bit of an anti-climatic ending, really. At any rate, this library is just a set of boolean helpers to put if statements into English-like syntax. Note that I used to be an English teacher and thus, that syntax is extremely opinionated and grammatically correct. Grammar is your friend.

Grammar Matters

Ahem . . . now on with the API.

Installation

npm install indeed --save

then

var helpers = require('indeed');

or

// Set up global methods.
// Mainly useful because I don't like having to say "if (helpers.indeed(a)..."
require('indeed')();

Usage

When you require('indeed'), you'll get an object with methods that begin boolean chains: indeed, either, neither, both, allOf, oneOf, noneOf, anyOf, nOf, and expect. These function are roughly the same - they allow you to assess conditions using natural language. These conditions are processed first in first out, so they can behave slightly differently than normal boolean logic. For example: if (a && b || c) becomes if (indeed(a).and(b).or(c)) as you might expect. However, if (indeed(a).or(b).and(c)) actually means if( (a || b) && c) because of how order of operations works. See Grouping below for more information.

Indeed

Begins a generic chain.

Chainable methods: and, andNot, or, orNot, butNot, and xor Chain limit: none

if (indeed(a).and(b).butNot(c).or(d).test())

Chaining is off by default with indeed, so that you can make simple comparisons:

// returns 'true'
indeed(a).is.true()

// returns 'indeed' so that you can assert more conditions
indeed.chain(a).is.true() // .and(b).is.false().test()

When chaining, use .test() or .val() to terminate the chain and evaluate the total result of the expression. Non-chaining is optimized for comparisons, so indeed(a).is.defined() will return true or false whereas indeed(a) by itself, will not. To assert on the definedness of a single thing, 1) chain: if (indeed.chain(a).test()), 2) use defined(): if (indeed(a).is.defined()), 3) use regular booleans: if (a). Calling one of the chain methods without calling a comparison will automatically turn on chaining, so that you can say if (indeed(a).and(b).and(c).test()) rather than if (indeed.chain(a).and(b).and(c).test()).

indeed is also equipped with some negation tools: not and Not. not simply negates the first condition:

if (indeed.not(a).and(b).test())

is equivalent to

if (!a && b)

Not negates the result of the chain, so

if (indeed.Not(a).and(b).test())

is equivalent to

if (!(a && b))

These too can be chained:

if (indeed.not.chain(a).and(b).test())
if (indeed.Not.chain(a).or(b).test())

Either

Begins a chain where one of two conditions (or both) should be true.

Chainable methods: or Chain limit: 1 Optimized for: existence checks

if (either(a).or(b))
if (either.chain(a).is.null().or(b).is.defined().test())

Neither

Begins a chain where both conditions should be false.

Chainable methods: nor Chain limit: 1 Optimized for: existence checks

if (neither(a).nor(b))
if (neither.chain(a).equals('foo').nor(b).is.a('date').test())

Both

Begins a chain where both conditions should be true.

Chainable methods: and Chain limit: 1 Optimized for: existence checks

if (both(a).and(b))
if (both.chain(a).is.false().and(b).contains('bar').test())

AllOf

Begins a chain where all conditions should be true. Incidentally, it only makes sense to use this with more than two conditions. With two conditions only, use both.

Chainable methods: and Chain limit: none Chaining is on by default and cannot be turned off since any number of and's can be used

if (allOf(a).and(b).and(c).test())

AnyOf

Begins a chain where at least one condition should be true.

Chainable methods: and Chain limit: none Chaining is on by default and cannot be turned off since any number of and's can be used

if (anyOf(a).and(b).and(c).test())

OneOf

Begins a chain where exactly one condition should be true.

Chainable methods: and Chain limit: none Chaining is on by default and cannot be turned off since any number of and's can be used

if (oneOf(a).and(b).and(c).test())

NoneOf

Begins a chain where all of the conditions should be false. With only two conditions, use neither instead.

Chainable methods: and Chain limit: none Chaining is on by default and cannot be turned off since any number of and's can be used

if (noneOf(a).and(b).and(c).test())

NOf

nOf is the only helper that deviates from the standard structure. It accepts a number, and then any number of conditions, of which exactly that number must be true.

Chainable methods: and Chain limit: none Chaining is on by default and cannot be turned off since any number of and's can be used

if (n(2).of(a).and(b).and(c).test())

Expect

Expect is identical to indeed and has all it's additional properties. It adds only a couple things: a throws comparison function (detailed below) for asserting that a function throws an exception, a with function for passing args to a function that should throw, and assert, which is the same as test but sounds more test-ish.

Grouping

You can create groups of chains, which are also evaluated left to right, using the properties And, But, Or, and Xor. They do what you would expect:

if (indeed(a).or(b).And.indeed(c).test())

if (indeed(a).and(b).Or.indeed(c).test())

if (indeed(a).and(b).Xor.indeed(c).test())

if (indeed(a).and(b).But.not.also(c).test())

The first example evaluates a || b first and then the result of that with && c. But is an alias to And because sometimes it feels more natural to say "but" than "and." indeed also has several aliases that can be used after joins depending on what you want to say next:

// just like indeed
if (indeed(a).or(b).And.also(c).test())

// also just like indeed
if (indeed(a).and(b).Or.else(c).test())

// just like indeed, but negated
if (indeed(a).and(b).But.not.also(c).test())

// just like indeed, but negates the entire next group
if (indeed(a).and(b).But.Not.also(c).or(d).test())

Additionally, all of the entry points are chainable after a Grouping property:

if (indeed(a).or(b).But.neither(c).nor(d).test())

Matching

Most of the examples so far have been simple checks for definedness (for simplicity), but indeed has a wide variety of comparison functions as well. All of the chain starters also have the chainable properties does, should, has, have, is, to, be, been, deep, deeply, not, Not, noCase, and caseless. In addition, indeed and expect have andDoes, andShould, andHas, andHave, andIs, andTo, and andBe which let you use multiple comparisons on the same object (chaining must be turned on). Most of these simply allow chaining with natural language. However, deep and deeply turn on deep object comparison when using equal (below), not and Not negate the comparison, caseless and noCase turn on case insensitivity (for equal, contains, key, keys, value, and values), and roughly makes order not matter when comparing arrays (if they have the same number of elements and all the same elements, they are considered equal).

Equals

Compares objects using _.isEqual when deep is applied and using === when it's not.

if (indeed(1).equals(1))
if (indeed({foo: { bar: 'baz'}}).deeply.equals({foo: { bar: 'baz'}})

Aliases: equal, eql

Matches

Checks whether a string matches the given regular expression. If given a string, it will convert it to a RegExp object.

if (indeed('foobar').matches(/oba/)) // or .matches('oba')

Aliases: match

A

For type comparisons. This is more strict that typeof however. It checks for constructor.name (allowing custom types) and, failing that, uses typeof. It is worth noting that both the type and comparison are lower cased, so 'string', 'String', 'strIng', etc. are all equivalent.

if (indeed('hello').is.a('string'))

An

Just like isA but preferable (for me anyway) for types beginning with vowels.

if (indeed([1, 2, 3]).is.an('array'))

Contains

Indicates if the string or array contains the given value.

if (indeed('foo bar').contains('foo'))
if (indeed([1,2,3]).contains(1))

Aliases: contain, indexOf

Key

Indicates if the object (or array, though somewhat by accident) contains the given key.

if (indeed({foo: 'bar'}).has.key('foo'))

Aliases: containsKey, containKey, property

Keys

Like .key but for multiple keys. Keys can be passed as an array or multiple strings.

if (indeed({foo: 'bar', baz: 'quux'}).has.keys('foo', 'baz')
if (indeed({foo: 'bar', baz: 'quux'}).has.keys(['foo', 'baz']))

Aliases: containKeys, keys, properties

Value

Indicates if the object (or array) contains the given value.

if (indeed({foo: 'bar'}).has.value('bar'))

Aliases: containsValue, containValue

Values

Like .value but for multiple values. Values can be passed as an array or multiple strings.

if (indeed({foo: 'bar', baz: 'quux'}).has.values('bar', 'quux'))
if (indeed({foo: 'bar', baz: 'quux'}).has.values(['bar', 'quux']))

Aliases: containsValues, containValues

Defined

Returns true for everthing except undefined. A little cleaner looking than if (typeof thing !== 'undefined'), though that's exactly what it does under the hood.

if (indeed('string').is.defined())
if (indeed(undefined).is.not.defined())

Null

Returns true for null and false for everything else.

if (indeed(null).is.null())
if (indeed('string').is.not.null())

True

Not to be confused with truthiness, this checks for the literal value true.

if (indeed(true).is.true())

False

Checks for the literal value false.

if (indeed(false).is.false())

Truthy

Checks for truthiness.

if (indeed(1).is.truthy())

Falsy

Checks for falsiness.

if (indeed(0).is.falsy())

GreaterThan

Compares two numbers

if (indeed(1).is.greaterThan(0))

Aliases: gt, above

LessThan

if (indeed(1).is.lessThan(2))

Aliases: lt, below

GreaterThanOrEqualTo

if (indeed(1).is.greaterThanOrEqualTo(1))

Aliases: gte

LessThanOrEqualTo

if (indeed(1).is.lessThanOrEqualTo(1))

Aliases: lte

Mixin

Additionally, indeed has a mixin method for extending these comparison methods. It takes an object of function names with corresponding functions.

indeed.mixin({
  can: function(condition) {
    return function(val) {
      return typeof val[condition] === 'function';
    }
  },
  beginsWith: function(condition) {
    return function(val) {
      return val.charAt(0).toLowerCase() === condition.toLowerCase();
    }
  }
});

The custom functions should be in this form, where condition is the thing to match against and val is the original object (which seems a little backwards, since val is "inside"). These methods, for example, would be called like this:

if (indeed({ foo: function() {} }).can('foo').test())
if (indeed('hello').beginsWith('h').test())

Tap

Executes a provided function, passing it this.

if (indeed(a).and(b).tap(console.log).or(c))

As an Assertion Library

I didn't write indeed to be an assertion library, but when I discovered that most assertion libraries don't return true/false values from their assertion methods, thereby making them unusable with mocha-given (which I use), I realized that indeed already had everything it needed (under the hood) to be that kind of assertion library. It just needed a few semantic changes to make it sound like an assertion library.

Expect

expect is an alias to indeed. It does all the same stuff but sounds more test-ish.

Given -> @thing = 'foo'
When -> @subject.doSomethingNeat(@thing)
Then -> expect(@thing).to.equal('foo bar')

Assert

assert is an alias to val and test. Once again, it's only purpose is to convey testing semantics. This only needs to be called when chaining multiple conditions.

Then -> expect.chain('foo').to.be.a('string').and.to.match(/o$/).and(['bar']).to.contain('bar').assert()

Throw

Returns true if the passed function throws an error. An optional string, regex, error, or function can be passed for more refined assertions:

expect(fn).to.throw();
expect(fn).to.throw('Inconceivable!');
expect(fn).to.throw(new Error('Mischief is afoot'));
expect(fn).to.throw(/timeout/);
expect(fn).to.throw(function(e) {
  return e.message.indexOf('!') > -1;
});

Aliases: throws

With

Assign parameters to pass to the invocation of a method to assert with throw:

expect(fn).with('foo', 'bar').to.throw('FOO BAR'); // Passes 'foo' and 'bar' to fn when it's called

Spies

expect also checks it's parameter to see if it's a sinon spy or stub, and if it is, it extends itself with the spy, letting you call sinon methods (which are sometimes awkward) in more natural English. For example:

var spy = sinon.spy()
// ... test things with the spy
expect(spy).to.have.been.called // Doesn't this feel more natural than 'spy.called'

When to use indeed for asserting

Indeed will work well with mocha-given or jasmine-given, but it isn't a full assertion library to be used in every project since it doesn't throw AssertionErrors or accept or generate messages (at least for now - perhaps in the next version I will get more abmitious). But because Then -> true is a passing test in Given style testing, it works well in that limited capacity.