doublescore
Advanced tools
+39
| 'use strict'; | ||
| var Types = require( './types' ); | ||
| var cloneDepth = 0; | ||
| exports.clone = function( arg ) { | ||
| cloneDepth++; | ||
| if ( cloneDepth >= 100 ) { | ||
| cloneDepth = 0; | ||
| throw new Error( 'max clone depth of 100 reached' ); | ||
| } | ||
| var target = null; | ||
| if ( arg instanceof Date ) { | ||
| target = new Date( arg.toISOString() ); | ||
| } else if ( Types.isArray( arg ) ) { | ||
| target = []; | ||
| for ( var i = 0; i < arg.length; i++ ) { | ||
| target[ i ] = exports.clone( arg[ i ] ); | ||
| } | ||
| } else if ( Types.isObject( arg ) ) { | ||
| target = {}; | ||
| for ( var field in arg ) { | ||
| if ( arg.hasOwnProperty( field ) ) { | ||
| target[ field ] = exports.clone( arg[ field ] ); | ||
| } | ||
| } | ||
| } else { // functions, etc. not cloneable, and will pass through, though for primitives like strings and numbers, arg is cloning | ||
| target = arg; | ||
| } | ||
| cloneDepth--; | ||
| return target; | ||
| }; |
+60
| 'use strict'; | ||
| var Mixin = require( './mixin' ); | ||
| var Close = module.exports = { | ||
| close: function( params ) { | ||
| params = Mixin.mixin( { | ||
| max: 1, | ||
| maxException: false, | ||
| ttl: 30000 | ||
| }, params ); | ||
| return function( cb ) { | ||
| var calls = 0; | ||
| var timeout = null; | ||
| if ( params.ttl && params.ttl > 0 ) { | ||
| timeout = setTimeout( function() { | ||
| calls++; | ||
| timeout = null; | ||
| setImmediate( cb, new Error( 'timeout' ) ); | ||
| }, params.ttl ); | ||
| } | ||
| return function( err, data ) { | ||
| calls++; | ||
| if ( params.max > -1 && calls > params.max ) { | ||
| if ( params.maxException ) { | ||
| throw new Error( 'max callbacks ' + params.max ); | ||
| } | ||
| return false; | ||
| } | ||
| if ( timeout ) { | ||
| clearTimeout( timeout ); | ||
| timeout = null; | ||
| } | ||
| if ( err ) { | ||
| cb( err ); | ||
| } else if ( arguments.length > 1 ) { | ||
| cb( null, data ); | ||
| } else { | ||
| cb( null ); | ||
| } | ||
| return true; | ||
| }; | ||
| }; | ||
| } | ||
| }; |
+129
| 'use strict'; | ||
| var Clone = require( './clone' ); | ||
| var Types = require( './types' ); | ||
| var mixinDepth = 0; | ||
| exports.mixin = function( arg ) { | ||
| mixinDepth++; | ||
| if ( mixinDepth >= 100 ) { | ||
| mixinDepth = 0; | ||
| throw new Error( 'max mixin depth of 100 reached' ); | ||
| } | ||
| var target = Clone.clone( arg ); // clone so we don't modify the original | ||
| // handle arbitrary number of mixins. precedence is from last to first item passed in. | ||
| for ( var i = 1; i < arguments.length; i++ ) { | ||
| var source = arguments[ i ]; | ||
| // mixin the source differently depending on what is in the destination | ||
| switch ( Types.getType( target ) ) { | ||
| case 'object': | ||
| case 'array': | ||
| case 'function': | ||
| // mixin in the source differently depending on its type | ||
| switch ( Types.getType( source ) ) { | ||
| case 'array': | ||
| case 'object': | ||
| case 'function': | ||
| // we don't care what descendant of object the source is | ||
| for ( var field in source ) { | ||
| // don't mixin parent fields | ||
| if ( source.hasOwnProperty( field ) ) { | ||
| // if the target is an array, only take fields that are integers | ||
| if ( Types.isArray( target ) ) { | ||
| var fieldFloat = parseFloat( field ); | ||
| // the field started with a number, or no number at all, then had non-numeric characters | ||
| if ( isNaN( fieldFloat ) || fieldFloat.toString().length !== field.length || Types.getType( fieldFloat ) !== 'integer' ) { | ||
| continue; | ||
| } | ||
| } | ||
| // recurse mixin differently depending on what the target value is | ||
| switch ( Types.getType( target[ field ] ) ) { | ||
| // for any non-objects, do this | ||
| case 'undefined': | ||
| case 'null': | ||
| switch ( Types.getType( source[ field ] ) ) { | ||
| case 'undefined': | ||
| // NO-OP undefined doesn't override anything | ||
| break; | ||
| case 'null': | ||
| target[ field ] = null; | ||
| break; | ||
| default: | ||
| target[ field ] = Clone.clone( source[ field ] ); | ||
| break; | ||
| } | ||
| break; | ||
| // if the target is already an object, we can mixin on it | ||
| default: | ||
| target[ field ] = exports.mixin( target[ field ], source[ field ] ); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| break; | ||
| default: | ||
| // NO-OP, primitives can't mixin to objects, arrays and functions | ||
| break; | ||
| } | ||
| break; | ||
| default: | ||
| // mixin in the source differently depending on its type | ||
| switch ( Types.getType( source ) ) { | ||
| // arrays and objects just replace primitives | ||
| case 'array': | ||
| case 'object': | ||
| // override primitives by just passing through a clone of parent | ||
| target = Clone.clone( source ); | ||
| break; | ||
| default: | ||
| // target is a primitive and can't be null or undefined here, and all other primitives have equal precedence, so just pass through | ||
| target = source; | ||
| break; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| mixinDepth--; | ||
| return target; | ||
| }; |
+26
| 'use strict'; | ||
| exports.timer = function() { | ||
| // account for overhead within this function, this may not be a good idea | ||
| var offset = 1; | ||
| var start = new Date().getTime() + offset; | ||
| return function( reset ) { | ||
| var diff = new Date().getTime() - start; | ||
| if ( diff < 0 ) { | ||
| diff = 0; | ||
| } | ||
| if ( reset ) { | ||
| start = new Date().getTime() + offset; | ||
| } | ||
| return diff; | ||
| }; | ||
| }; |
+64
| 'use strict'; | ||
| var myArray = Array; | ||
| if ( !myArray.isArray ) { | ||
| myArray = { | ||
| isArray: function( arg ) { | ||
| return Object.prototype.toString.call( arg ) === '[object Array]'; | ||
| } | ||
| }; | ||
| } | ||
| var Types = module.exports = { | ||
| isArray: function( arg ) { | ||
| return myArray.isArray( arg ); | ||
| }, | ||
| isNumber: function( arg ) { | ||
| return typeof arg === 'number' && !isNaN( arg ); | ||
| }, | ||
| isObject: function( arg ) { | ||
| return typeof arg === 'object' && !Types.isArray( arg ) && !(arg instanceof Number) && arg !== null; | ||
| }, | ||
| getType: function( arg ) { | ||
| if ( arg instanceof Number ) { | ||
| arg = arg * 1; | ||
| } | ||
| // handle exceptions that typeof doesn't handle | ||
| if ( arg === null ) { | ||
| return 'null'; | ||
| } else if ( Types.isArray( arg ) ) { | ||
| return 'array'; | ||
| } else if ( arg instanceof Date ) { | ||
| return 'date'; | ||
| } else if ( arg instanceof RegExp ) { | ||
| return 'regex'; | ||
| } | ||
| var type = typeof arg; | ||
| // more resolution on numbers | ||
| if ( type === 'number' ) { | ||
| if ( isNaN( arg ) ) { | ||
| type = 'not-a-number'; | ||
| } else if ( Infinity === arg ) { | ||
| type = 'infinity'; | ||
| } else if ( Math.ceil( arg ) > Math.floor( arg ) ) { | ||
| type = 'float'; | ||
| } else { | ||
| type = 'integer'; | ||
| } | ||
| } | ||
| return type; | ||
| } | ||
| }; |
+133
| 'use strict'; | ||
| var assert = require( 'assert' ); | ||
| var __ = require( '../' ); | ||
| describe( 'close', function() { | ||
| it( 'should return the correct number of arguments', function() { | ||
| var c = __.close( { max: 100 } ); | ||
| var args = null; | ||
| var cb = c( function() { | ||
| args = arguments.length; | ||
| } ); | ||
| cb( null, 200 ); | ||
| assert.strictEqual( args, 2 ); | ||
| cb( false, 200 ); | ||
| assert.strictEqual( args, 2 ); | ||
| cb( undefined, 200 ); | ||
| assert.strictEqual( args, 2 ); | ||
| cb( 0, 200 ); | ||
| assert.strictEqual( args, 2 ); | ||
| cb( new Error( 'hi' ), 200 ); | ||
| assert.strictEqual( args, 1 ); | ||
| cb( new Error( 'hi' ) ); | ||
| assert.strictEqual( args, 1 ); | ||
| cb( null ); | ||
| assert.strictEqual( args, 1 ); | ||
| cb(); | ||
| assert.strictEqual( args, 1 ); | ||
| } ); | ||
| it( 'should return data once', function() { | ||
| var c = __.close(); | ||
| var calls = 0; | ||
| var lastData = null; | ||
| var cb = c( function( err, data ) { | ||
| lastData = data; | ||
| calls++; | ||
| } ); | ||
| cb( null, 200 ); | ||
| cb( null, 'hi' ); | ||
| assert.strictEqual( lastData, 200 ); | ||
| assert.strictEqual( calls, 1 ); | ||
| } ); | ||
| it( 'should return data twice', function() { | ||
| var c = __.close( { | ||
| max: 2 | ||
| } ); | ||
| var calls = 0; | ||
| var data = []; | ||
| var cb = c( function( err, datum ) { | ||
| data.push( datum ); | ||
| calls++; | ||
| } ); | ||
| cb( null, 200 ); | ||
| cb( null, 'hi' ); | ||
| cb( null, 'bye' ); | ||
| assert.deepEqual( data, [ 200, 'hi' ] ); | ||
| assert.strictEqual( calls, 2 ); | ||
| } ); | ||
| it( 'should timeout error', function( done ) { | ||
| var c = __.close( { | ||
| ttl: 10 | ||
| } ); | ||
| var cb = c( function( err, data ) { | ||
| try { | ||
| assert( err ); | ||
| assert.strictEqual( data, undefined ); | ||
| done(); | ||
| } catch ( e ) { | ||
| done( e ); | ||
| } | ||
| } ); | ||
| setTimeout( function() { | ||
| cb( null, true ); | ||
| }, 100 ); | ||
| } ); | ||
| it( 'should throw exception on extra callbacks', function() { | ||
| var c = __.close( { | ||
| maxException: true | ||
| } ); | ||
| var calls = 0; | ||
| var lastData = null; | ||
| var cb = c( function( err, data ) { | ||
| lastData = data; | ||
| calls++; | ||
| } ); | ||
| cb( null, 200 ); | ||
| assert.throws( function() { | ||
| cb( null, 'exception' ); | ||
| }, /max callbacks 1/ ); | ||
| assert.strictEqual( calls, 1 ); | ||
| } ); | ||
| } ); |
+20
-257
@@ -1,263 +0,25 @@ | ||
| "use strict"; | ||
| 'use strict'; | ||
| var myArray = Array; | ||
| var Clone = require( './lib/clone' ); | ||
| var Close = require( './lib/close' ); | ||
| var Mixin = require( './lib/mixin' ); | ||
| var Timer = require( './lib/timer' ); | ||
| var Types = require( './lib/types' ); | ||
| if ( !myArray.isArray ) { | ||
| myArray = { | ||
| isArray: function( arg ) { | ||
| return Object.prototype.toString.call( arg ) === '[object Array]'; | ||
| } | ||
| }; | ||
| } | ||
| function isNumber( arg ) { | ||
| return typeof arg === 'number' && !isNaN( arg ); | ||
| } | ||
| function isArray( arg ) { | ||
| return myArray.isArray( arg ); | ||
| } | ||
| function isObject( arg ) { | ||
| return typeof arg === 'object' && !Array.isArray( arg ) && !(arg instanceof Number) && arg !== null; | ||
| } | ||
| function getType( arg ) { | ||
| if ( arg instanceof Number ) { | ||
| arg = arg * 1; | ||
| } | ||
| // handle exceptions that typeof doesn't handle | ||
| if ( arg === null ) { | ||
| return 'null'; | ||
| } else if ( isArray( arg ) ) { | ||
| return 'array'; | ||
| } else if ( arg instanceof Date ) { | ||
| return 'date'; | ||
| } else if ( arg instanceof RegExp ) { | ||
| return 'regex'; | ||
| } | ||
| var type = typeof arg; | ||
| // more resolution on numbers | ||
| if ( type === 'number' ) { | ||
| if ( isNaN( arg ) ) { | ||
| type = 'not-a-number'; | ||
| } else if ( Infinity === arg ) { | ||
| type = 'infinity'; | ||
| } else if ( Math.ceil( arg ) > Math.floor( arg ) ) { | ||
| type = 'float'; | ||
| } else { | ||
| type = 'integer'; | ||
| } | ||
| } | ||
| return type; | ||
| } | ||
| var cloneDepth = 0; | ||
| function clone( arg ) { | ||
| cloneDepth++; | ||
| if ( cloneDepth >= 100 ) { | ||
| cloneDepth = 0; | ||
| throw new Error( 'max clone depth of 100 reached' ); | ||
| } | ||
| var target = null; | ||
| if ( arg instanceof Date ) { | ||
| target = new Date( arg.toISOString() ); | ||
| } else if ( isArray( arg ) ) { | ||
| target = []; | ||
| for ( var i = 0; i < arg.length; i++ ) { | ||
| target[ i ] = clone( arg[ i ] ); | ||
| } | ||
| } else if ( isObject( arg ) ) { | ||
| target = {}; | ||
| for ( var field in arg ) { | ||
| if ( arg.hasOwnProperty( field ) ) { | ||
| target[ field ] = clone( arg[ field ] ); | ||
| } | ||
| } | ||
| } else { // functions, etc. not clonable, and will pass through, though for primitives like strings and numbers, arg is cloning | ||
| target = arg; | ||
| } | ||
| cloneDepth--; | ||
| return target; | ||
| } | ||
| var mixinDepth = 0; | ||
| function mixin( arg ) { | ||
| mixinDepth++; | ||
| if ( mixinDepth >= 100 ) { | ||
| mixinDepth = 0; | ||
| throw new Error( 'max mixin depth of 100 reached' ); | ||
| } | ||
| var target = clone( arg ); // clone so we don't modify the original | ||
| // handle arbitrary number of mixins. precedence is from last to first item passed in. | ||
| for ( var i = 1; i < arguments.length; i++ ) { | ||
| var source = arguments[ i ]; | ||
| // mixin the source differently depending on what is in the destination | ||
| switch ( getType( target ) ) { | ||
| case 'object': | ||
| case 'array': | ||
| case 'function': | ||
| // mixin in the source differently depending on its type | ||
| switch ( getType( source ) ) { | ||
| case 'array': | ||
| case 'object': | ||
| case 'function': | ||
| // we don't care what descendant of object the source is | ||
| for ( var field in source ) { | ||
| // don't mixin parent fields | ||
| if ( source.hasOwnProperty( field ) ) { | ||
| // if the target is an array, only take fields that are integers | ||
| if ( Array.isArray( target ) ) { | ||
| var fieldFloat = parseFloat( field ); | ||
| // the field started with a number, or no number at all, then had non-numeric characters | ||
| if ( isNaN( fieldFloat ) || fieldFloat.toString().length !== field.length || getType( fieldFloat ) !== 'integer' ) { | ||
| continue; | ||
| } | ||
| } | ||
| // recurse mixin differently depending on what the target value is | ||
| switch ( getType( target[ field ] ) ) { | ||
| // for any non-objects, do this | ||
| case 'undefined': | ||
| case 'null': | ||
| switch ( getType( source[ field ] ) ) { | ||
| case 'undefined': | ||
| // NO-OP undefined doesn't override anything | ||
| break; | ||
| case 'null': | ||
| target[ field ] = null; | ||
| break; | ||
| default: | ||
| target[ field ] = clone( source[ field ] ); | ||
| break; | ||
| } | ||
| break; | ||
| // if the target is already an object, we can mixin on it | ||
| default: | ||
| target[ field ] = mixin( target[ field ], source[ field ] ); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| break; | ||
| default: | ||
| // NO-OP, primitives can't mixin to objects, arrays and functions | ||
| break; | ||
| } | ||
| break; | ||
| default: | ||
| // mixin in the source differently depending on its type | ||
| switch ( getType( source ) ) { | ||
| // arrays and objects just replace primitives | ||
| case 'array': | ||
| case 'object': | ||
| // override primitives by just passing through a clone of parent | ||
| target = clone( source ); | ||
| break; | ||
| default: | ||
| // target is a primitive and can't be null or undefined here, and all other primitives have equal precedence, so just pass through | ||
| target = source; | ||
| break; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| mixinDepth--; | ||
| return target; | ||
| } | ||
| function timer() { | ||
| // account for overhead within this function, this may not be a good idea | ||
| var offset = 1; | ||
| var start = new Date().getTime() + offset; | ||
| return function( reset ) { | ||
| var diff = new Date().getTime() - start; | ||
| if ( diff < 0 ) { | ||
| diff = 0; | ||
| } | ||
| if ( reset ) { | ||
| start = new Date().getTime() + offset; | ||
| } | ||
| return diff; | ||
| }; | ||
| } | ||
| module.exports = function( obj ) { | ||
| return { | ||
| clone: function() { | ||
| return clone( obj ); | ||
| return Clone.clone( obj ); | ||
| }, | ||
| getType: function() { | ||
| return getType( obj ); | ||
| return Types.getType( obj ); | ||
| }, | ||
| isArray: function() { | ||
| return isArray( obj ); | ||
| return Types.isArray( obj ); | ||
| }, | ||
| isNumber: function() { | ||
| return isNumber( obj ); | ||
| return Types.isNumber( obj ); | ||
| }, | ||
| isObject: function() { | ||
| return isObject( obj ); | ||
| return Types.isObject( obj ); | ||
| }, | ||
@@ -271,3 +33,3 @@ mixin: function() { | ||
| } | ||
| return mixin.apply( module.exports, args ); | ||
| return Mixin.mixin.apply( module.exports, args ); | ||
| } | ||
@@ -277,8 +39,9 @@ }; | ||
| module.exports.clone = clone; | ||
| module.exports.getType = getType; | ||
| module.exports.timer = timer; | ||
| module.exports.isArray = isArray; | ||
| module.exports.isNumber = isNumber; | ||
| module.exports.isObject = isObject; | ||
| module.exports.mixin = mixin; | ||
| module.exports.clone = Clone.clone; | ||
| module.exports.close = Close.close; | ||
| module.exports.getType = Types.getType; | ||
| module.exports.timer = Timer.timer; | ||
| module.exports.isArray = Types.isArray; | ||
| module.exports.isNumber = Types.isNumber; | ||
| module.exports.isObject = Types.isObject; | ||
| module.exports.mixin = Mixin.mixin; |
+1
-1
@@ -5,3 +5,3 @@ { | ||
| "author": "Anthony Hildoer <anthony@bluerival.com>", | ||
| "version": "0.2.1", | ||
| "version": "0.3.0", | ||
| "repository": { | ||
@@ -8,0 +8,0 @@ "type": "git", |
+5
-0
@@ -6,2 +6,7 @@ doublescore | ||
| close() | ||
| Returns a function used to generate callbacks with a service level of max calls and minimum TTL until callback errors | ||
| isObject() | ||
@@ -8,0 +13,0 @@ |
+1
-0
| 'use strict'; | ||
| require( './close' ); | ||
| require( './clone' ); | ||
@@ -4,0 +5,0 @@ require( './getType' ); |
42114
10.17%18
50%1202
13.72%102
5.15%