Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggested structure #35

Open
wafflepie opened this issue Sep 24, 2019 · 0 comments
Open

Suggested structure #35

wafflepie opened this issue Sep 24, 2019 · 0 comments

Comments

@wafflepie
Copy link
Collaborator

wafflepie commented Sep 24, 2019

Hi @tommmyy, in our internal project, I sketched a draft of what validarium could look like in terms of predicates, validations and integration with intl. I'm creating this issue so that the suggestion is written down somewhere and not just forgotten in the git history.

First of all, validate, validateMany and combineValidate are fine and I don't think any changes are necessary in this regard. However, what users of validarium often don't realise is that if you need to do validations using multiple fields, validate shouldn't/cannot be used.

Although the library should technically be agnostic (which should be possible). I think the APIs should suit the libraries that we use in our stack: redux-form and react-intl. There do not have to be any dependencies on these libraries I think.

Because we don't use the intl package in our internal project, this package should just be removed.

Predicates should always be factories, meaning that even a simple isRequired predicate needs to be "initialized":

validate({ name: [isRequired()] })

This allows validations (or validators, however we want to call this concept) to be created dynamically. Here's a sketch:

import { o, pick, keys, mapObjIndexed } from 'ramda';
import createValidation from './createValidation';
import * as predicates from './predicates';
import messages from './messages';

const validations = o(
	mapObjIndexed((predicate, key) => (options, message = messages[key]) =>
		createValidation(predicate, message, options)
	),
	pick(keys(predicates)) // NOTE: To only select enumerable properties.
)(predicates);

export default validations;

and

import { cond, T } from 'ramda';
import { alwaysNull } from 'ramda-extension';

const createValidation = (predicate, message, options) =>
	cond([
		// TODO: Add null safety?
		[predicate(options), alwaysNull],
		[T, value => ({ value, message, options })],
	]);

export default createValidation;

This allows the following usage:

import v from '@validarium/validations'

v.isRequired()
v.hasLengthMax({ max: 100 })
v.hasLengthMax({ max: 100 }, { id: 'sth', defaultMessage: 'Max length is {max}' })

Meaning that we can easily override validation messages and the creation process of validations is also simplified.

It is possible to further optimize this by creating the validations as a build script (allowing for code completion and better imports), but the predicate structure should stay consistent.

Validations either return null (if validation passed) or they return { value, message, options } object. With redux-form, this object should be stored as the field error (storing the translated value doesn't make much sense).

When visualising the error, this should be handled by a decorator/hook aware of both react-intl and redux-form. We could provide one in this library as a separate package, although a simple recipe would probably do.

Signatures:

Predicate
  (options?: Object) => (value: any) => bool

Validation
  (options?: Object, message?: Message) =>
  (value: any) =>
  ({ message: Message, options?: Object, value: any }) || null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant