README
railsish
A collection of functions that operate on principles found in Rails,
specifically with regard to figuring out if a given value is blank
or present
.
Example Usage
import { strict: assert } from "assert";
import { isPresent, isBlank } from "railsish";
// It treats empty or invalid values as blank...
assert(isBlank([]), "an empty array is blank");
assert(isBlank({}), "an empty object is blank");
assert(isBlank(new Date("2020-03-32")), "an invalid date is blank");
assert(isBlank(NaN), "NaN is blank");
assert(isBlank("\n\t "), "a string containing only whitespace is blank");
// But there's a special case: 0 is present!
assert(isPresent(0), "0 is present");
Accessor helpers
Related to the above concepts, railsish
includes some helper methods
that can be used to extract values from nested objects, perform some
checks, and do what you want them to.
Get any present value
import { strict: assert } from "assert";
import { getPresent } from "railsish";
assert.equal(getPresent({ foo: { bar: { baz: null } } }, "foo.bar.baz", "quux"), "quux", "it will return a default value");
Get a present number or string
import { strict: assert } from "assert";
import { getPresentNumber, getPresentString } from "railsish";
assert.equal(getPresentNumber({ foo: "-3.14" }, "foo"), -3.14, "fetching a numeric string will cast it");
assert.equal(getPresentString({ foo: "\t" }, "foo", "default"), "default", "whitespace-only strings are not present");
Converting values to booleans
Sometimes you want to work with boolean values directly, distinct from blankness and presence,
and so there’s also the booleanize
function. It works a little differently, in that it does
treat 0, and certain string values as falsey. It’s intended for use with API responses or
other inconsistent data containing booleanesque values that aren’t true
or false
.
import { booleanize } from "railsish";
booleanize(0) === false
booleanize("0") == false
// an empty object
booleanize({}) === false
// an empty array
booleanize([]) === false
booleanize(new Int8Array()) === false
// an empty string
booleanize("") === false
// a string that's only whitespace
booleanize(" ") === false
// special string handling: the following are case-insensitive
booleanize("F") === false
booleanize("n") === false
booleanize("no") === false
booleanize("nil") === false
booleanize("NULL") === false
booleanize("FALSE") === false
Get a boolean value
There’s a helper method for this one, too, that uses its logic instead:
import { strict: assert } from "assert";
import { getBoolean } from "railsish";
assert.fail(getBoolean({ foo: [] }, "foo"), "it will booleanize the value at a given path");
API Documentation
Table of Contents
- Core functionality
- isPresent
- isBlank
- booleanize
- Accessors
- getPresent
- getBoolean
- getFunction
- getPresentNumber
- getPresentString
- getArray
- getPresentArray
- Utility
- isPresentObject
- isBlankObject
- respondTo
- respondToPath
- Jest Matchers
- install
- matchers
- jestMatcherResult
Core functionality
isPresent
and isBlank
trace their inspiration to Rails and ActiveSupport’s Object#blank?
logic.
booleanize
is loosely inspired by how ActiveRecord
converts values from forms to booleans,
but has been expanded for handling other edge cases I’ve come across when developing.
isPresent
- See: Object#present? from Rails
Check to see if a given value is “present”, translated from Rails’ standards into JavaScript.
It is a boolean complement of {@linkcode module:railsish/presence.isBlank isBlank}. Simply put, if an object is not blank, it is considered present.
Parameters
value
any
Returns boolean
isBlank
- See: Object#blank? from Rails
Check to see if a given value is “blank”, translated from Rails’ standards into JavaScript.
Certain values are truthy in JS that are considered blank by this function, namely:
- A string containing only whitespace, e.g.
"\n\t \r\n"
- An empty array
- An empty plain object, e.g.
{}
- An empty
Map
orSet
- An invalid
Date
object, e.g.new Date("2020-03-32")
Additionally, 0
is considered false but is not considered blank,
as it is a finite number.
Parameters
value
any
Returns boolean
booleanize
Convert a provided value to a boolean with some handy logic.
This is used to handle cases where the value should be a
boolean, but might be coming from an API or some other representation
that is not explicitly true
/ false
Parameters
value
any something to convert
Examples
booleanize(0) === false
booleanize("0") == false
// an empty object
booleanize({}) === false
// an empty array
booleanize([]) === false
booleanize(new Int8Array()) === false
// an empty string
booleanize("") === false
// a string that's only whitespace
booleanize(" ") === false
// special string handling: the following are case-insensitive
booleanize("F") === false
booleanize("n") === false
booleanize("no") === false
booleanize("nil") === false
booleanize("NULL") === false
booleanize("FALSE") === false
Returns boolean the booleanized value
Accessors
Methods used to fetch various deeply-nested values based on presence or booleanization from an object.
Process an API response, elasticsearch document, JSON document, or anything else and remove the need for complex code like:
import { getPresentArray }
const response = {
foo: {
bar: {
baz: {
quux: []
}
}
}
};
// instead of this:
function extractDeepValue(value) {
if (value && value.foo) {
if (value.foo.bar) {
if (value.foo.bar.baz) {
if (Array.isArray(value.foo.bar.baz.quux) && value.foo.bar.baz.quux.length > 0) {
return value;
}
}
}
}
return ["default"];
};
extractDeepValue(response, ["default"]);
// you can just:
getPresentArray(response, "foo.bar.baz.quux", ["default"]);
Caveat!
While all of the get___
accessors accept a defaultValue
as their third parameter,
no type-checking or related validation is performed on it: it’s returned as is. This
is by design, so that you can handle unset values at a higher level.
import { getPresentArray } from "railsish";
const NO_TAGS_SELECTED = Symbol("NO_TAGS");
const getSelectedTags = (config) => getPresentArray(config, "user.selected.tags", NO_TAGS_SELECTED);
// A contrived express handler
export async function renderSelectedTags(req, res) {
const tags = getSelectedTags(req.user.config);
if (tags === NO_TAGS_SELECTED) {
// take the user to a
return res.json({
error: "User needs to select tags",
code: "SELECT_TAGS_FIRST"
}).status(400);
}
const tagResponse = await myDB.getTags(tags);
res.json(tagResponse);
}
getPresent
Get a property from object
at path
that
isPresent.
Parameters
Returns object?
getBoolean
- See: booleanize
Get a boolean value from object
at path
.
Parameters
Examples
// It'll booleanize a deeply-nested value
getBoolean({ foo: { bar: [] } }, "foo.bar") === false;
Returns boolean
getFunction
Parameters
Returns function?
getPresentNumber
Parameters
Returns number?
getPresentString
Retrieve a present string from an object
at path
.
Parameters
Examples
getPresentString({ foo: "\t" }, "foo", "default") === "default";
Returns string?
getArray
Extract an Array
-typed value from object
at path
.
Unlike getPresentArray, the array can be blank.
Parameters
Returns array?
getPresentArray
Extract an Array
-typed value from object
at path
.
If the value is blank, it will return defaultValue
instead.
Parameters
Returns array?
Utility
Some additional lower-level helper methods that get used elsewhere
isPresentObject
Check if a provided value
is a plain object with at least 1 key set.
Parameters
value
any
Examples
isPresentObject({}) === false
isPresentObject({ foo: "bar" }) === true
isPresentObject([]) === false
isPresentObject(new Foo()) === false
isPresentObject(Object.create(null)) === false
Returns boolean
isBlankObject
Test if a provided object is a blank, plain object.
Parameters
value
object
Examples
isBlankObject({}) === true
isBlankObject({ foo: "bar" }) === false
isBlankObject([]) === false
isBlankObject(new Foo()) === false
isBlankObject(Object.create(null)) === false
Returns boolean
respondTo
- See: Object#respond_to? from Ruby
A duck-typing method to check if a property on object
named name
is a function.
Parameters
object
any an objectname
string a function name
Returns boolean whether typeof object[name] === "function"
respondToPath
- See: Object#respond_to? from Ruby
A duck-typing method to check if a property on object
at the provided path
is a function.
Similar to respondTo
, but allows you to use an arbitrarily nested path.
Parameters
object
any an objectpath
(string | array<number, string>) a path to a possible function à lalodash.get
Returns boolean whether typeof get(object, path) === "function"
Jest Matchers
Some helpers for jest ship with this library that you can install into your test environment.
import { install as installRailsishMatchers } from "railsish/jest-matchers"
installRailsishMatchers(expect)
describe("some tests", () => {
it("returns a present response", () => {
expect(myLibrary.doSomething()).toBePresent();
});
it("returns something blank", () => {
expect(anonymousRequest.getCurrentUser()).toBeBlank();
});
it("implements foo", () => {
expect({ foo: () => "bar" }).toRespondTo("foo");
});
});
install
- See: expect.extend
Install the associated matchers
into your
jest environment to have presence, blankness, booleanization, and response helpers.
Parameters
expect
object jest’s globalexpect
methodexpect.extend
function the function that will accept the jest matchers
Examples
// in a file called by "setupFilesAfterEnv":
import { install } from "railsish/jest-matchers"
install(expect);
Returns void
matchers
- See: expect.extend
A collection of matchers for use with jest.
Type: object
toBePresent
Parameters
received
any something to check for presenceexpected
Examples
expect(0).toBePresent();
expect([]).not.toBePresent();
expect(false).not.toBePresent();
expect("some string").toBePresent();
expect({}).not.toBePresent();
expect({ foo: "bar" }).toBePresent();
expect
Returns jestMatcherResult
toBeBlank
Parameters
received
any something to check for blanknessexpected
Examples
expect(0).not.toBeBlank();
expect([]).toBeBlank();
expect({}).toBeBlank();
expect(NaN).toBeBlank();
expect(Infinity).not.toBeBlank();
expect("\t\n\t").toBeBlank();
Returns jestMatcherResult
toBooleanizeAs
- See: booleanize
Test a value to see how it booleanizes.
Parameters
received
any something tobooleanize
expected
boolean the value thatbooleanize
should evaluate to
Examples
expect("some text").toBooleanizeAs(true);
expect([]).toBooleanizeAs(false);
Returns jestMatcherResult
toBooleanizeTrue
- See: booleanize
A wrapper around matchers.toBooleanizeAs with expected
set to false
.
Parameters
received
any something tobooleanize
expected
Examples
expect("some text").toBooleanizeTrue();
expect([]).not.toBooleanizeTrue();
Returns jestMatcherResult
toBooleanizeFalse
- See: booleanize
A wrapper around matchers.toBooleanizeAs with expected
set to false
.
Parameters
received
any something tobooleanize
expected
Examples
expect("some text").not.toBooleanizeFalse();
expect([]).toBooleanizeFalse();
Returns jestMatcherResult
toRespondTo
- See: respondTo
Parameters
received
any something that might have a functionexpected
string a function name
Examples
expect(new Date()).toRespondTo("toISOString");
Returns jestMatcherResult
toRespondToPath
- See: respondToPath
Parameters
received
any something that might have a functionexpected
(string | Array<string>) a path to a function
Examples
expect({ foo: { bar: { baz: () => "quux" } } }).toRespondToPath("foo.bar.baz");
Returns jestMatcherResult
jestMatcherResult
An interface returned from a jest matcher.