Skip to content

Commit

Permalink
add(n4s): enforce.condition
Browse files Browse the repository at this point in the history
  • Loading branch information
ealush committed Nov 10, 2021
1 parent 5d5540a commit 149aab3
Show file tree
Hide file tree
Showing 28 changed files with 141 additions and 309 deletions.
1 change: 0 additions & 1 deletion packages/n4s/docs/_sidebar.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
- [Main](./)
- [List of Enforce Rules](./rules)
- [Schema validations](./compound)
- [Templates and Composites](./template)
- [Custom Enforce Rules](./custom)
- [Consuming external rules](./external)
53 changes: 0 additions & 53 deletions packages/n4s/docs/compose.md

This file was deleted.

12 changes: 4 additions & 8 deletions packages/n4s/docs/compound.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,28 @@ enforce(value).anyOf(enforce.isString(), enforce.isArray()).isNotEmpty();

## enforce.allOf() - all/and validations :id=allof

`allOf` lets us validate that a value passes _all_ of the supplied rules or composites.
`allOf` lets us validate that a value passes _all_ of the supplied rules.

enforce(value).allOf(
enforce.isArray(),
enforce.longerThan(2)
);

This can be even more useful when combined with shapes and composites:
This can be even more useful when combined with shapes:

```js
const User = enforce.composite(
enforce.loose({
const User = enforce.loose({
id: enforce.isNumber()
name: enforce.shape({
first: enforce.isString(),
last: enforce.isString(),
middle: enforce.optional(enforce.isString()),
}),
})
);

const DisabledAccount = enforce.composite(
enforce.loose({
const DisabledAccount = enforce.loose({
disabled: enforce.equals(true)
})
)

enforce(value).allOf(
User,
Expand Down
26 changes: 26 additions & 0 deletions packages/n4s/docs/custom.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Creating Custom Rules

By default enforce comes with a list of rules that are available to be used. They intentionally do not cover all the cases that can be encountered in a real-world application but instead focus on the most common use cases.

## Inline logic with `condition`

Sometimes you would need to add some custom logic to your validation. For that you can use `enforce.condition` which accepts a function.

Your provided function will receive the enforced value, and returns either a boolean or a rule-return object.

```js
// Passes if the value is `1`
enforce(1).condition(value => {
return value === 1;
});
```

```js
enforce(2).condition(value => {
return {
pass: value === 1,
message: 'value must be one',
};
});
```

## Reusable custom rules with enforce.extend

To make it easier to reuse logic across your application, sometimes you would want to encapsulate bits of logic in rules that you can use later on, for example, "what's considered a valid email".

Rules are called with the argument passed to enforce(x) followed by the arguments passed to `.yourRule(y, z)`.
Expand Down
70 changes: 70 additions & 0 deletions packages/n4s/src/rules/__tests__/ruleCondition.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import enforce from 'n4s';

import ruleReturn, { failing, passing } from 'ruleReturn';

describe('enforce.condition', () => {
it('Should pass down enforced value to condition as the first argument', () => {
const condition = jest.fn(() => true);

enforce(1).condition(condition);
expect(condition).toHaveBeenCalledWith(1);

enforce.condition(condition).run(2);
expect(condition).toHaveBeenCalledWith(2);
});

describe('Lazy interface', () => {
it('Should return a failing result if condition is failing', () => {
expect(enforce.condition(() => false).run(1)).toEqual(failing());
expect(enforce.condition(() => failing()).run(1)).toEqual(failing());
expect(
enforce.condition(() => ruleReturn(false, 'failure message')).run(1)
).toEqual(ruleReturn(false, 'failure message'));
});

it('Should return a passing result if condition is passing', () => {
expect(enforce.condition(() => true).run(1)).toEqual(passing());
expect(enforce.condition(() => passing()).run(1)).toEqual(passing());
expect(
enforce.condition(() => ruleReturn(true, 'success message')).run(1)
).toEqual(passing());
});
});

describe('Eager interface', () => {
it('Should throw an error if condition is failing', () => {
expect(() => enforce(1).condition(() => false)).toThrow();

expect(() => enforce(1).condition(() => failing())).toThrow();

expect(() =>
enforce(1).condition(() => ruleReturn(false, 'failure message'))
).toThrow();
});

it('Should return silently if condition is passing', () => {
expect(() => enforce(1).condition(() => true)).not.toThrow();

expect(() => enforce(1).condition(() => passing())).not.toThrow();

expect(() =>
enforce(1).condition(() => ruleReturn(true, 'success message'))
).not.toThrow();
});
});

describe('Error handling', () => {
it('Should fail if not a function', () => {
expect(() => enforce().condition('not a function')).toThrow();
expect(enforce.condition('not a function').run(1)).toEqual(failing());
});

it('Should throw an error if condition returns a non-boolean or a non-ruleReturn', () => {
expect(() => enforce(1).condition(() => 1)).toThrow();
expect(() => enforce(1).condition(() => undefined)).toThrow();
expect(() => enforce(1).condition(() => 'not a boolean')).toThrow();
expect(() => enforce(1).condition(() => ruleReturn())).toThrow();
expect(() => enforce.condition(() => 1).run(1)).toThrow();
});
});
});
12 changes: 12 additions & 0 deletions packages/n4s/src/rules/ruleCondition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { TRuleReturn } from 'ruleReturn';

export default function condition(
value: any,
callback: (value: any) => TRuleReturn
): TRuleReturn {
try {
return callback(value);
} catch {
return false;
}
}
132 changes: 0 additions & 132 deletions packages/n4s/src/runtime/__tests__/compose.test.ts

This file was deleted.

Loading

0 comments on commit 149aab3

Please sign in to comment.