开发者

JavaScript Code Contract Libraries?

I am just starting up a new web application and I want to implement some form of contract'esque style validation in my JavaScript. I did some quick googling, and came across JsContact but the syntax isn't quite what I had in mind. Is anyone aware of other libraries?

I am thinking I want the syntax to be something like

String.prototype.padLeft = function(c, width) {
  Verify.value(c).isRequired().isNotNull().isChar();
  Verify.value(width).isRequired().isNotNull().isNumber().greaterThan(0);

  ...

  Verify.value(result).isNotNull();

  return result;
};

Although it won't take long to put together my own library that has the syntax/methods I want, if someone else has already done the work and it is close enough, it will save me some time. Thanks in advance.

UPDATE

I won't have time to work on this until this afternoon, so I will give it a few more hours to see if anyone has any recommendations. If not, I will post whatever I create up somewhere as an answer below for other people to reference if they desire.

I have also given a little more thought to the API that would make sense, and I am currently thinking something like (contrived examples):

 function searchUser(firstName, middleInit, lastName) {
   Verify.value(firstName).isString().matching(/\w+/);       // Must have value
   Verify.value(middleInit).whenNotNull().isChar();          // May be null, but not undefined
   Verify.value(lastName).isString().withMinimumLengthOf(2); // Must have value

   ...
 }

 function syncTime(serverTime, now) {
   Verify.value(serverTime).isDate();         // Must have value.
   Verify.value(now).whenDefined().isDate();  // May be undefined, but not null.

 }

My current thought is that tolerating NULL or UNDEFINED values is atypical (at least for me?), as such, rather than explicitly specifying that a value .is开发者_运维百科NotNull() you would actually disable the rule for .whenDefined() or .whenNotNull() as shown above. I may make .whenNotNull() not error on UNDEFINED, but I see NULL vs. UNDEFINED as an important distinction; we'll see... all other methods will be pretty typical... thoughts? comments?


Given that no one has recommended any existing libraries, or that I am crazy for thinking this is a good idea I went ahead and threw together a basic library. The code isn't fancy, but it does what I want, and it is reasonably fast to run (approx 40 chained checks per ms in IE).

I settled on a final syntax like:

function syncTime(serverTime, now) {
  Verify.value(serverTime).always().isDate();   // Cannot be undefined or null.
  Verify.value(now).whenDefined().isDate();     // Cannot be null, but must be date when defined.

  //Code
}

function searchForUser(firstName, middleInit, lastName) {
  Verify.value(firstName).always().isString().withMinimumLengthOf(2);  // Cannot be undefined or null.
  Verify.value(lastName).always().isString().withMinimumLengthOf(2);   // Cannot be undefined or null.
  Verify.value(middleInit).whenNotNull().isChar().between('A', 'Z');   // Cannot be undefined, but must be single char string when not null.

  //Code
}

I opted for an explicit 'Must Have Value' via the .always() check, personally I found it nicer to read; but I could see some going another way.

Given that the source is more than I want to post in this answer, please head to this CodePlex Wiki Page if you are interested in the source. I guess it turned in to more of a fluent assertion library; but it does what I need.

Update

I updated the source on the linked CodePlex page above. Specifically, I restructed the Verify class to make use of a 'value context' rather than always building new Verifier objects; improved IE's performance greatly (never was an issue with FireFox or Chrome)... now handles about 100 chained checks per ms in IE.


I may suggest you the next code contracts library: dbc-code-contracts.


NPM: https://www.npmjs.com/package/dbc-code-contracts

GitLab repo (home): https://gitlab.com/o.oleg/orbios.dbc#README

CI-builds with the unit-tests: https://gitlab.com/o.oleg/orbios.dbc/-/jobs/


Sample code:

Dbc.Contract.throwException = true;

const domId = "my-div";
const div   = document.createElement("div");
div.id .    = domId;
document.body.appendChild(div);

const state = Dbc.Dom.removeById(domId);
Dbc.Contract.isTrue(state);
Dbc.Contract.isNull(document.getElementById(domId));

The following contracts are supported (2nd November, 2017):

  1. isFunction
  2. isObject
  3. isSymbol
  4. isBoolean
  5. isTrue
  6. isFalse
  7. isString
  8. isEmptyString
  9. isNotEmptyString
  10. areStringsEqual
  11. isNumber
  12. isNumberLess
  13. isNumberBigger
  14. areNumbersEqual
  15. isValueNaN
  16. isDefined
  17. isUndefined
  18. isNull
  19. isArray
  20. isEmptyArray
  21. isNotEmptyArray
  22. isObjectImmutable
  23. isPromise
  24. isPrototypeOf

Also, there are internal contract checks, inside the DOM methods from this library.


I also have thrown together my idea of type contracts, which does what I want. A little late, I think, but I'll recommend it nevertheless for people willing to look at it: https://github.com/lindem/FirstContract

It's a WIP, but I needed something like it.

function bmi (weight, height) {
    return weight / height * height;
}

var c = require("firstcontract").c
    /* 
     * contract applies to function taking two non-negative numbers,
     * returning a negative number: 
     */
    , contract = c(["R+0", "R+0"], "R+0")
    , cbmi = contract(bmi)
    ;

it was created so that I can add and remove it without changing the function monitored itself and provides just a clamp around the function. It's commonjs and on npm.


One more - https://www.npmjs.com/package/bycontract It's a small library that expects JSDoc expressions (http://usejsdoc.org/) for a contract. A good chance for you are already familiar with the syntax.

Just see it in action:

// Simple test
byContract( true, "boolean" ); // ok
// Multiple Types
byContract( 100, "string|number|boolean" ); // ok 
// Optional Parameters
function foo( bar, baz ) {
  byContract( arguments, [ "number=", "string=" ] );
}

Here kinda real-world example:

/**
 * @param {number|string} sum
 * @param {Object.<string, string>} payload
 * @param {function} cb
 * @returns {HTMLElement}
 */
function foo( sum, payload, cb ) {
  // Test if the contract is respected at entry point 
  byContract( arguments, [ "number|string", "Object.<string, string>", "function" ] );
  // .. 
  var res = document.createElement( "div" );
  // Test if the contract is respected at exit point 
  return byContract( res, HTMLElement );
}
// Test it 
foo( 100, { foo: "foo" }, function(){}); // ok 
foo( 100, { foo: 100 }, function(){}); // exception - ByContractError: Value of index 1 violates the contract `Object.<string, string>` 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜