Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "handcuffs", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Validation library", | ||
"scripts": { | ||
"example": "nodemon --watch example --watch src --exec babel-node example", | ||
"test": "jest" | ||
"test": "jest --coverage && codecov", | ||
"test:coverage": "jest --coverage" | ||
}, | ||
@@ -19,2 +20,3 @@ "main": "src/index.js", | ||
"babel-preset-env": "^1.6.0", | ||
"codecov": "^2.3.0", | ||
"eslint": "^4.4.1", | ||
@@ -21,0 +23,0 @@ "eslint-config-airbnb": "^15.1.0", |
# Handcuffs | ||
[![Build Status](https://travis-ci.org/VladShcherbin/handcuffs.svg?branch=master)](https://travis-ci.org/VladShcherbin/handcuffs) | ||
[![Build Status](https://travis-ci.org/handcuffs/handcuffs.svg?branch=master)](https://travis-ci.org/handcuffs/handcuffs) | ||
[![Test Coverage](https://img.shields.io/codecov/c/github/handcuffs/handcuffs/master.svg)](https://codecov.io/gh/handcuffs/handcuffs) | ||
@@ -47,4 +48,12 @@ Validation library | ||
| accepted | `{ rules: 'accepted' }` | The field under validation must be `'yes'`, `'on'`, `1`, or `true`. | | ||
| alpha | `{ name: 'alpha' }` | The field under validation must be entirely alphabetic characters. | | ||
| alphaDash | `{ slug: 'alphaDash' }` | The field under validation may have alpha-numeric characters, as well as dashes and underscores. | | ||
| alphaNum | `{ password: 'alphaNum' }` | The field under validation must be entirely alpha-numeric characters. | | ||
| array | `{ permissions: 'array' }` | The field under validation must be an array. | | ||
| between | `{ guests: 'between:1,3' }` | The field under validation must have a size between the given *min* and *max* values. | | ||
| boolean | `{ isVisible: 'boolean' }` | The field under validation must be a boolean value `true` or `false`. | | ||
| required | `{ name: 'required' }` | The field under validation must be present and not empty. | | ||
| max | `{ password: 'max:10' }` | The field under validation must be less than or equal to the given *max* value. | | ||
| min | `{ password: 'min:6' }` | The field under validation must have the given *min* value. | | ||
| numeric | `{ age: 'numeric' }` | The field under validation must be numeric. | | ||
| required | `{ name: 'required' }` | The field under validation must be present and not empty. | | ||
| string | `{ name: 'string' }` | The field under validation must be a string. | |
import messages from './messages/en' | ||
import { hasRules, hasNumericRules } from './rules' | ||
export default function formatErrorMessage(errorTitle) { | ||
return messages[errorTitle] || errorTitle | ||
// Replacers for message placeholders | ||
const replacers = { | ||
between: (message, params) => message.replace(':min', params[0]).replace(':max', params[1]), | ||
max: (message, params) => message.replace(':max', params[0]), | ||
min: (message, params) => message.replace(':min', params[0]) | ||
} | ||
// Get field type from field rules | ||
function getFieldType(rules) { | ||
if (hasNumericRules(rules)) { | ||
return 'numeric' | ||
} | ||
if (hasRules(rules, ['array'])) { | ||
return 'array' | ||
} | ||
return 'string' | ||
} | ||
// Replace message placeholders | ||
function replacePlaceholders(message, rule, params) { | ||
return replacers[rule] | ||
? replacers[rule](message, params) | ||
: message | ||
} | ||
// Return error message | ||
export default function formatErrorMessage(ruleTitle, ruleParams, fieldRules) { | ||
const definedMessage = messages[ruleTitle] | ||
if (definedMessage) { | ||
if (typeof definedMessage === 'object') { | ||
const fieldType = getFieldType(fieldRules) | ||
return replacePlaceholders(definedMessage[fieldType], ruleTitle, ruleParams) | ||
} | ||
return replacePlaceholders(definedMessage, ruleTitle, ruleParams) | ||
} | ||
return ruleTitle | ||
} |
export default { | ||
accepted: 'This field must be accepted', | ||
alpha: 'This field may only contain letters', | ||
alphaDash: 'This field may only contain letters, numbers, and dashes', | ||
alphaNum: 'This field may only contain letters and numbers', | ||
array: 'This field must be an array', | ||
between: { | ||
array: 'This field must have between :min and :max items', | ||
numeric: 'This field must be between :min and :max', | ||
string: 'This field must be between :min and :max characters' | ||
}, | ||
boolean: 'This field must be true or false', | ||
required: 'This field is required' | ||
max: { | ||
array: 'This field may not have more than :max items', | ||
numeric: 'This field may not be greater than :max', | ||
string: 'This field may not be greater than :max characters' | ||
}, | ||
min: { | ||
array: 'This field must have at least :min items', | ||
numeric: 'This field must be at least :min', | ||
string: 'This field must be at least :min characters' | ||
}, | ||
numeric: 'This field must be a number', | ||
required: 'This field is required', | ||
string: 'This filed must be a string' | ||
} |
import accepted from './rules/accepted' | ||
import alpha from './rules/alpha' | ||
import alphaDash from './rules/alphaDash' | ||
import alphaNum from './rules/alphaNum' | ||
import array from './rules/array' | ||
import between from './rules/between' | ||
import boolean from './rules/boolean' | ||
import max from './rules/max' | ||
import min from './rules/min' | ||
import numeric from './rules/numeric' | ||
import required from './rules/required' | ||
import string from './rules/string' | ||
@@ -9,7 +17,17 @@ // Predefined rules | ||
accepted, | ||
alpha, | ||
alphaDash, | ||
alphaNum, | ||
array, | ||
between, | ||
boolean, | ||
required | ||
max, | ||
min, | ||
numeric, | ||
required, | ||
string | ||
} | ||
const numericRules = ['numeric'] | ||
// Add a new rule | ||
@@ -33,2 +51,27 @@ export function addRule(ruleTitle, ruleFunction) { | ||
// Check if rules array has any of provided rules | ||
export function hasRules(rulesArray, rulesToFind) { | ||
const ruleTitlesArray = rulesArray.map(rule => rule.title) | ||
if (Array.isArray(rulesToFind)) { | ||
return rulesToFind.reduce((result, currentRuleToFind) => ( | ||
result || ruleTitlesArray.includes(currentRuleToFind) | ||
), false) | ||
} | ||
return ruleTitlesArray.includes(rulesToFind) | ||
} | ||
// Check if rules array has any of numeric rules | ||
export function hasNumericRules(rulesArray) { | ||
return hasRules(rulesArray, numericRules) | ||
} | ||
// Check required params for a rule | ||
export function requireParamsCount(count, params, rule) { | ||
if (!Array.isArray(params) || params.length < count) { | ||
throw new Error(`Validation rule "${rule}" requires at least ${count} parameters`) | ||
} | ||
} | ||
// Split rule title and params, located after ':', divided by ',' | ||
@@ -57,1 +100,16 @@ function extractRuleParams(rule) { | ||
} | ||
// Get the size of the value, depending on the value type and rules | ||
export function getSize(value, rules) { | ||
const hasNumericRule = hasNumericRules(rules) | ||
if (numeric(value) && hasNumericRule) { | ||
return value | ||
} | ||
if (Array.isArray(value)) { | ||
return value.length | ||
} | ||
return String(value).length | ||
} |
@@ -7,6 +7,7 @@ import { formatRules, getRule } from './rules' | ||
return rules.reduce((existingErrors, rule) => { | ||
const isValid = getRule(rule.title)(value) | ||
const { title, params } = rule | ||
const isValid = getRule(title)(value, params, rules) | ||
return !isValid | ||
? [...existingErrors, formatErrorMessage(rule.title, rule.params)] | ||
? [...existingErrors, formatErrorMessage(title, params, rules)] | ||
: existingErrors | ||
@@ -16,3 +17,3 @@ }, []) | ||
// Validate fields against rules, return field with array of errors if found | ||
// Validate fields against rules, return fields with an array of errors if found | ||
function validateFields(fields, rules) { | ||
@@ -29,3 +30,3 @@ return Object.keys(fields).reduce((fieldsWithErrors, currentField) => { | ||
// Proceed the first part of field rules, return object with fields and their values or errors | ||
// Proceed the first part of field rules, return an object with fields and their values or errors | ||
function proceedRuleFirstPart(partTitle, partRules, data, isLastPart) { | ||
@@ -52,3 +53,3 @@ if (partTitle === '*') { | ||
// Proceed the rest part of field rules, return object with fields and their values or errors | ||
// Proceed the rest part of field rules, return an object with fields and their values or errors | ||
function proceedRuleRestPart(part, fields, rules, isLastPart) { | ||
@@ -90,3 +91,3 @@ const fieldsArray = Object.keys(fields) | ||
// Split field by '.', return object with fields and array of validation errors | ||
// Split field by '.', return an object with fields and array of validation errors | ||
function validateField(field, rules, data) { | ||
@@ -104,3 +105,3 @@ return field.split('.').reduce((fields, currentRulePart, partIndex, partArray) => { | ||
// Validate data with rules, return object with fields and their errors | ||
// Validate data with rules, return an object with fields and their errors | ||
export default function validate(data, rules) { | ||
@@ -107,0 +108,0 @@ const parsedRules = formatRules(rules) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
13635
19
305
58
11