to.is v0.1.0
to.is
What is this?
Data filtering, sanitization and validation.
This lib is built on top of the excellent validator.js lib.
Requirements
- Browsers: Modern browsers and IE 9+
- Node: >= 0.10.0
- validator.js >= 3.33.0
Installing (Browser)
$ bower install to.is --save
<script src="validator.min.js"></script>
<script src="to.is.min.js"></script>
<script>
to.lower().is.email("test@test.com");
</script>
NOTE: Not tested thoroughly in browsers currently, use at your own risk!
Installing (Node.js)
$ npm install to.is --save
var to = require('to.is').to,
is = require('to.is').is;
Usage
Overview
to.filter1(options).filter2(options).is.constraint1().constraint2().type
// -or-
is.constraint1().constraint2().type
Examples
var GmailAddress = to.trim().normalizeEmail().is.endsWith("@gmail.com").email;
if (GmailAddress("test@gmail.com")){
// it is true!
}
var results = GmailAddress.from(" test+extra@GMAIL.com ");
var normalizedEmail = results.data; // === "test@gmail.com"
var errors = results.errors;
Only when you use to
, trim()
is automatically performed at the start
and need not be specified. To disable, use trim(false)
.
var Profile = is.has({
email: to.normalizeEmail().is.maxLen(100).email,
username: to.lower().is.maxLen(100).str,
age: is.within(13, 99).int,
}).obj;
var results = Profile.from({
email: "tEsT@GMAIL.com",
username: " TEST ",
age: "99",
});
// === {email: "test@gmail.com", username: "test", age: 99}
var parsedProfile = results.data;
// Reusable
var results2 = Profile.from({
email: "tEsT@GMAIL.com",
age: 98.5,
});
There is no limit to nesting.
How It Works
to
and is
construct a chain of filters and constraints respectively.
The chain is executed from left to right like a pipeline: processing the input according to the filters, parsing the processed input according to the type, and finally checking the constraints. The output of a filter is passed to the next as input.
The philosophy is that parsing is forgiving and tries to convert the input to the target type as much as possible.
At the end of the is
chain, a type must be specified, after which a function
is returned. Calling it sparks off the chain and returns a boolean
indicating whether the raw input, after being processed by the filters and parsed, conforms to the constraints. You may also call from()
to parse the input and retrieve any errors at the same time.
The input is never modified. If the input is an object
, the parser makes a shallow clone first. Many of the types are immutable anyway, so there is no way to modify them.
undefined
denotes "no value" and will not be processed nor parsed.
The only way to process undefined
is to specify a default value.
For example,
to.def(0).is.int.from(undefined).data // === 0
// contrast with:
is.int.from(undefined).data // === undefined
Types
Not to be confused with JS builtin types.
str
Coerced to a string
.
The rules for coercion are the same as that of validator's, except that
undefined
remains as it is.
In summary,
null
,NaN
, functions and arrays are replaced with an empty string''
.toString
is applied if available.- concat with an empty string.
int
Parsed to a number
(int).
If the input is a string
, it is parsed using validator.toInt()
(with trimming).
Use is.radix()
to specify the radix (default is 10).
If the input is a number
, no parsing is performed.
All other types will fail parsing straightaway.
The resultant number must not be an NaN
or an infinity.
It will also be truncated. Example: -9.9 and 9.9 become -9
and 9 respectively.
float
Parsed to a number
.
If the input is a string
, it is parsed using validator.toFloat()
(with trimming).
If the input is a number
, no parsing is performed.
All other types will fail parsing straightaway.
The resultant number must not be an NaN
(although infinities are accepted).
bool
Parsed to a boolean
.
If the input is a string
,
only '0'
, 'false'
and ''
are parsed to false
.
Parsing is case-insensitive and ignores leading and trailing
whitespaces.
For other types, the parser will use the !
operator like this !!input
.
date
Parsed to a Date
.
The input is parsed using validator.toDate()
which in turn uses Date.parse()
.
If the input is a string
, although it can be in a wide variety of formats,
to be safe, you should only use the ISO 8601 format.
If the input is an object
, it must be a Date
object.
If the input is a number
, it is treated as the UNIX epoch time with millisecond
precision.
All other types will fail parsing straightaway.
obj
If the input is an object
, it must not be null
and not an Array
. The parser makes a shallow clone.
All other types will fail parsing straightaway.
array
Coming soon.
email, url, ip4, ip6, ip
The input must be a string and is checked using the corresponding validator's methods.
ip
accepts both IPv4 and IPv6.
url
accepts both relative and absolute URLs (but not paths).
Use is.absolute()
to accept only absolute ones (with protocols).
Filters
All filters can handle any input, undefined
, NaN
, null
and etc., you name it. But they only process the types they accept. For other types, they return the input untouched.
Name | Types Accepted | Description |
---|---|---|
def(defVal) | any | Specifies a default value. If the input is undefined or an empty string '' , returns defVal . Note that there is an implicit trim() right after to , so to.def(x) is really to.trim().def(x) , and all strings will be trimmed first. |
lower() ,upper() | string | Returns a lowercase/uppercase version of the input. |
trim(want) | string | Trims the input. want defaults to true . Note that this is implicit. Use trim(false) to disable. |
ltrim() ,rtrim() | string | Trims leading/trailing whitespaces. To use this, you have to disable trim() first. |
normalizeEmail() | string | Normalizes an email address using validator.normalizeEmail() . |
Do not specify the same filter more than once, if you do, only the
last one has effect. For example, to.trim(false).trim(true)
will still trim the
input.
Custom Filters
Coming soon.
Constraints
NOTE: Unlike filters, constraints assume a valid type is passed in,
so only use a constraint with a compatible type. For example, do not use absolute()
with an int
.
Name | Types Accepted | Description |
---|---|---|
lt(n) , lte(n) , gt(n) , gte(n) | int ,float ,str | Self-explanatory. |
radix(base) | int | Parses the input according to the base specified (default is 10). This is not really a constraint but a way to specify the base for validator.toInt() . |
within(min, max) | int ,float | Checks the input int is within the range (inclusive). |
contains(needle) | str | Checks the input contains the needle. |
startsWith(prefix) , endsWith(suffix) | str | Self-explanatory. |
matches(pattern, mods) | str | Same as validator.matches() . pattern can be an regex or a string. |
len(min, max) , len(exact) , minLen(n) , maxLen(n) , byteLen(exact) | str | Checks the input has the required length. |
upper() , lower() , hex() | str | Checks all chars are lowercase, uppercase or hex, respectively. |
inside(s1, s2, ...) | str ,int | Checks the input is strictly exactly one of specified. Example: inside('red', 'blue') |
before(date) , after(date) | date | Checks the input is strictly before/after the specified date which could be a Date or string . If date is not specified, defaults to now. |
within(min, max) | date | Checks the input date is within the specified range (inclusive). min and max are optional defaulting to now. |
has(fields) | obj | Parses the input object according to the fields specification. See example above. Extra fields not in the specs will be discarded. |
absolute(protocol, ...) | url | Checks the input URL starts with the specified protocols. Example: absolute('http', 'https') . If protocols are not specified, the defaults are 'http' , 'https' , and 'ftp' . |
You may use the str
constraints for string-based types such as url
and email
.
Like filters, do not specify the same constraint more than once, if you do, only the last one has effect.
Custom Constraints
Coming soon.
either
Usage:
// Required in node.js:
// var either = require('to.is').either;
var emailOrUsername = either(is.maxLen(100).email,
is.lower().maxLen(30).str);
var LoginData = is.has({
login: emailOrUsername,
password: is.len(6, 30).str,
}).obj;
// true
if (emailOrUsername("t@t.com"))
// true
if (emailOrUsername("john"))
var login = emailOrUsername.from("helloworld").data;
either
returns a function
as well that also has a from()
method.
It employs "short-circuit" logic.
Exact behavior
See the test cases in test/index.js
for the exact behavior and accepted formats.
Error Handling
Example
Consider:
var errors = is.lower().minLen(100).absolute("http", "https").url.from("ftp://t.com").errors
The errors
object will be
{
absolute: ["http", "https"],
minLen: [100],
}
Explanation: The input failed the absolute
and minLen
constraints, and
their arguments are put in the errors
object for easy diagnosis.
If there are no errors, errors
will be undefined
.
Invalid types
If the parser failed to parse the input, the errors object will be
{type: "typeName"}
Missing values
If the parser encountered an undefined
input, the errors will be
{missing: true}
A more complex example
var SignupData = is.has({
email: to.normalizeEmail().is.maxLen(100).email,
password: to.trim().is.len(6, 30).str,
name: to.trim().is.len(1, 30).str,
}).obj;
var result = SignupData.from({
password: ' 123 ',
name: [],
});
In this case, result.errors
will be nested like this:
{
has: {
email: {missing: true}, // the email field is missing and no default is specified
password: {len: [6, 30]}, // the password is too short
name: {type: "str"}, // name is an array, not a string.
},
}
result.data
will always be the raw, processed or parsed input, depending
on when the error arises in the pipeline.
In this case, it is:
{
password: '123',
name: [],
}
either
errors
Using emailOrUsername
from above,
var result = emailOrUsername.from("JOHN");
// result will be
{
data: ["JOHN", "JOHN"],
errors: [{type: "email"}, {lower: []}],
}
result = emailOrUsername.from("john@test.com");
// result will be
{
data: "john@test.com",
}
Testing
$ npm test
Browser
For now, use test/index.html
until automated testing is setup.
Contributing
Contributions are welcome but please conform to the coding style. Create test cases and ensure your code passes the tests.
License
Copyright 2015 Lucas Tan. The MIT License.
9 years ago