From 00a29f9d47380a22c1954f08382bdedda0562d13 Mon Sep 17 00:00:00 2001 From: Ryan Brooks Date: Mon, 7 Sep 2015 15:09:35 +0100 Subject: [PATCH 1/4] Initial commit + test suite --- .gitignore | 1 + .jshintrc | 77 +++++++++++++++++++++++++++++++ LICENSE.md | 21 +++++++++ README.md | 17 +++++++ index.js | 7 +++ index.spec.js | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 32 +++++++++++++ 7 files changed, 278 insertions(+) create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 index.js create mode 100644 index.spec.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..d9c5637 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,77 @@ +{ + // JSHint Default Configuration File (as on JSHint website) + // See http://jshint.com/docs/ for more details + + "maxerr" : 50, // {int} Maximum error before stopping + + // Enforcing + "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : false, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. + "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "indent" : 4, // {int} Number of spaces to use for indentation + "latedef" : false, // true: Require variables/functions to be defined before being used + "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. + "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : false, // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // Unused variables: + // true : all variables, last function parameter + // "vars" : all variables only + // "strict" : all variables, all function parameters + "strict" : true, // true: Requires all functions run in ES5 Strict Mode + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : false, // {int} Max number of characters per line + "varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed. + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // true: Tolerate use of `== null` + "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements + "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : false, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : false, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "noyield" : false, // true: Tolerate generator functions with no yield statement in them. + "notypeof" : false, // true: Tolerate invalid typeof operator values + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser" : false, // Web Browser (window, document, etc) + "browserify" : false, // Browserify (node.js code in the browser) + "jasmine" : true, // Jasmine + "node" : true, // Node.js + + // Custom Globals + "globals" : {} // additional predefined global variables +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..8af142d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +This software is released under the MIT License (MIT) + +Copyright (c) 2015 Ryan Brooks + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..48bf843 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ + +#### Running and testing + +Some useful commands: + +* `npm run`: run the validator CLI. +* `npm test`: run the test suite. +* `npm run test-watch --silent`: watch for changes and run the test suite each time. +* `npm run lint --silent`: run the code linter. + +NPM is overly verbose [at the moment](https://github.com/npm/npm/issues/5452), hence the additional `--silent` to suppress the unnecessary noise. + +You can run the tests on every change with: + +```bash +karma start +``` diff --git a/index.js b/index.js new file mode 100644 index 0000000..335f103 --- /dev/null +++ b/index.js @@ -0,0 +1,7 @@ +"use strict"; + +exports.validate = validate; + +function validate(nhsNumber){ + return true; +} diff --git a/index.spec.js b/index.spec.js new file mode 100644 index 0000000..7c58d14 --- /dev/null +++ b/index.spec.js @@ -0,0 +1,123 @@ +"use strict"; + +var validator = require('./index'); + +var validNumberExamples = [ + // add specific examples to test here +]; + +var invalidNumberExamples = [ + // add specific examples to test here + "0" +]; + +var ZERO_TO_NINE = [0,1,2,3,4,5,6,7,8,9]; + +// The number of samples to take for each position +// Value between 1 and 10 +// Note that this slows down a LOT for values > 6 +var numbersToSample = 2; + +// generate some sample valid and invalid numbers. +// NOTE: this isn't seeded, so you'll want to record the number in any failure messages +// to allow it to be reproduced manually. +// this isn't a world-class method for generating permutations +randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number1) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number2) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number3) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number4) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number5) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number6) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number7) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number8) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number9) { + var multipliedTotal = number1*10 + number2*9 + number3*8 + number4*7 + number5*6 + number6*5 + number7*4 + number8*3 + number9*2; + var remainder = multipliedTotal % 11; + if(remainder == 10){ + invalidNumberExamples.push([number1, number2, number3, number4, number5, number6, number7, number8, number9, 11-remainder].join('')); + }else{ + validNumberExamples.push([number1, number2, number3, number4, number5, number6, number7, number8, number9, 11-remainder].join('')); + } + }); + }); + }); + }); + }); + }); + }); + }); +}); + +console.log('Sample data generated:') +console.log(validNumberExamples.length + ' valid samples'); +console.log(invalidNumberExamples.length + ' invalid samples'); + + +describe("validating valid numbers", function() { + validNumberExamples.forEach(function (validNumber) { + it("returns TRUE for" + validNumber, function() { + expect(validator.validate(validNumber)).toBe(true); + }); + }); + + it("validates numbers wrapped in strings", function () { + expect(validator.validate("2983396363")).toBe(true); + }); + + it("validates numbers represented as integers", function () { + expect(validator.validate(2983396339)).toBe(true); + }); +}); + +describe("validating invalid numbers", function() { + invalidNumberExamples.forEach(function (invalidNumber) { + it("returns FALSE for " + invalidNumber, function() { + expect(validator.validate(invalidNumber)).toBe(false); + }); + }); + + it("returns FALSE for the empty string", function () { + expect(validator.validate("")).toBe(false); + }); + + it("returns FALSE for undefined", function () { + expect(validator.validate()).toBe(false); + }); + + it("returns FALSE for null", function () { + expect(validator.validate(null)).toBe(false); + }); + + it("returns FALSE for 0", function () { + expect(validator.validate("0")).toBe(false); + }); + + it("returns FALSE for non-number strings", function () { + expect(validator.validate("a string")).toBe(false); + }); +}); + +// private helper functions +function randomSlice(array, sliceLength) { + return shuffle(array).slice(0, sliceLength); +} + +// Fisher-Yates shuffle, from somewhere on Stack Overflow +function shuffle(array) { + var currentIndex = array.length, temporaryValue, randomIndex ; + + // While there remain elements to shuffle... + while (0 !== currentIndex) { + + // Pick a remaining element... + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + // And swap it with the current element. + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + + return array; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..79d9cf1 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "nhs-number-validator", + "version": "1.0.0", + "description": "Validate NHS numbers in various guises", + "main": "index.js", + "scripts": { + "test": "./node_modules/.bin/jasmine-node .", + "test-watch": "./node_modules/.bin/jasmine-node . --autotest", + "lint": "./node_modules/.bin/jshint *.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/spikeheap/nhs-number-validator.git" + }, + "keywords": [ + "nhs", + "validator", + "validation" + ], + "author": "Ryan Brooks ", + "license": "MIT", + "bugs": { + "url": "https://github.com/spikeheap/nhs-number-validator/issues" + }, + "homepage": "https://github.com/spikeheap/nhs-number-validator#readme", + "devDependencies": { + "jasmine-core": "^2.3.4", + "jasmine-node": "^1.14.5", + "js-combinatorics": "^0.5.0", + "jshint": "^2.8.0" + } +} From 6b96c711c39539cdc2845c71841b50d88c85795e Mon Sep 17 00:00:00 2001 From: Ryan Brooks Date: Mon, 7 Sep 2015 16:11:18 +0100 Subject: [PATCH 2/4] Implemented validator to pass tests. --- index.js | 44 +++++++++++++++- index.spec.js | 139 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 132 insertions(+), 51 deletions(-) diff --git a/index.js b/index.js index 335f103..5c3e9bc 100644 --- a/index.js +++ b/index.js @@ -3,5 +3,47 @@ exports.validate = validate; function validate(nhsNumber){ - return true; + // pre-flight checks + if( + nhsNumber === undefined || + nhsNumber === null || + isNaN(Number(nhsNumber)) || + nhsNumber.toString().length !== 10 + ){ + return false; + } + + // convert numbers to strings, for internal consistency + if(Number.isInteger(nhsNumber)){ + nhsNumber = nhsNumber.toString(); + } + + // Step 1: Multiply each of the first 9 numbers by (11 - position indexed from 1) + // Step 2: Add the results together + // Step 3: Divide the total by 11 to get the remainder + var nhsNumberAsArray = nhsNumber.split(''); + var remainder = nhsNumberAsArray.slice(0,9) + .map(multiplyByPosition) + .reduce(addTogether, 0) % 11; + + var checkDigit = 11 - remainder; + + // replace 11 for 0 + if(checkDigit === 11){ + checkDigit = 0; + } + + var providedCheckDigit = nhsNumberAsArray[9]; + + // Do the check digits match? + return checkDigit === Number(providedCheckDigit); +} + +function multiplyByPosition(value, index) { + // multiple each digit by 11 minus its position (indexed from 1) + return value * (11 - (index+1)); +} + +function addTogether(previousValue, currentValue){ + return previousValue + currentValue; } diff --git a/index.spec.js b/index.spec.js index 7c58d14..f6d910e 100644 --- a/index.spec.js +++ b/index.spec.js @@ -2,56 +2,9 @@ var validator = require('./index'); -var validNumberExamples = [ - // add specific examples to test here -]; - -var invalidNumberExamples = [ - // add specific examples to test here - "0" -]; - -var ZERO_TO_NINE = [0,1,2,3,4,5,6,7,8,9]; - -// The number of samples to take for each position -// Value between 1 and 10 -// Note that this slows down a LOT for values > 6 -var numbersToSample = 2; - -// generate some sample valid and invalid numbers. -// NOTE: this isn't seeded, so you'll want to record the number in any failure messages -// to allow it to be reproduced manually. -// this isn't a world-class method for generating permutations -randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number1) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number2) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number3) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number4) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number5) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number6) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number7) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number8) { - randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number9) { - var multipliedTotal = number1*10 + number2*9 + number3*8 + number4*7 + number5*6 + number6*5 + number7*4 + number8*3 + number9*2; - var remainder = multipliedTotal % 11; - if(remainder == 10){ - invalidNumberExamples.push([number1, number2, number3, number4, number5, number6, number7, number8, number9, 11-remainder].join('')); - }else{ - validNumberExamples.push([number1, number2, number3, number4, number5, number6, number7, number8, number9, 11-remainder].join('')); - } - }); - }); - }); - }); - }); - }); - }); - }); -}); - -console.log('Sample data generated:') -console.log(validNumberExamples.length + ' valid samples'); -console.log(invalidNumberExamples.length + ' invalid samples'); - +var nhsSamples = generateSampleNumbers(); +var validNumberExamples = nhsSamples[0]; +var invalidNumberExamples = nhsSamples[1]; describe("validating valid numbers", function() { validNumberExamples.forEach(function (validNumber) { @@ -95,9 +48,27 @@ describe("validating invalid numbers", function() { it("returns FALSE for non-number strings", function () { expect(validator.validate("a string")).toBe(false); }); + + it("must be 10 digits long", function () { + expect(validator.validate("1")).toBe(false); + expect(validator.validate("12")).toBe(false); + expect(validator.validate("123")).toBe(false); + expect(validator.validate("1234")).toBe(false); + expect(validator.validate("12345")).toBe(false); + expect(validator.validate("123456")).toBe(false); + expect(validator.validate("1234567")).toBe(false); + expect(validator.validate("12345678")).toBe(false); + expect(validator.validate("123456789")).toBe(false); + expect(validator.validate("12345678901")).toBe(false); + expect(validator.validate("123456789012")).toBe(false); + }); }); + +// // private helper functions +// + function randomSlice(array, sliceLength) { return shuffle(array).slice(0, sliceLength); } @@ -121,3 +92,71 @@ function shuffle(array) { return array; } + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random +// Returns a random integer between min (included) and max (excluded) +// Using Math.round() will give you a non-uniform distribution! +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +// Generate sample data +function generateSampleNumbers() { + + var validNumberExamples = [ + // add specific examples to test here + ]; + + var invalidNumberExamples = [ + // add specific examples to test here + ]; + + var ZERO_TO_NINE = [0,1,2,3,4,5,6,7,8,9]; + + // The number of samples to take for each position + // Value between 1 and 10 + // Note that this slows down a LOT for values > 6 + var numbersToSample = 2; + + // generate some sample valid and invalid numbers. + // NOTE: this isn't seeded, so you'll want to record the number in any failure messages + // to allow it to be reproduced manually. + // this isn't a world-class method for generating permutations + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number1) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number2) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number3) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number4) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number5) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number6) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number7) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number8) { + randomSlice(ZERO_TO_NINE, numbersToSample).forEach(function (number9) { + var multipliedTotal = number1*10 + number2*9 + number3*8 + number4*7 + number5*6 + number6*5 + number7*4 + number8*3 + number9*2; + var remainder = multipliedTotal % 11; + var checkDigit = 11-remainder; + + if(checkDigit === 11){ + checkDigit = 0; + } + + if(checkDigit === 10){ + invalidNumberExamples.push([number1, number2, number3, number4, number5, number6, number7, number8, number9, getRandomInt(0,10)].join('')); + }else{ + validNumberExamples.push([number1, number2, number3, number4, number5, number6, number7, number8, number9, checkDigit].join('')); + } + }); + }); + }); + }); + }); + }); + }); + }); + }); + + console.log('Sample data generated:'); + console.log(validNumberExamples.length + ' valid samples'); + console.log(invalidNumberExamples.length + ' invalid samples'); + + return [validNumberExamples, invalidNumberExamples]; +} From 7b8c3b41f2e236a2fe4426482b606e4c8207116a Mon Sep 17 00:00:00 2001 From: Ryan Brooks Date: Tue, 8 Sep 2015 09:56:33 +0100 Subject: [PATCH 3/4] Cleaning up for release --- .gitignore | 2 +- README.md | 17 +++++++++++++++++ bower.json | 27 +++++++++++++++++++++++++++ index.js | 31 ++++++++++++++++++++++++++++--- 4 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 bower.json diff --git a/.gitignore b/.gitignore index 3c3629e..2ccbe46 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -node_modules +/node_modules/ diff --git a/README.md b/README.md index 48bf843..b66d2d7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ +# NHS number validator (JS) + +A simple NHS number validator, following the process described in the [NHS Data Dictionary](http://www.datadictionary.nhs.uk/data_dictionary/attributes/n/nhs/nhs_number_de.asp?shownav=0?query=%22nhs+number%22&rank=100&shownav=1). + +## Installation + +##### NPM + +``` +npm install --save nhs-number-validator +``` + +##### Bower + +``` +bower install --save nhs-number-validator +``` #### Running and testing diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..c50582c --- /dev/null +++ b/bower.json @@ -0,0 +1,27 @@ +{ + "name": "nhs-number-validator", + "main": "index.js", + "version": "1.0.0", + "homepage": "https://github.com/spikeheap/nhs-number-validator", + "authors": [ + "Ryan Brooks " + ], + "description": "A simple NHS number validator", + "moduleType": [ + "node", + "globals" + ], + "keywords": [ + "nhs", + "validator", + "validation" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/index.js b/index.js index 5c3e9bc..1aea067 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,20 @@ "use strict"; -exports.validate = validate; +// Only export where a Node/Browerserify-esque environment is present +if(typeof exports !== 'undefined'){ + exports.validate = validate; +} + +// Only attach to `window` if it's present +if(typeof window !== 'undefined'){ + window.nhsNumberValidator = {validate: validate}; +} +/** + * Validate an NHS number + * @param {Number, String} nhsNumber The NHS number to validate. This may be a String or a number. + * @returns {Boolean} `true` IFF the NHS number validates, else `false` + **/ function validate(nhsNumber){ // pre-flight checks if( @@ -39,11 +52,23 @@ function validate(nhsNumber){ return checkDigit === Number(providedCheckDigit); } -function multiplyByPosition(value, index) { +/** + * Multiply a value by its position, using the NHS number strategy + * @param {Number} digit the single-digit portion of the number + * @param {Number} index The 0-indexed position of `digit` within the NHS number + * @returns {Number} the result of the 'multiply by (11-position)' calculation + **/ +function multiplyByPosition(digit, index) { // multiple each digit by 11 minus its position (indexed from 1) - return value * (11 - (index+1)); + return digit * (11 - (index+1)); } +/** + * Add two values together. Useful for use in `reduce` calls + * @param {Number} previousValue the initial value + * @param {Number} currentValue the value to add to the initial value + * @returns {Number} the sum of the two parameters + **/ function addTogether(previousValue, currentValue){ return previousValue + currentValue; } From 0f3ed2d4166390f054215b7c1f70d96faa91cca1 Mon Sep 17 00:00:00 2001 From: Ryan Brooks Date: Tue, 8 Sep 2015 09:58:17 +0100 Subject: [PATCH 4/4] Updating release notes in readme --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b66d2d7..e30f9ae 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,29 @@ A simple NHS number validator, following the process described in the [NHS Data ##### NPM -``` +```bash npm install --save nhs-number-validator ``` ##### Bower -``` +```bash bower install --save nhs-number-validator ``` -#### Running and testing +## Usage + +```javascript +var validator = require('nhs-number-validator'); + +validator.validate('2983396339') // => true +validator.validate('test') // => false +``` + + +## Contributing + +Pull requests, issues, and questions are all welcome. Some useful commands: @@ -32,3 +44,33 @@ You can run the tests on every change with: ```bash karma start ``` + +### Creating a release + +Making a release is pretty straightforward: + +```bash +VERSION_NUMBER=1.0.0 + +git checkout master && git pull +git checkout develop && git pull + +git flow release start $VERSION_NUMBER + +# Bump the version in package.json now! +jq ".version = \"${VERSION_NUMBER}\"" package.json > package.json.new && mv package.json.new package.json +git add package.json +git commit -m "Update package version number to $VERSION_NUMBER" + +git flow release finish $VERSION_NUMBER + +git checkout master && git push +git checkout develop && git push +git push --tags + +git checkout master +hub release create -a labking_$VERSION_NUMBER.zip -m "$VERSION_NUMBER" $VERSION_NUMBER +npm publish +# Bower is published automatically using Git tags, so don't worry about that +git checkout develop +```