koriggans:k-check
Korrigans's own argument checking and general pattern matching package
and function K.check
. Mostly compatible with native check
package
(see limitations).
Offers better error reporting and extensibility than the native package.
In your Meteor app directory, enter:
$ meteor add koriggans:k-check
In your package:
api.use('korrigans:[email protected]');
Use the package through K.check
:
K.check(42, Number);
You can use almost all legacy check patterns.
These pass:
K.check('foo', String);
K.check(new Error('message'), Error);
K.check(20, Match.Where(value => value % 5 === 0));
K.check('exact value', 'exact value');
K.check(true, Match.OneOf(42, true));
K.check({ foo : 0 }, { foo : Number });
K.check([3, 6, 9], [Number]);
These fail:
// Error: [K.check] Expected number, got "foo".
K.check('foo', Number);
// Error: [K.check] Expected 42, got 0.
K.check(0, 42);
// Error: [K.check] Expected Match.OneOf(string, number, 42), got null.
K.check(null, Match.OneOf(String, Number, 42));
// Legacy error:
// Error: Match error: Failed Match.OneOf or Match.Optional validation
check(null, Match.OneOf(String, Number, 42));
// Error: [K.check] Expected { foo: string, bar: number, korrigans: function },
// got { foo: "foo", bar: 0, baz: true } (excess: baz; missing: korrigans).
K.check({
foo: 'foo',
bar: 0,
baz: true
}, {
foo: String,
bar: Number,
korrigans: Function
});
// Legacy error:
// Error: Match error: Unknown key in field baz
// Error: [K.check] Expected number,
// got "foo" at index 3 of [5, 10, 15, "foo"] against [number].
K.check([5, 10, 15, 'foo'], [Number]);
// Legacy error:
// Error: Match error: Expected number, got string in field [3]
You can also extend the possibilities of K.check
by
defining your own patterns and test functions.
To allow the use of custom functions K.check
provides a Symbol (basically a hidden property) to be placed
on objects: K.check.custom
.
K.check
expects a function to be set on this specific symbol.
To use custom tests use this Symbol on objects:
myObject[K.check.custom] = value => runTest(value);
// Or:
myObject[K.check.custom] = runTest;
You may then use your object as a pattern:
K.check('some value', myObject);
The custom function overrides all native behaviours of K.check
.
If the function throws an error, it is propagated.
Contrary to Match.Where
the return value is discarded. Returning a falsey
value (or not returning at all) will not fail the test.
Example use:
const percentagePattern = {
min: 0,
max: 100,
// Magic happens here:
[K.check.custom](value) {
if (!Number.isFinite(value)) {
throw new Error('value not finite number');
}
if (value < this.min) {
throw new Error('number too small');
}
if (value > this.max) {
throw new Error('number too big');
}
// Return value is discarded, no need to provide one
}
};
// These pass
K.check(42, percentagePattern);
K.check(0, percentagePattern);
K.check(Math.PI, percentagePattern);
// Error: value not finite number
K.check('foo', percentagePattern);
K.check(NaN, percentagePattern);
K.check(Infinity, percentagePattern);
// Error: number too small
K.check(-1, percentagePattern);
// Error: number too big
K.check(9001, percentagePattern);
Using an existing object:
const todayPattern = new Date();
todayPattern[K.check.custom] = function createdToday(value) {
if (!_.isObject(value) || !_.has(value, 'creationDate')) {
throw new Error('value is not a document with creationDate property');
}
if (value.creationDate.getDate() !== this.getDate()) {
throw new Error('document was not created today');
}
};
// This passes
const today = Date.now();
K.check({
creationDate: today
}, todayPattern);
// These fail
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
K.check({
creationDate: yesterday
}, todayPattern);
K.check('foo', todayPattern);
Some patterns can generate very lengthy error messages:
K.check({
foo: {
bar: {
baz: [3, 6, 9, 'dwarf']
}
}
}, {
foo: {
bar: {
baz: [Number]
}
}
});
// Error: [K.check] Expected number, got "dwarf"
// at index 3 of [3, 6, 9, "dwarf"] against [number] (K.ErrorLog.Check[0]).
The full path is not in the error message as it would get very long.
Instead only last path is reported.
Here, full path is stored at log indicated at end of error message:
K.ErrorLog.Check[0]
This log has following structure:
{
message: 'Expected number, got "dwarf" at index 3 of [3, 6, 9, "dwarf"] against [number]',
path : [
'key foo of { foo: { bar: { baz: [3, 6, 9, "dwarf"] } } } against { foo: { bar: { baz: [number] } } }',
'key bar of { bar: { baz: [3, 6, 9, "dwarf"] } } against { bar: { baz: [number] } }',
'key baz of { baz: [3, 6, 9, "dwarf"] } against { baz: [number] }'
]
}
This is a complete trace of each recursive call run by K.check
.
It provides exact error message in message
field, and each step of validation
in path
. path
is an array ordered from outer to inner of value and pattern.
Match.Optional
, Match.ObjectIncluding
and Match.ObjectWithValues
are not
supported.
K.check
uses duck typing to figure out which legacy patterns are passed.
For example, if a passed pattern has a condition
method then it is probably a
Match.Where
instance.
This is not possible with the three above legacy patterns. All three shadow
each other with the same pattern
field. Due to this limitation, they
are indistinguishable from the outside.
- meteor-k-pattern alias koriggans:k-pattern
- meteor-k-schema alias koriggans:k-schema
In your Meteor app directory, clone the package files in packages
folder:
$ git clone https://github.com/Korrigans/meteor-k-check ./packages/meteor-k-check
Then start the tests:
$ set VELOCITY_TEST_PACKAGES=1
$ meteor test-packages --driver-package velocity:html-reporter korrigans:k-check
- MrLowkos: @github - @atmosphere
- Aralun: @github - @atmosphere