Skip to content

Commit 7ed002a

Browse files
committed
Version 5.0.0 - breaking changes: when and unless conditions only apply to rules defined since last when/unless call, rename scalePrecision to precisionScale and interpret terms correctly
1 parent 185bc2e commit 7ed002a

17 files changed

Lines changed: 793 additions & 278 deletions

.idea/runConfigurations/All_Tests.xml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/api/configuration/unless.md

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ The `.unless` option is used to control when a rule or chain of rules should **n
77

88
By default, the `.unless` option will apply to all rules in the chain so far, but you can pass a second parameter to specify that it should only apply to the rule immediately preceding it.
99

10+
:::note
11+
12+
In the case that there are multiple `.when` and/or `.unless` conditions in the rule chain, each condition applies only to the rules defined **between it and the previous condition**.
13+
14+
:::
15+
1016
## Examples
1117

1218
### Apply to all rules in the chain so far
@@ -51,6 +57,59 @@ formValidator.validate({
5157
// ❌ { deliveryNote: 'Value cannot be null' }
5258
```
5359

60+
### Multiple calls within the same chain
61+
62+
In this example we apply multiple `.unless` conditions within the same rule chain.
63+
64+
In particular, we validate that the account balance is non-negative unless overdrafts are allowed, and also validate that the account balance is more than 100 unless the account is not subject to minimum balance requirements.
65+
66+
```typescript
67+
import { Validator } from 'fluentvalidation-ts';
68+
69+
type FormModel = {
70+
accountBalance: number;
71+
allowOverdrafts: boolean;
72+
subjectToMinimumBalance: boolean;
73+
};
74+
75+
class FormValidator extends Validator<FormModel> {
76+
constructor() {
77+
super();
78+
79+
this.ruleFor('accountBalance')
80+
.greaterThanOrEqualTo(0)
81+
// highlight-next-line
82+
.unless((formModel) => formModel.allowOverdrafts)
83+
.greaterThanOrEqualTo(100)
84+
// highlight-next-line
85+
.unless((formModel) => !formModel.subjectToMinimumBalance);
86+
}
87+
}
88+
89+
const formValidator = new FormValidator();
90+
91+
formValidator.validate({
92+
accountBalance: -50,
93+
allowOverdrafts: true,
94+
subjectToMinimumBalance: false,
95+
});
96+
// ✔ {}
97+
98+
formValidator.validate({
99+
accountBalance: -50,
100+
allowOverdrafts: false,
101+
subjectToMinimumBalance: false,
102+
});
103+
// ❌ { accountBalance: 'Value must be greater than or equal to 0' }
104+
105+
formValidator.validate({
106+
accountBalance: 50,
107+
allowOverdrafts: false,
108+
subjectToMinimumBalance: true,
109+
});
110+
// ❌ { accountBalance: 'Value must be greater than or equal to 100' }
111+
```
112+
54113
### Apply to a specific rule in the chain
55114

56115
In this example we apply an `.unless` condition to a specific rule in the chain.
@@ -73,10 +132,7 @@ class FormValidator extends Validator<FormModel> {
73132
.notNull()
74133
.greaterThanOrEqualTo(18)
75134
// highlight-start
76-
.unless(
77-
(formModel) => formModel.alcoholicDrink == null,
78-
'AppliesToCurrentValidator'
79-
);
135+
.unless((formModel) => formModel.alcoholicDrink == null, 'AppliesToCurrentValidator');
80136
// highlight-end
81137
}
82138
}
@@ -122,8 +178,10 @@ Matches the type of the base model.
122178

123179
### `appliesTo`
124180

125-
This is an optional parameter which can be used to control whether the condition applies to all rules in the chain so far, or just the rule immediately preceding the call to `.unless`.
181+
This is an optional parameter which can be used to control which rules in the current rule chain the condition applies to.
182+
183+
A value of `'AppliesToAllValidators'` means that the `.unless` condition applies to all rules in the current rule chain so far. If there are other calls to `.when` or `.unless` in the chain, only the rules defined since the most recent condition will have the condition applied to them.
126184

127-
By default, this parameter is set to `'AppliesToAllValidators'`, which means that the `.unless` condition applies to all rules in the current chain.
185+
A value of `'AppliesToCurrentValidator'` specifies that the `.unless` condition only controls the execution of the rule immediately preceding it in the current rule chain.
128186

129-
Setting this value to `'AppliesToCurrentValidator'` specifies that the `.unless` condition only controls the execution of the rule immediately preceding it in the current chain.
187+
By default, the `appliesTo` parameter is set to `'AppliesToAllValidators'`.

docs/api/configuration/when.md

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ The `.when` option is used to control when a rule or chain of rules should execu
77

88
By default, the `.when` option will apply to all rules in the chain so far, but you can pass a second parameter to specify that it should only apply to the rule immediately preceding it.
99

10+
:::note
11+
12+
In the case that there are multiple `.when` and/or `.unless` conditions in the rule chain, each condition applies only to the rules defined **between it and the previous condition**.
13+
14+
:::
15+
1016
## Examples
1117

1218
### Apply to all rules in the chain so far
@@ -51,6 +57,51 @@ formValidator.validate({
5157
// ❌ { deliveryNote: 'Value cannot be null' }
5258
```
5359

60+
### Multiple calls within the same chain
61+
62+
In this example we apply multiple `.when` conditions within the same rule chain.
63+
64+
In particular, we validate that Sunday delivery rates are only applied when the delivery day is a Sunday.
65+
66+
```typescript
67+
import { Validator } from 'fluentvalidation-ts';
68+
69+
type FormModel = {
70+
deliveryDay: string;
71+
deliveryRate: number;
72+
};
73+
74+
class FormValidator extends Validator<FormModel> {
75+
constructor() {
76+
super();
77+
78+
this.ruleFor('deliveryRate')
79+
.equal(4.99)
80+
.withMessage('Sunday rates must apply if delivery day is Sunday')
81+
// highlight-next-line
82+
.when((formModel) => formModel.deliveryDay === 'Sunday')
83+
.equal(2.99)
84+
.withMessage('Standard rates must apply if delivery day is Monday to Saturday')
85+
// highlight-next-line
86+
.when((formModel) => formModel.deliveryDay !== 'Sunday');
87+
}
88+
}
89+
90+
const formValidator = new FormValidator();
91+
92+
formValidator.validate({ deliveryDay: 'Sunday', deliveryRate: 4.99 });
93+
// ✔ {}
94+
95+
formValidator.validate({ deliveryDay: 'Sunday', deliveryRate: 2.99 });
96+
// ❌ { deliveryRate: 'Sunday rates must apply if delivery day is Sunday' }
97+
98+
formValidator.validate({ deliveryDay: 'Monday', deliveryRate: 2.99 });
99+
// ✔ {}
100+
101+
formValidator.validate({ deliveryDay: 'Monday', deliveryRate: 4.99 });
102+
// ❌ { deliveryRate: 'Standard rates must apply if delivery day is Monday to Saturday' }
103+
```
104+
54105
### Apply to a specific rule in the chain
55106

56107
In this example we apply a `.when` condition to a specific rule in the chain.
@@ -73,10 +124,7 @@ class FormValidator extends Validator<FormModel> {
73124
.notNull()
74125
.greaterThanOrEqualTo(18)
75126
// highlight-start
76-
.when(
77-
(formModel) => formModel.alcoholicDrink != null,
78-
'AppliesToCurrentValidator'
79-
);
127+
.when((formModel) => formModel.alcoholicDrink != null, 'AppliesToCurrentValidator');
80128
// highlight-end
81129
}
82130
}
@@ -122,8 +170,10 @@ Matches the type of the base model.
122170

123171
### `appliesTo`
124172

125-
This is an optional parameter which can be used to control whether the condition applies to all rules in the chain so far, or just the rule immediately preceding the call to `.when`.
173+
This is an optional parameter which can be used to control which rules in the current rule chain the condition applies to.
174+
175+
A value of `'AppliesToAllValidators'` means that the `.when` condition applies to all rules in the current rule chain so far. If there are other calls to `.when` or `.unless` in the chain, only the rules defined since the most recent condition will have the condition applied to them.
126176

127-
By default, this parameter is set to `'AppliesToAllValidators'`, which means that the `.when` condition applies to all rules in the current chain.
177+
A value of `'AppliesToCurrentValidator'` specifies that the `.when` condition only controls the execution of the rule immediately preceding it in the current rule chain.
128178

129-
Setting this value to `'AppliesToCurrentValidator'` specifies that the `.when` condition only controls the execution of the rule immediately preceding it in the current chain.
179+
By default, the `appliesTo` parameter is set to `'AppliesToAllValidators'`.

docs/api/rules/precisionScale.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
id: precisionScale
3+
title: '.precisionScale'
4+
---
5+
6+
The `.precisionScale` rule is used to ensure that the value of a given `number` property is permissible for the specified **precision** and **scale**.
7+
8+
These terms are defined as follows:
9+
10+
- **Precision** is the number of digits in a number.
11+
- **Scale** is the number of digits to the right of the decimal point in a number.
12+
13+
:::warning
14+
15+
Prior to `v5.0.0` the `.precisionScale` rule was called `.scalePrecision` and the parameter naming was incorrect!
16+
17+
:::
18+
19+
## Example
20+
21+
```typescript
22+
import { Validator } from 'fluentvalidation-ts';
23+
24+
type FormModel = {
25+
price: number;
26+
};
27+
28+
class FormValidator extends Validator<FormModel> {
29+
constructor() {
30+
super();
31+
32+
this.ruleFor('price').precisionScale(4, 2);
33+
}
34+
}
35+
36+
const formValidator = new FormValidator();
37+
38+
formValidator.validate({ price: 10.01 });
39+
// ✔ {}
40+
41+
formValidator.validate({ price: 0.001 }); // Too many digits after the decimal point
42+
// ❌ { price: 'Value must be no more than 4 digits in total, with allowance for 2 decimals' }
43+
44+
formValidator.validate({ price: 100.1 }); // Too many digits (when accounting for reserved digits after the decimal point)
45+
// ❌ { price: 'Value must be no more than 4 digits in total, with allowance for 2 decimals' }
46+
```
47+
48+
## Reference
49+
50+
### `.precisionScale(precision: number, scale: number)`
51+
52+
A number validation rule which takes in an allowed precision and scale, and ensures that the value of the given property is permissible.
53+
54+
:::danger
55+
56+
Due to rounding issues with floating point numbers in JavaScript, this rule may not function as expected for large precisions/scales.
57+
58+
:::
59+
60+
### `precision`
61+
62+
This is the total number of digits that the value may have (taking into account the number of digits "reserved" for after the decimal point).
63+
64+
The maximum number of significant digits allowed before the decimal point (i.e. the integer part) can be calculated as `(precision - scale)`.
65+
66+
### `scale`
67+
68+
This is the maximum number of digits after the decimal point that the value may have.
69+
70+
:::note
71+
72+
When `precision` and `scale` are equal, the "leading zero" to the left of the decimal point is **not** counted as a digit (e.g. a value of `0.01` would be viewed as `.01`).
73+
74+
:::
75+
76+
## Example Message
77+
78+
> Value must not be more than `[precision]` digits in total, with allowance for `[scale]` decimals

docs/api/rules/scalePrecision.md

Lines changed: 0 additions & 80 deletions
This file was deleted.

0 commit comments

Comments
 (0)