coupon-code
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -7,9 +7,18 @@ // -------------------------------------------------------------------------------------------------------------------- | ||
// Web : http://www.chilts.org/blog/ | ||
// Email : <chilts@appsattic.com> | ||
// Email : <andychilton@gmail.com> | ||
// | ||
// Copyright (c) : 2011 AppsAttic Ltd | ||
// Copyright (c) : AppsAttic Ltd 2011 | ||
// Web : http://www.appsattic.com/ | ||
// License : http://opensource.org/licenses/MIT | ||
// | ||
// Copyright (c) : Andrew Chilton 2013 | ||
// Web : http://chilts.org/ | ||
// License : http://opensource.org/licenses/MIT | ||
// | ||
// -------------------------------------------------------------------------------------------------------------------- | ||
// npm | ||
var xtend = require('xtend'); | ||
// -------------------------------------------------------------------------------------------------------------------- | ||
// constants | ||
@@ -20,8 +29,11 @@ | ||
var symbolsObj = {}; | ||
var i = 0; | ||
symbolsStr.split('').forEach(function(c) { | ||
symbolsArr.forEach(function(c, i) { | ||
symbolsObj[c] = i; | ||
i++; | ||
}); | ||
var defaults = { | ||
parts : 3, | ||
partLen : 4, | ||
}; | ||
// -------------------------------------------------------------------------------------------------------------------- | ||
@@ -31,7 +43,9 @@ // exports | ||
module.exports.generate = function(opts) { | ||
if ( !opts ) { | ||
opts = {}; | ||
} | ||
opts.parts = opts.parts || 3; | ||
opts = xtend({}, defaults, opts); | ||
var parts = []; | ||
var part; | ||
var i; | ||
var j; | ||
// if we have a plaintext, generate a code from that | ||
@@ -44,8 +58,8 @@ if ( opts.plaintext ) { | ||
// default to a random code | ||
var parts = []; | ||
var data; | ||
var part; | ||
for( var i = 0; i < opts.parts; i++ ) { | ||
data = randomSymbol() + randomSymbol() + randomSymbol(); | ||
part = data + checkDigitAlg1(data, i+1); | ||
for( i = 0; i < opts.parts; i++ ) { | ||
part = ''; | ||
for ( j = 0; j < opts.partLen - 1; j++ ) { | ||
part += randomSymbol(); | ||
} | ||
part = part + checkDigitAlg1(part, i+1); | ||
parts.push(part); | ||
@@ -58,29 +72,15 @@ } | ||
module.exports.validate = function(opts) { | ||
if ( !opts ) { | ||
return ''; | ||
module.exports.validate = function(code, opts) { | ||
if ( !code ) { | ||
throw new Error("Provide a code to be validated"); | ||
} | ||
opts = xtend({}, defaults, opts); | ||
// turn the string into a set of options | ||
if ( typeof opts === 'string' ) { | ||
opts = { code : opts, parts : 3 }; | ||
} | ||
// default parts to 3 | ||
opts.parts = opts.parts || 3; | ||
// if we have been given no code, this is not valid | ||
if ( !opts.code ) { | ||
return ''; | ||
} | ||
var code = opts.code; | ||
// uppercase the code, take out any random chars and replace OIZS with 0125 | ||
code = code.toUpperCase(); | ||
code = code.replace(/[^0-9A-Z]+/g, ''); | ||
code = code.replace(/O/g, '0'); | ||
code = code.replace(/I/g, '1'); | ||
code = code.replace(/Z/g, '2'); | ||
code = code.replace(/S/g, '5'); | ||
code = code.toUpperCase() | ||
.replace(/[^0-9A-Z]+/g, '') | ||
.replace(/O/g, '0') | ||
.replace(/I/g, '1') | ||
.replace(/Z/g, '2') | ||
.replace(/S/g, '5'); | ||
@@ -91,4 +91,4 @@ // split in the different parts | ||
while( tmp.length > 0 ) { | ||
parts.push( tmp.substr(0, 4) ); | ||
tmp = tmp.substr(4); | ||
parts.push( tmp.substr(0, opts.partLen) ); | ||
tmp = tmp.substr(opts.partLen); | ||
} | ||
@@ -106,3 +106,3 @@ | ||
// check this part has 4 chars | ||
if ( part.length !== 4 ) { | ||
if ( part.length !== opts.partLen ) { | ||
return ''; | ||
@@ -112,4 +112,4 @@ } | ||
// split out the data and the check | ||
data = part.substr(0, 3); | ||
check = part.substr(3, 1); | ||
data = part.substr(0, opts.partLen-1); | ||
check = part.substr(opts.partLen-1, 1); | ||
@@ -129,3 +129,3 @@ if ( check !== checkDigitAlg1(data, i+1) ) { | ||
function randomSymbol() { | ||
return symbolsArr[parseInt(Math.random() * symbolsArr.length)]; | ||
return symbolsArr[parseInt(Math.random() * symbolsArr.length, 10)]; | ||
} | ||
@@ -132,0 +132,0 @@ |
{ | ||
"name": "coupon-code", | ||
"description": "An implementation of Perl's Algorithm::CouponCode for NodeJS.", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"author": { | ||
@@ -13,5 +13,7 @@ "name":"Andrew Chilton", | ||
"devDependencies": { | ||
"tap": ">= 0.0.14" | ||
"tape": "~2.3.0" | ||
}, | ||
"dependencies": {}, | ||
"dependencies": { | ||
"xtend": "~2.1.1" | ||
}, | ||
"main": "coupon-code.js", | ||
@@ -18,0 +20,0 @@ "engines": { |
An implementation of Perl's [Algorithm::CouponCode][couponcode] for NodeJS. Thanks to [Grant][grant] for the | ||
inspiration. :) | ||
# Usage | ||
# Synopsis # | ||
var cc = require('coupon-code'); | ||
``` | ||
var cc = require('coupon-code'); | ||
// generate a 3 part code | ||
cc.generate(); | ||
=> '55G2-DHM0-50NN' | ||
// generate a 3 part code | ||
cc.generate(); | ||
=> '55G2-DHM0-50NN' | ||
// generate a 4 part code | ||
cc.generate({ parts : 4 }); | ||
=> 'U5H9-HKDH-8RNX-1EX7' | ||
// generate a 4 part code | ||
cc.generate({ parts : 4 }); | ||
=> 'U5H9-HKDH-8RNX-1EX7' | ||
// same code, just lowercased | ||
cc.validate('55g2-dhm0-50nn'); | ||
=> '55G2-DHM0-50NN' | ||
// generate a code with partLen of 6 | ||
cc.generate({ partLen : 6 }); | ||
=> WYLKQM-U35V40-9N84DA | ||
``` | ||
// various letters instead of numbers | ||
cc.validate('SSGZ-DHMO-SONN'); | ||
=> '55G2-DHM0-50NN' | ||
Now, when someone types their code in, you can check that it is valid. This means that letters like `O` | ||
are converted to `0` prior to checking. | ||
// wrong last character | ||
cc.validate('55G2-DHM0-50NK'); | ||
=> '' | ||
``` | ||
// same code, just lowercased | ||
cc.validate('55g2-dhm0-50nn'); | ||
=> '55G2-DHM0-50NN' | ||
// not enough chars in the 2nd part | ||
cc.validate('55G2-DHM-50NN'); | ||
=> '' | ||
// various letters instead of numbers | ||
cc.validate('SSGZ-DHMO-SONN'); | ||
=> '55G2-DHM0-50NN' | ||
# Example | ||
// wrong last character | ||
cc.validate('55G2-DHM0-50NK'); | ||
=> '' | ||
// not enough chars in the 2nd part | ||
cc.validate('55G2-DHM-50NN'); | ||
=> '' | ||
``` | ||
The first thing we do to each code is uppercase it. Then we convert the following letters to numbers: | ||
* O -> 0 | ||
* I -> 1 | ||
* Z -> 2 | ||
* S -> 5 | ||
This means [oizs], [OIZS] and [0125] are considered the same code. | ||
# Example # | ||
Let's say you want a user to verify they got something, whether that is an email, letter, fax or carrier pigeon. To | ||
@@ -38,6 +58,8 @@ prove they received it, they have to type the code you sent them into a certain page on your website. You create a code | ||
var cc = require('coupon-code'); | ||
``` | ||
var cc = require('coupon-code'); | ||
var verificationCode = cc.generate(); | ||
=> 55G2-DHM0-50NN | ||
var code = cc.generate(); | ||
=> 55G2-DHM0-50NN | ||
``` | ||
@@ -50,3 +72,5 @@ Time passes, letters get wet, carrier pigeons go on adventures and faxes are just as bad as they ever were. Now the | ||
[s5g2-dhmo-50nn] | ||
``` | ||
[s5g2-dhmo-50nn] | ||
``` | ||
@@ -64,3 +88,3 @@ Because our codes are case insensitive and have good conversions for similar chars, the code is accepted as correct. | ||
``` bash | ||
$ npm install coupon-code | ||
$ npm install coupon-code | ||
``` | ||
@@ -72,10 +96,12 @@ | ||
$ npm test | ||
``` | ||
$ npm test | ||
``` | ||
# Author | ||
Written by [Andrew Chilton](http://www.chilts.org/blog/) | ||
* Written by [Andrew Chilton](http://chilts.org/blog/) | ||
* Copyright 2011 [AppsAttic](http://appsattic.com/) | ||
* Copyright 2013 [Andrew Chilton](http://chilts.org/) | ||
Copyright 2011 [AppsAttic](http://www.appsattic.com/) | ||
# Inspired By | ||
@@ -82,0 +108,0 @@ |
@@ -15,12 +15,14 @@ // -------------------------------------------------------------------------------------------------------------------- | ||
var tap = require("tap"), | ||
test = tap.test, | ||
plan = tap.plan; | ||
// npm | ||
var test = require("tape"); | ||
// local | ||
var cc = require('../coupon-code'); | ||
// -------------------------------------------------------------------------------------------------------------------- | ||
var contents = /^[0-9A-Z-]+$/; | ||
var makeUp = /^\w{4}-\w{4}-\w{4}$/; | ||
var makeUpLong = /^\w{9}-\w{9}-\w{9}-\w{9}-\w{9}$/; | ||
// -------------------------------------------------------------------------------------------------------------------- | ||
test("Generating Random Coupon Codes", function (t) { | ||
@@ -30,2 +32,6 @@ // start with two random codes | ||
var code2 = cc.generate(); | ||
var codel = cc.generate({ | ||
parts : 5, | ||
partLen : 9, | ||
}); | ||
@@ -41,4 +47,8 @@ // check their contents and makeUp | ||
t.ok(codel !== '', 'codel should not be empty'); | ||
t.ok(codel.match(contents), 'code; comprises uppercase letter, digits and dashes'); | ||
t.ok(codel.match(makeUpLong), 'codel pattern is XXXX...-XXXX...-XXXX...etcetc'); | ||
// check that they aren't the same | ||
t.notEqual(code1, code2, 'two codes without plaintexts supplied are different'); | ||
t.notEqual(code1, code2, 'two codes are different'); | ||
@@ -45,0 +55,0 @@ t.end(); |
@@ -15,5 +15,6 @@ // -------------------------------------------------------------------------------------------------------------------- | ||
var tap = require("tap"), | ||
test = tap.test, | ||
plan = tap.plan; | ||
// npm | ||
var test = require("tape"); | ||
// local | ||
var cc; | ||
@@ -20,0 +21,0 @@ |
@@ -15,5 +15,6 @@ // -------------------------------------------------------------------------------------------------------------------- | ||
var tap = require("tap"), | ||
test = tap.test, | ||
plan = tap.plan; | ||
// npm | ||
var test = require("tape"); | ||
// local | ||
var cc = require('../coupon-code'); | ||
@@ -24,47 +25,54 @@ | ||
test("Validating Various Inputs", function (t) { | ||
t.ok(!cc.validate(), 'missing code failed validation'); | ||
try { | ||
t.ok(!cc.validate(), 'missing code failed validation'); | ||
t.fail('Validate should have failed with no code'); | ||
} | ||
catch(e) { | ||
t.pass('Validate threw when given no code'); | ||
} | ||
t.ok( cc.validate({ code : '1K7Q-CTFM-LMTC' }), 'valid code accepted'); | ||
t.ok(!cc.validate({ code : '1K7Q-CTFM' }), 'short code rejected'); | ||
t.ok( cc.validate('1K7Q-CTFM-LMTC'), 'valid code accepted'); | ||
t.ok(!cc.validate('1K7Q-CTFM'), 'short code rejected'); | ||
t.ok( cc.validate({ code : '1K7Q-CTFM', parts : 2 }), "but accepted with correct 'parts'"); | ||
t.ok( cc.validate('1K7Q-CTFM', { parts : 2 }), "but accepted with correct 'parts'"); | ||
t.ok(!cc.validate('CTFM-1K7Q', { parts : 2 }), "parts must be in correct order"); | ||
t.ok(!cc.validate({ code : 'CTFM-1K7Q', parts : 2 }), "parts must be in correct order"); | ||
t.ok( cc.validate('QBXA5CV4Q85E-HNYV4U3UD69M-B7XU1BHF3FYE-HXT9LD4Q0DAH-U6WMKC1WNF4N-5PCG5C4JF0GL-5DTUNJ40LRB5', { parts : 7, partLen : 12 }), "but accepted with correct 'parts'"); | ||
t.equal( cc.validate({ code : '1k7q-ctfm-lmtc' }), '1K7Q-CTFM-LMTC', "lowercase code is fixed and valid"); | ||
t.equal( cc.validate('1k7q-ctfm-lmtc'), '1K7Q-CTFM-LMTC', "lowercase code is fixed and valid"); | ||
t.equal(cc.validate({ code : 'I9oD-V467-8D52' }), '190D-V467-8D52', "'o' is fixed to '0'"); | ||
t.equal(cc.validate({ code : 'I9oD-V467-8D52' }), '190D-V467-8D52', "'O' is fixed to '0'"); | ||
t.equal(cc.validate({ code : 'i9oD-V467-8D52' }), '190D-V467-8D52', "'i' is fixed to '1'"); | ||
t.equal(cc.validate({ code : 'i9oD-V467-8D52' }), '190D-V467-8D52', "'I' is fixed to '1'"); | ||
t.equal(cc.validate({ code : 'i9oD-V467-8D5z' }), '190D-V467-8D52', "'z' is fixed to '2'"); | ||
t.equal(cc.validate({ code : 'i9oD-V467-8D5z' }), '190D-V467-8D52', "'Z' is fixed to '2'"); | ||
t.equal(cc.validate({ code : 'i9oD-V467-8Dsz' }), '190D-V467-8D52', "'s' is fixed to '5'"); | ||
t.equal(cc.validate({ code : 'i9oD-V467-8Dsz' }), '190D-V467-8D52', "'S' is fixed to '5'"); | ||
t.equal(cc.validate('I9oD-V467-8D52'), '190D-V467-8D52', "'o' is fixed to '0'"); | ||
t.equal(cc.validate('I9oD-V467-8D52'), '190D-V467-8D52', "'O' is fixed to '0'"); | ||
t.equal(cc.validate('i9oD-V467-8D52'), '190D-V467-8D52', "'i' is fixed to '1'"); | ||
t.equal(cc.validate('i9oD-V467-8D52'), '190D-V467-8D52', "'I' is fixed to '1'"); | ||
t.equal(cc.validate('i9oD-V467-8D5z'), '190D-V467-8D52', "'z' is fixed to '2'"); | ||
t.equal(cc.validate('i9oD-V467-8D5z'), '190D-V467-8D52', "'Z' is fixed to '2'"); | ||
t.equal(cc.validate('i9oD-V467-8Dsz'), '190D-V467-8D52', "'s' is fixed to '5'"); | ||
t.equal(cc.validate('i9oD-V467-8Dsz'), '190D-V467-8D52', "'S' is fixed to '5'"); | ||
t.equal(cc.validate({ code : 'i9oD/V467/8Dsz' }), '190D-V467-8D52', "alternative separator is accepted and fixed"); | ||
t.equal(cc.validate('i9oD/V467/8Dsz'), '190D-V467-8D52', "alternative separator is accepted and fixed"); | ||
t.equal(cc.validate({ code : ' i9oD V467 8Dsz ' }), '190D-V467-8D52', "whitespace is accepted and fixed"); | ||
t.equal(cc.validate(' i9oD V467 8Dsz '), '190D-V467-8D52', "whitespace is accepted and fixed"); | ||
t.equal(cc.validate({ code : ' i9oD_V467_8Dsz ' }), '190D-V467-8D52', "underscores are accepted and fixed"); | ||
t.equal(cc.validate(' i9oD_V467_8Dsz '), '190D-V467-8D52', "underscores are accepted and fixed"); | ||
t.equal(cc.validate({ code : 'i9oDV4678Dsz' }), '190D-V467-8D52', "no separator is required"); | ||
t.equal(cc.validate('i9oDV4678Dsz'), '190D-V467-8D52', "no separator is required"); | ||
t.ok( cc.validate({ code : '1K7Q', parts : 1 }), 'valid code-pretest'); | ||
t.ok(!cc.validate({ code : '1K7C', parts : 1 }), 'invalid checkdigit rejected in part 1'); | ||
t.ok( cc.validate('1K7Q', { parts : 1 }), 'valid code-pretest'); | ||
t.ok(!cc.validate('1K7C', { parts : 1 }), 'invalid checkdigit rejected in part 1'); | ||
t.ok( cc.validate({ code : '1K7Q-CTFM', parts : 2 }), 'valid code-pretest'); | ||
t.ok(!cc.validate({ code : '1K7Q-CTFW', parts : 2 }), 'invalid checkdigit rejected in part 2'); | ||
t.ok( cc.validate('1K7Q-CTFM', { parts : 2 }), 'valid code-pretest'); | ||
t.ok(!cc.validate('1K7Q-CTFW', { parts : 2 }), 'invalid checkdigit rejected in part 2'); | ||
t.ok( cc.validate({ code : '1K7Q-CTFM-LMTC', parts : 3 }), 'valid code-pretest'); | ||
t.ok(!cc.validate({ code : '1K7Q-CTFM-LMT1', parts : 3 }), 'invalid checkdigit rejected in part 3'); | ||
t.ok( cc.validate('1K7Q-CTFM-LMTC', { parts : 3 }), 'valid code-pretest'); | ||
t.ok(!cc.validate('1K7Q-CTFM-LMT1', { parts : 3 }), 'invalid checkdigit rejected in part 3'); | ||
t.ok( cc.validate({ code : '7YQH-1FU7-E1HX-0BG9', parts : 4 }), 'valid code-pretest'); | ||
t.ok(!cc.validate({ code : '7YQH-1FU7-E1HX-0BGP', parts : 4 }), 'invalid checkdigit rejected in part 4'); | ||
t.ok( cc.validate('7YQH-1FU7-E1HX-0BG9', { parts : 4 }), 'valid code-pretest'); | ||
t.ok(!cc.validate('7YQH-1FU7-E1HX-0BGP', { parts : 4 }), 'invalid checkdigit rejected in part 4'); | ||
t.ok( cc.validate({ code : 'YENH-UPJK-PTE0-20U6-QYME', parts : 5 }), 'valid code-pretest'); | ||
t.ok(!cc.validate({ code : 'YENH-UPJK-PTE0-20U6-QYMT', parts : 5 }), 'invalid checkdigit rejected in part 5'); | ||
t.ok( cc.validate('YENH-UPJK-PTE0-20U6-QYME', { parts : 5 }), 'valid code-pretest'); | ||
t.ok(!cc.validate('YENH-UPJK-PTE0-20U6-QYMT', { parts : 5 }), 'invalid checkdigit rejected in part 5'); | ||
t.ok( cc.validate({ code : 'YENH-UPJK-PTE0-20U6-QYME-RBK1', parts : 6 }), 'valid code-pretest'); | ||
t.ok(!cc.validate({ code : 'YENH-UPJK-PTE0-20U6-QYME-RBK2', parts : 6 }), 'invalid checkdigit rejected in part 6'); | ||
t.ok( cc.validate('YENH-UPJK-PTE0-20U6-QYME-RBK1', { parts : 6 }), 'valid code-pretest'); | ||
t.ok(!cc.validate('YENH-UPJK-PTE0-20U6-QYME-RBK2', { parts : 6 }), 'invalid checkdigit rejected in part 6'); | ||
@@ -71,0 +79,0 @@ t.end(); |
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
17072
10
241
117
1
+ Addedxtend@~2.1.1
+ Addedobject-keys@0.4.0(transitive)
+ Addedxtend@2.1.2(transitive)