Skip to content

Instantly share code, notes, and snippets.

@sivakumar-kailasam
Forked from taras/gist:89eda6beea2abcb60408
Last active August 29, 2015 14:10
Show Gist options
  • Save sivakumar-kailasam/46a149198ecaad34d31d to your computer and use it in GitHub Desktop.
Save sivakumar-kailasam/46a149198ecaad34d31d to your computer and use it in GitHub Desktop.
import Ember from 'ember';
var get = Ember.get;
var keys = Ember.keys;
var isNone = Ember.isNone;
/**
* Computed Property Macro returns computed property that evaluates to result of validation.
*
* It accepts rules hash which property name as keys and validation functions as values.
*
* ```javascript
* var controller = Ember.Controller.extend({
* name: null,
* date: null,
* validation: validation({
* name: function(value) {
* return validation.required(value);
* },
* date: function(date) {
* return validation.date(date);
* }
* })
* });
* ```
*
* Result of validation is an object with the following signature
*
* {
* <propertyName>: {
* isPassed: <boolean>,
* error: <string> // error that was returned as a result of failed validation
* value: <mixed> // value that was validated
* }
* ...
* }
*
* @param {Object} rules
* @returns {Ember.ComputedProperty}
*/
function validation(rules) {
// create array of dependent keys
var dependentKeys = keys(rules);
var isPassedKeys = keys(rules).map(function (propName) {
return '%@.%@'.fmt(propName, 'isPassed');
});
var ValidationResponse = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin, {
isPassed: Ember.computed.and.apply(this, isPassedKeys),
isFailed: Ember.computed.not('isPassed')
});
function buildValidationPromise() {
var _this = this;
var promises = {};
keys(rules).forEach(function (propName) {
var callback = rules[propName];
var value = get(_this, propName);
// coerce the return values into being a promise
promises[propName] = new Ember.RSVP.Promise(function (resolve) {
resolve(callback.call(_this, value));
});
});
function updateIsValidating(value) {
return function (result) {
_this.set('isValidating', value);
return result;
};
}
function transform(response) {
keys(response).forEach(function (propName) {
var result = response[propName];
// TODO: does need to read the return value to figure out if tests?
response[propName] = {
isPassed: get(result, 'state') === 'fulfilled',
error: get(result, 'state') === 'rejected' ? get(result, 'reason') : null,
value: get(_this, propName)
};
});
return response;
}
return ValidationResponse.create({
promise: Ember.RSVP.hashSettled(promises).then(transform).finally(updateIsValidating(false)),
target: this,
isValidating: true
});
}
return applyComputed(dependentKeys, buildValidationPromise);
}
function applyComputed(dependentKeys, callback) {
return Ember.computed.apply(this, [].concat(dependentKeys, [callback]));
}
var requiredRegex = /([^\s])/;
var urlRegex = /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
var emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
var dateISORegex = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/;
var numberRegex = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/;
var slugRegex = /^[0-9a-zA-Z-_]+$/;
var required = makeFormat(requiredRegex, "Must not be empty.");
var url = makeFormat(urlRegex, "Must be a url and start with http://");
var email = makeFormat(emailRegex, "Must be an email address");
var dateISO = makeFormat(dateISORegex, "Must be in date ISO makeFormat");
var number = makeFormat(numberRegex, "Must be a number");
var slug = makeFormat(slugRegex, "May contain only numbers, letters, dash or underscore.");
function date(value, message) {
if (isNone(message)) {
message = "Must be a valid date.";
}
var passed = !/Invalid|NaN/.test(new Date(value).toString());
if (passed) {
return value;
}
throw message;
}
export function makeFormat(regex, defaultMessage) {
return function(value, message) {
if (isNone(message)) {
message = defaultMessage;
}
if (!isNone(value) && regex.test(value)) {
return value;
}
throw message;
};
}
validation.required = required;
validation.url = url;
validation.email = email;
validation.dateISO = dateISO;
validation.number = number;
validation.slug = slug;
validation.date = date;
export default validation;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment