From 46e22c0d17abfb2fa82cc0a6fb6f94bbf3fc3c35 Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 6 Jul 2017 13:10:54 +0200 Subject: [PATCH 1/5] refactor: remove util/model.js --- Document.js | 373 +++++++++++++++++++++++++++++-------------- test/run.js | 1 - test/util/model.js | 381 -------------------------------------------- util/model.js | 387 --------------------------------------------- 4 files changed, 255 insertions(+), 887 deletions(-) delete mode 100644 test/util/model.js delete mode 100644 util/model.js diff --git a/Document.js b/Document.js index 9af3fdd..10eeb62 100644 --- a/Document.js +++ b/Document.js @@ -1,7 +1,6 @@ var config = require('pelias-config').generate(); var pkg = require('./package'); -var model = require('./util/model'); var valid = require('./util/valid'); var transform = require('./util/transform'); var _ = require('lodash'); @@ -24,15 +23,26 @@ const parentFields = [ 'postalcode' ]; +// var parentInitJson = function(){ +// var memo = {}; +// parentFields.forEach( (field) => { +// memo[field] = []; +// memo[`${field}_a`] = []; +// memo[`${field}_id`] = []; +// }); +// return JSON.stringify( memo ); +// }(); + function Document( source, layer, source_id ){ this.name = {}; this.phrase = {}; this.parent = {}; + // this.parent = JSON.parse( parentInitJson ); this.address_parts = {}; this.center_point = {}; this.category = []; - // initialize 'parent' fields to empty arrays + // // initialize 'parent' fields to empty arrays parentFields.forEach( (field) => { this.parent[field] = []; this.parent[`${field}_a`] = []; @@ -79,65 +89,98 @@ Document.prototype.toESDocument = function() { // id Document.prototype.setId = function( id ){ - return model.setChild( '_meta' ) - .transform( transform.stringify() ) - .validate( valid.type('string') ) - .validate( valid.truthy() ) - .call( this, 'id', id ); + + id = transform.stringify()(id); + valid.type('string')(id); + valid.truthy()(id); + + this._meta.id = id; + return this; }; + Document.prototype.getId = function(){ - return model.getChild( '_meta' ) - .call( this, 'id' ); + return this._meta.id; }; // type Document.prototype.setType = function( type ){ - return model.setChild( '_meta' ) - .validate( valid.type('string') ) - .validate( valid.truthy() ) - .call( this, 'type', type ); + + valid.type('string')(type); + valid.truthy()(type); + + this._meta.type = type; + return this; }; + Document.prototype.getType = function(){ - return model.getChild( '_meta' ) - .call( this, 'type' ); + return this._meta.type; }; // source -Document.prototype.setSource = model.set( 'source' ) - .transform( transform.lowercase() ) - .validate( valid.type('string') ) - .validate( valid.truthy() ); +Document.prototype.setSource = function( source ){ + + source = transform.lowercase()(source); + valid.type('string')(source); + valid.truthy()(source); -Document.prototype.getSource = model.get( 'source' ); + this.source = source; + return this; +}; + +Document.prototype.getSource = function(){ + return this.source; +}; // layer -Document.prototype.setLayer = model.set( 'layer' ) - .transform( transform.lowercase() ) - .validate( valid.type('string') ) - .validate( valid.truthy() ); +Document.prototype.setLayer = function( layer ){ -Document.prototype.getLayer = model.get( 'layer' ); + layer = transform.lowercase()(layer); + valid.type('string')(layer); + valid.truthy()(layer); -// source -Document.prototype.setSourceId = model.set( 'source_id' ) - .transform( transform.stringify() ) - .transform( transform.lowercase() ) - .validate( valid.type('string') ) - .validate( valid.truthy() ); + this.layer = layer; + return this; +}; + +Document.prototype.getLayer = function(){ + return this.layer; +}; -Document.prototype.getSourceId = model.get( 'source_id' ); +// source id +Document.prototype.setSourceId = function( source_id ){ + + source_id = transform.stringify()(source_id); + source_id = transform.lowercase()(source_id); + valid.type('string')(source_id); + valid.truthy()(source_id); + + this.source_id = source_id; + return this; +}; + +Document.prototype.getSourceId = function(){ + return this.source_id; +}; // alpha3 -Document.prototype.setAlpha3 = model.set( 'alpha3' ) - .transform( transform.uppercase() ) - .validate( valid.type('string') ) - .validate( valid.truthy() ) - .validate( valid.length(3) ); +Document.prototype.setAlpha3 = function( alpha3 ){ + alpha3 = transform.uppercase()(alpha3); + valid.type('string')(alpha3); + valid.truthy()(alpha3); + valid.length(3)(alpha3); -Document.prototype.getAlpha3 = model.get( 'alpha3' ); + this.alpha3 = alpha3; + return this; +}; + +Document.prototype.getAlpha3 = function(){ + return this.alpha3; +}; -Document.prototype.clearAlpha3 = model.clear( 'alpha3' ); +Document.prototype.clearAlpha3 = function(){ + delete this.alpha3; +}; // globally unique id Document.prototype.getGid = function(){ @@ -145,38 +188,74 @@ Document.prototype.getGid = function(){ }; // meta -Document.prototype.setMeta = model.setChild( '_meta' ); -Document.prototype.getMeta = model.getChild( '_meta' ); -Document.prototype.hasMeta = model.hasChild( '_meta' ); -Document.prototype.delMeta = model.delChild( '_meta' ); -// names -Document.prototype.setName = function(prop, value) { - var setterFn = model.setChild( 'name' ) - .validate( valid.type('string') ) - .validate( valid.truthy() ); +Document.prototype.setMeta = function( prop, val ){ + this._meta[prop] = val; + return this; +}; + +Document.prototype.getMeta = function( prop ){ + return this._meta[prop]; +}; + +Document.prototype.hasMeta = function( prop ){ + return this._meta.hasOwnProperty( prop ); +}; - setterFn.call(this, prop, value); +Document.prototype.delMeta = function( prop ){ + if( this.hasMeta( prop ) ){ + delete this._meta[ prop ]; + return true; + } + return false; +}; + +// names +Document.prototype.setName = function( prop, value ){ - this.phrase = this.name; + valid.type('string')(value); + valid.truthy()(value); + this.name[ prop ] = value; + this.phrase[ prop ] = value; // must copy name to 'phrase' index return this; }; -Document.prototype.getName = model.getChild( 'name' ); -Document.prototype.hasName = model.hasChild( 'name' ); -Document.prototype.delName = model.delChild( 'name' ); +Document.prototype.getName = function( prop ){ + return this.name[ prop ]; +}; + +Document.prototype.hasName = function( prop ){ + return this.name.hasOwnProperty( prop ); +}; + +Document.prototype.delName = function( prop ){ + if( this.hasName( prop ) ){ + delete this.name[ prop ]; + return true; + } + return false; +}; // parent Document.prototype.addParent = function( field, name, id, abbr ){ - var add = model.pushChild( 'parent' ) - .validate( valid.type('string') ) - .validate( valid.truthy() ) - .bind(this); + + var add = function( prop, value ){ + // here + if( -1 === this.parent[prop].indexOf(value) ){ + this.parent[prop].push(value); + } + }.bind(this); + + var addValidate = function( prop, value ){ + valid.type('string')(value); + valid.truthy()(value); + add( prop, value ); + }.bind(this); // mandatory fields, eg: 'country', 'country_id' - add( field, name ); - add( field + '_id', id ); + addValidate( field, name ); + addValidate( field + '_id', id ); // optional field, eg: 'country_a', defaults to `null` for downstream ES /** @@ -204,11 +283,9 @@ Document.prototype.addParent = function( field, name, id, abbr ){ == you can now be sure that the abbreviation 'bingo' belongs to '2' and not '1'. **/ if (_.isUndefined(abbr)) { - var addNull = model.pushChild( 'parent' ).bind(this); - addNull( field + '_a', null ); - + add( field + '_a', null ); } else { - add( field + '_a', abbr ); + addValidate( field + '_a', abbr ); } // chainable @@ -217,11 +294,10 @@ Document.prototype.addParent = function( field, name, id, abbr ){ // clear all all added values Document.prototype.clearParent = function(field) { - var clear = model.clearChild( 'parent' ).bind(this); - clear( field ); - clear( field + '_id' ); - clear( field + '_a' ); + this.parent[ field ] = []; + this.parent[ field + '_id' ] = []; + this.parent[ field + '_a' ] = []; return this; }; @@ -235,71 +311,115 @@ Document.prototype.clearAllParents = function() { }; // address -Document.prototype.setAddress = function ( prop, val ){ - return model.setChild( 'address_parts' ) - .validate( valid.property( addressFields ) ) - .validate( valid.type('string') ) - .validate( valid.truthy() ) - .call( this, prop, val ); +Document.prototype.setAddress = function( prop, value ){ + + valid.property(addressFields)(value, prop); + valid.type('string')(value); + valid.truthy()(value); + + this.address_parts[ prop ] = value; + return this; }; -Document.prototype.getAddress = function ( prop ){ +Document.prototype.getAddress = function( prop ){ return this.address_parts[ prop ]; }; -Document.prototype.delAddress = function ( prop ){ - delete this.address_parts[ prop ]; +Document.prototype.hasAddress = function( prop ){ + return this.address_parts.hasOwnProperty( prop ); }; -Document.prototype.hasAddress = model.hasChild( 'address_parts' ); +Document.prototype.delAddress = function( prop ){ + if( this.hasName( prop ) ){ + delete this.address_parts[ prop ]; + return true; + } + return false; +}; // population -Document.prototype.setPopulation = model.set( 'population', null, null ) - .validate( valid.type('number') ) - .validate( valid.nonnegative() ); +Document.prototype.setPopulation = function( population ){ -Document.prototype.getPopulation = model.get( 'population' ); + valid.type('number')(population); + valid.nonnegative()(population); + + this.population = population; + return this; +}; + +Document.prototype.getPopulation = function(){ + return this.population; +}; // popularity -Document.prototype.setPopularity = model.set( 'popularity', null, null ) - .transform( transform.roundify() ) - .validate( valid.type('number') ) - .validate( valid.nonnegative() ); +Document.prototype.setPopularity = function( popularity ){ -Document.prototype.getPopularity = model.get( 'popularity' ); + popularity = transform.roundify()(popularity); + valid.type('number')(popularity); + valid.nonnegative()(popularity); -// latitude -Document.prototype.setLon = function( lon ){ - return model.setChild( 'center_point' ) - .transform( transform.floatify(6) ) - .validate( valid.type('number') ) - .validate( valid.geo('longitude') ) - .call( this, 'lon', lon ); + this.popularity = popularity; + return this; }; -Document.prototype.getLon = function(){ - return model.getChild( 'center_point' ).call( this, 'lon' ); + +Document.prototype.getPopularity = function(){ + return this.popularity; }; // longitude -Document.prototype.setLat = function( lat ){ - return model.setChild( 'center_point' ) - .transform( transform.floatify(6) ) - .validate( valid.type('number') ) - .validate( valid.geo('latitude') ) - .call( this, 'lat', lat ); +Document.prototype.setLon = function( value ){ + + value = transform.floatify(6)(value); + valid.type('number')(value); + valid.geo('longitude')(value); + + this.center_point.lon = value; + return this; +}; + +Document.prototype.getLon = function(){ + return this.center_point.lon; }; + +// latitude +Document.prototype.setLat = function( value ){ + + value = transform.floatify(6)(value); + valid.type('number')(value); + valid.geo('latitude')(value); + + this.center_point.lat = value; + return this; +}; + Document.prototype.getLat = function(){ - return model.getChild( 'center_point' ).call( this, 'lat' ); + return this.center_point.lat; }; // categories -Document.prototype.addCategory = model.push( 'category' ) - .transform( transform.lowercase() ) - .validate( valid.type('string') ) - .validate( valid.truthy() ); +Document.prototype.addCategory = function( value ){ + + value = transform.lowercase()(value); + valid.type('string')(value); + valid.truthy()(value); + + if( -1 === this.category.indexOf(value) ){ + this.category.push(value); + } + return this; +}; + +Document.prototype.removeCategory = function( value ){ -Document.prototype.removeCategory = model.splice( 'category' ); + for(var i = this.category.length - 1; i >= 0; i--) { + if(this.category[i] === value) { + this.category.splice(i, 1); + } + } + + return this; +}; // centroid Document.prototype.setCentroid = function( centroid ){ @@ -308,25 +428,42 @@ Document.prototype.setCentroid = function( centroid ){ this.setLat.call( this, centroid.lat ); return this; }; -Document.prototype.getCentroid = model.get( 'center_point' ); + +Document.prototype.getCentroid = function(){ + return this.center_point; +}; // shape -Document.prototype.setPolygon = model.set( 'shape' ) - .validate( valid.type('object') ) - .validate( valid.truthy() ); +Document.prototype.setPolygon = function( value ){ + + valid.type('object')(value); + valid.truthy()(value); + + this.shape = value; + return this; +}; -Document.prototype.getPolygon = model.get( 'shape' ); +Document.prototype.getPolygon = function(){ + return this.shape; +}; // bounding box // verify that the supplied bounding_box is a well-formed object, finally // marshaling into a ES-specific format -Document.prototype.setBoundingBox = model.set( 'bounding_box' ) - .validate( valid.type('object') ) - .validate( valid.boundingBox() ) - .postValidationTransform( transform.toULLR() ); +Document.prototype.setBoundingBox = function( value ){ + + valid.type('object')(value); + valid.boundingBox()(value); + value = transform.toULLR()(value); + + this.bounding_box = value; + return this; +}; + +Document.prototype.getBoundingBox = function(){ + return this.bounding_box; +}; -// marshal the internal bounding_box back into the representation the caller supplied -Document.prototype.getBoundingBox = model.get('bounding_box'); Document.prototype.isSupportedParent = (placetype) => { return parentFields.indexOf(placetype) !== -1; diff --git a/test/run.js b/test/run.js index 5a135ee..9e46d7b 100644 --- a/test/run.js +++ b/test/run.js @@ -25,7 +25,6 @@ var tests = [ require('./document/toESDocument.js'), require('./DocumentMapperStream.js'), require('./util/transform.js'), - require('./util/model.js'), require('./util/valid.js'), require('./serialize/test.js'), ]; diff --git a/test/util/model.js b/test/util/model.js deleted file mode 100644 index 4401c68..0000000 --- a/test/util/model.js +++ /dev/null @@ -1,381 +0,0 @@ -var model = require('../../util/model'), - valid = require('../../util/valid'), - transform = require('../../util/transform'); - -module.exports.tests = {}; - -module.exports.tests.interface = function(test) { - test('valid interface', function(t) { - - // root level accessors - t.equal(typeof model.get, 'function', 'get() is a function'); - t.equal(typeof model.set, 'function', 'set() is a function'); - - // second level accessors - t.equal(typeof model.getChild, 'function', 'getChild() is a function'); - t.equal(typeof model.hasChild, 'function', 'hasChild() is a function'); - t.equal(typeof model.setChild, 'function', 'setChild() is a function'); - t.equal(typeof model.delChild, 'function', 'delChild() is a function'); - - // root level array accessors - t.equal(typeof model.push, 'function', 'push() is a function'); - t.equal(typeof model.splice, 'function', 'splice() is a function'); - - t.end(); - }); -}; - -module.exports.tests.get = function(test) { - test('get()', function(t) { - - // invalid prop - t.throws( model.get.bind(null, null), /invalid property/ ); - - // getter - var get = model.get('myKey'); - t.equal( typeof get, 'function', 'returns function' ); - t.equal( get.length, 0, 'returns function' ); - - // inheritance - var obj = { foo: 'bar' }; - obj.getFoo = model.get('foo'); - t.equal( obj.getFoo(), 'bar', 'gets value from object' ); - - t.end(); - }); -}; - -module.exports.tests.set = function(test) { - test('set()', function(t) { - - // invalid prop - t.throws( model.set.bind(null, null), /invalid property/ ); - - // setter - var set = model.set('myKey'); - t.equal( typeof set, 'function', 'returns function' ); - t.equal( set.length, 1, 'returns function' ); - - // inheritance - var obj = {}; - obj.setFoo = model.set('foo'); - var chain = obj.setFoo('bar'); - t.equal( obj.foo, 'bar', 'sets value on object' ); - t.equal( chain, obj, 'methods chainable' ); - - // validators - obj.setFoo = model.set('foo', [ valid.length(2) ]); - t.throws( obj.setFoo.bind(null, 'b') ); - - // transforms - obj.setFoo = model.set('foo', null, [ transform.uppercase() ]); - obj.setFoo('bong'); - t.equal( obj.foo, 'BONG', 'runs transforms before setting value' ); - - // post validation transforms - obj.setFoo = model.set('foo', null, null, [ transform.uppercase() ]); - obj.setFoo('bong'); - t.equal( obj.foo, 'BONG', 'runs transforms before setting value' ); - - t.end(); - }); -}; - -module.exports.tests.clear = (test) => { - test('clear()', (t) => { - - // invalid prop - t.throws( model.clear.bind(null, null), /invalid property/ ); - - // clear-er - const clear = model.clear('myKey'); - t.equal( typeof clear, 'function', 'returns function' ); - t.equal( clear.length, 0, 'returns function' ); - - // inheritance - const obj = { key1: 'value1', key2: 'value2' }; - obj.clearKey1 = model.clear('key1'); - const chain = obj.clearKey1(); - t.notOk(obj.hasOwnProperty('key1'), 'key1 should be gone'); - t.deepEquals(obj.key2, 'value2', 'key2 should remain' ); - t.equal( chain, obj, 'methods chainable' ); - - t.end(); - }); -}; - -module.exports.tests.getChild = function(test) { - test('getChild()', function(t) { - - // invalid props - t.throws( model.getChild.bind(null, null), /invalid child/ ); - t.throws( function(){ model.getChild('test')(); }, /invalid property/ ); - - // getter - var get = model.getChild('myKey'); - t.equal( typeof get, 'function', 'returns function' ); - t.equal( get.length, 1, 'returns function' ); - - // get on non-object - var invalid = { foo: 'string' }; - invalid.getChildFoo = model.getChild('foo'); - t.throws( function(){ invalid.getChildFoo('bing'); }, /invalid child/ ); - - // inheritance - var obj = { foo: { bing: 'bong' } }; - obj.getChildFoo = model.getChild('foo'); - t.equal( obj.getChildFoo('bing'), 'bong', 'gets value from child object' ); - - t.end(); - }); -}; - -module.exports.tests.hasChild = function(test) { - test('hasChild()', function(t) { - - // invalid props - t.throws( model.hasChild.bind(null, null), /invalid child/ ); - t.throws( function(){ model.hasChild('test')(); }, /invalid property/ ); - - // hasser - var has = model.hasChild('myKey'); - t.equal( typeof has, 'function', 'returns function' ); - t.equal( has.length, 1, 'returns function' ); - - // has on non-object - var invalid = { foo: 'string' }; - invalid.hasChildFoo = model.hasChild('foo'); - t.throws( function(){ invalid.hasChildFoo('bing'); }, /invalid child/ ); - - // inheritance - var obj = { foo: { bing: 'bong' } }; - obj.hasChildFoo = model.hasChild('foo'); - t.true( obj.hasChildFoo('bing'), 'returns true if present' ); - t.false( obj.hasChildFoo('not'), 'returns false if not present' ); - - t.end(); - }); -}; - -module.exports.tests.setChild = function(test) { - test('setChild()', function(t) { - - // invalid props - t.throws( model.setChild.bind(null, null), /invalid child/ ); - t.throws( function(){ model.setChild('test')(); }, /invalid property/ ); - - // setter - var set = model.setChild('myKey'); - t.equal( typeof set, 'function', 'returns function' ); - t.equal( set.length, 2, 'returns function' ); - - // set on non-object - var invalid = { foo: 'string' }; - invalid.setChildFoo = model.setChild('foo'); - t.throws( function(){ invalid.setChildFoo('bing','bong'); }, /invalid child/ ); - - // inheritance - var obj = { foo: {} }; - obj.setChildFoo = model.setChild('foo'); - var chain = obj.setChildFoo('bing','bong'); - t.equal( obj.foo.bing, 'bong', 'sets value on child object' ); - t.equal( chain, obj, 'methods chainable' ); - - // validators - obj.setChildFoo = model.setChild('foo', [ valid.length(2) ]); - t.throws( obj.setChildFoo.bind(null, 'b') ); - - // transforms - obj.setChildFoo = model.setChild('foo', null, [ transform.uppercase() ]); - obj.setChildFoo('bing','bong'); - t.equal( obj.foo.bing, 'BONG', 'runs transforms before setting value' ); - - t.end(); - }); -}; - -module.exports.tests.delChild = function(test) { - test('delChild()', function(t) { - - // invalid props - t.throws( model.delChild.bind(null, null), /invalid child/ ); - t.throws( function(){ model.delChild('test')(); }, /invalid property/ ); - - // hasser - var has = model.delChild('myKey'); - t.equal( typeof has, 'function', 'returns function' ); - t.equal( has.length, 1, 'returns function' ); - - // has on non-object - var invalid = { foo: 'string' }; - invalid.delChildFoo = model.delChild('foo'); - t.throws( function(){ invalid.delChildFoo('bing'); }, /invalid child/ ); - - // inheritance - var obj = { foo: { bing: 'bong' } }; - obj.delChildFoo = model.delChild('foo'); - t.true( obj.delChildFoo('bing'), 'returns true if present' ); - t.false( obj.delChildFoo('not'), 'returns false if not present' ); - - t.end(); - }); -}; - -module.exports.tests.push = function(test) { - test('push()', function(t) { - - // invalid prop - t.throws( model.push.bind(null, null), /invalid property/ ); - - // pusher - var push = model.push('myKey'); - t.equal( typeof push, 'function', 'returns function' ); - t.equal( push.length, 1, 'returns function' ); - - // push on non-array - var invalid = { foo: 'string' }; - invalid.pushFoo = model.push('foo'); - t.throws( function(){ invalid.pushFoo('item1'); }, /invalid child/ ); - - // inheritance - var obj = { foo: [] }; - obj.pushFoo = model.push('foo'); - var chain = obj.pushFoo('bar'); - t.deepEqual( obj.foo, ['bar'], 'add value to set' ); - t.equal( chain, obj, 'methods chainable' ); - - // validators - obj.pushFoo = model.push('foo', [ valid.length(2) ]); - t.throws( obj.pushFoo.bind(null, 'b') ); - - // transforms - obj.pushFoo = model.push('foo', null, [ transform.uppercase() ]); - obj.pushFoo('bong'); - t.deepEqual( obj.foo, ['bar','BONG'], 'runs transforms before pushing value' ); - - t.end(); - }); -}; - -module.exports.tests.splice = function(test) { - test('splice()', function(t) { - - // invalid prop - t.throws( model.splice.bind(null, null), /invalid property/ ); - - // splicer - var splice = model.splice('myKey'); - t.equal( typeof splice, 'function', 'returns function' ); - t.equal( splice.length, 1, 'returns function' ); - - // splice on non-array - var invalid = { foo: 'string' }; - invalid.spliceFoo = model.splice('foo'); - t.throws( function(){ invalid.spliceFoo('item1'); }, /invalid child/ ); - - // inheritance - var obj = { foo: ['bar','BONG'] }; - obj.spliceFoo = model.splice('foo'); - var chain = obj.spliceFoo('BONG'); - t.deepEqual( obj.foo, ['bar'], 'remove value from set' ); - t.equal( chain, obj, 'methods chainable' ); - - t.end(); - }); -}; - -module.exports.tests.pushChild = function(test) { - test('pushChild()', function(t) { - - // invalid prop - t.throws( model.pushChild.bind(null, null), /invalid child/ ); - - // pusher - var push = model.pushChild('myKey'); - t.equal( typeof push, 'function', 'returns function' ); - t.equal( push.length, 2, 'returns function' ); - - // push on non-array - var invalid = { foo: { bar: 'string' } }; - invalid.pushChildFoo = model.pushChild('foo'); - t.throws( function(){ invalid.pushChildFoo('bar','item1'); }, /invalid child/ ); - - // inheritance - var obj = { foo: { baz: [] } }; - obj.pushChildFoo = model.pushChild('foo'); - var chain = obj.pushChildFoo('baz','item1'); - t.deepEqual( obj.foo.baz, ['item1'], 'add value to set' ); - t.equal( chain, obj, 'methods chainable' ); - - // validators - obj.pushChildFoo = model.pushChild('foo', [ valid.length(2) ]); - t.throws( obj.pushChildFoo.bind(null, 'baz', 'b') ); - - // transforms - obj.pushChildFoo = model.pushChild('foo', null, [ transform.uppercase() ]); - obj.pushChildFoo('baz','item2'); - t.deepEqual( obj.foo.baz, ['item1','ITEM2'], 'runs transforms before pushing value' ); - - t.end(); - }); -}; - -module.exports.tests.clearChild = function(test) { - test('clearChild()', function(t) { - // invalid prop - t.throws( model.clearChild.bind(null, null), /invalid child/ ); - - // clearer - var clear = model.clearChild('myKey'); - t.equal( typeof clear, 'function', 'returns function' ); - t.equal( clear.length, 1, 'returns function' ); - - // inheritance - var obj = { foo: { baz: [1, 2] } }; - obj.clearChildFoo = model.clearChild('foo'); - var chain = obj.clearChildFoo('baz'); - t.deepEqual( obj.foo.baz, [], 'empty array' ); - t.equal( chain, obj, 'methods chainable' ); - - t.end(); - - }); -}; - -module.exports.tests.spliceChild = function(test) { - test('spliceChild()', function(t) { - - // invalid prop - t.throws( model.spliceChild.bind(null, null), /invalid child/ ); - - // splicer - var splice = model.spliceChild('myKey'); - t.equal( typeof splice, 'function', 'returns function' ); - t.equal( splice.length, 2, 'returns function' ); - - // splice on non-array - var invalid = { foo: { bar: 'string' } }; - invalid.spliceChildFoo = model.spliceChild('foo'); - t.throws( function(){ invalid.spliceChildFoo('bar','item1'); }, /invalid child/ ); - - // inheritance - var obj = { foo: { bar: ['item1','ITEM2'] } }; - obj.spliceFoo = model.spliceChild('foo'); - var chain = obj.spliceFoo('bar','ITEM2'); - t.deepEqual( obj.foo.bar, ['item1'], 'remove value from set' ); - t.equal( chain, obj, 'methods chainable' ); - - t.end(); - }); -}; - -module.exports.all = function (tape, common) { - - function test(name, testFunction) { - return tape('model: ' + name, testFunction); - } - - for( var testCase in module.exports.tests ){ - module.exports.tests[testCase](test, common); - } -}; diff --git a/util/model.js b/util/model.js deleted file mode 100644 index 6d8a500..0000000 --- a/util/model.js +++ /dev/null @@ -1,387 +0,0 @@ - -var PeliasModelError = require('../errors').PeliasModelError; - -/** - Get the value of a property from the root of the model. - - // example: - var get = model.get('myKey') - get() // returns: model['myKey'] - -**/ -module.exports.get = function( prop ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - return function(){ - return this[prop]; - }; -}; - -/** - Set the value of a property on the root of the model. - - // example: - var set = model.set('myKey') - set('example') // returns: model - // effect: model['myKey'] = 'example' - - // validators example: - var set = model.set('myKey', [ valid.type('string') ]) - set(1) // throws Error - - // transformers example: - var set = model.set('myKey', null, [ transform.uppercase() ]) - set('example') // returns: model - // effect: model['myKey'] = 'EXAMPLE' - - // post validation transformers example: - var set = model.set('myKey', null, null, [ transform.roundify() ]) - set(1.11111) // returns: model - // effect: model['myKey'] = 1 - -**/ -module.exports.set = function( prop, validators, transformers, postValidationTransformers ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !validators ){ validators = []; } - if( !transformers ){ transformers = []; } - if( !postValidationTransformers ) { postValidationTransformers = []; } - var setter = function( val ){ - - val = transform( val, transformers ); - validate( val, prop, validators ); - val = transform( val, postValidationTransformers ); - - this[prop] = val; - - // chain - return this; - }; - setter.validate = function( validator ){ - validators.push( validator ); - return setter; - }; - setter.transform = function( transformer ){ - transformers.push( transformer ); - return setter; - }; - setter.postValidationTransform = function( transformer ) { - postValidationTransformers.push( transformer); - return setter; - }; - return setter; -}; - -// clears the value of a property -module.exports.clear = (prop) => { - if( !prop ) { - throw new PeliasModelError( 'invalid property' ); - } - return function() { - delete this[prop]; - // make chainable - return this; - }; -}; - -/** - Get the value of a property from a *second level* property of the model. - - // example: - var get = model.getChild('myKey') - get('myChildKey') // returns: model['myKey']['myChildKey'] - -**/ -module.exports.getChild = function( child ){ - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - return function( prop ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !this.hasOwnProperty(child) ){ throw new PeliasModelError( 'invalid child' ); } - if( 'object' !== typeof this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( null === this[child] ){ throw new PeliasModelError( 'invalid child' ); } - - return this[child][prop]; - }; -}; - -/** - Test the existence of a *second level* property of the model. - - // example: - var has = model.hasChild('myKey') - has('myChildKey') // returns: false - -**/ -module.exports.hasChild = function( child ){ - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - return function( prop ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !this.hasOwnProperty(child) ){ throw new PeliasModelError( 'invalid child' ); } - if( 'object' !== typeof this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( null === this[child] ){ throw new PeliasModelError( 'invalid child' ); } - - return this.hasOwnProperty(child) && this[child].hasOwnProperty(prop); - }; -}; - -/** - Set the value of a *second level* property of the model. - - // example: - var set = model.setChild('myKey') - set('myChildKey', 'example') // returns: model - // effect: model['myKey']['myChildKey'] = 'example' - - // validators & transformers behave the same as model.get() - // see: the code comments above for more info. - -**/ -module.exports.setChild = function( child, validators, transformers ){ - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - if( !validators ){ validators = []; } - if( !transformers ){ transformers = []; } - var setter = function( prop, val ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !this.hasOwnProperty(child) ){ throw new PeliasModelError( 'invalid child' ); } - if( 'object' !== typeof this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( null === this[child] ){ throw new PeliasModelError( 'invalid child' ); } - - val = transform( val, transformers ); - validate( val, prop, validators ); - this[child][prop] = val; - - // chain - return this; - }; - setter.validate = function( validator ){ - validators.push( validator ); - return setter; - }; - setter.transform = function( transformer ){ - transformers.push( transformer ); - return setter; - }; - return setter; -}; - -/** - Remove the *second level* property of the model. - Returns true if the property was found and deleted, else false. - - // example: - var del = model.delChild('myKey') - del('myChildKey') // returns: false - -**/ -module.exports.delChild = function( child ){ - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - return function( prop ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - - if( module.exports.hasChild( child ).call( this, prop ) ){ - delete this[child][prop]; - return true; - } - return false; - }; -}; - -/** - Push a value on to the Array stored at a root property of the model. - Note: the Array enforced uniqueness and will not store duplicates. - - // example: - model.items = []; - var push = model.push('items') - push('item1') // returns: model - push('item2') // returns: model - // effect: model.items = ['item1', 'item2'] - - // validators & transformers behave the same as model.get() - // see: the code comments above for more info. - -**/ -module.exports.push = function( prop, validators, transformers ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !validators ){ validators = []; } - if( !transformers ){ transformers = []; } - var adder = function( val ){ - if( !this.hasOwnProperty(prop) ){ throw new PeliasModelError( 'invalid child' ); } - if( !Array.isArray(this[prop]) ){ throw new PeliasModelError( 'invalid child' ); } - - val = transform( val, transformers ); - validate( val, prop, validators ); - - if( -1 === this[prop].indexOf(val) ){ - this[prop].push(val); - } - - // chain - return this; - }; - adder.validate = function( validator ){ - validators.push( validator ); - return adder; - }; - adder.transform = function( transformer ){ - transformers.push( transformer ); - return adder; - }; - return adder; -}; - -/** - Push a value on to the Array stored at *second level* of the model. - Note: the Array enforced uniqueness and will not store duplicates. - - // example: - model.items.child = []; - var push = model.pushChild('items') - push('child','item1') // returns: model - push('child','item2') // returns: model - // effect: model.items.child = ['item1', 'item2'] - - // validators & transformers behave the same as model.get() - // see: the code comments above for more info. - -**/ -module.exports.pushChild = function( child, validators, transformers ){ - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - if( !validators ){ validators = []; } - if( !transformers ){ transformers = []; } - var setter = function( prop, val ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !this.hasOwnProperty(child) ){ throw new PeliasModelError( 'invalid child' ); } - if( 'object' !== typeof this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( null === this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( !this[child].hasOwnProperty(prop) ){ throw new PeliasModelError( 'invalid child' ); } - if( !Array.isArray(this[child][prop]) ){ throw new PeliasModelError( 'invalid child' ); } - - val = transform( val, transformers ); - validate( val, prop, validators ); - - if( -1 === this[child][prop].indexOf(val) ){ - this[child][prop].push(val); - } - - // chain - return this; - }; - setter.validate = function( validator ){ - validators.push( validator ); - return setter; - }; - setter.transform = function( transformer ){ - transformers.push( transformer ); - return setter; - }; - return setter; -}; - -/** - Clear a value from the Array stored at a root property of the model. - - // example: - model.items = []; - var push = model.push('items') - push('item1') // returns: model - push('item2') // returns: model - model.clear('items') - // effect: model.items = [] - -**/ -module.exports.clearChild = function(child) { - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - var clearer = function(prop) { - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !this.hasOwnProperty(child) ){ throw new PeliasModelError( 'invalid child' ); } - if( 'object' !== typeof this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( null === this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( !this[child].hasOwnProperty(prop) ){ throw new PeliasModelError( 'invalid child' ); } - if( !Array.isArray(this[child][prop]) ){ throw new PeliasModelError( 'invalid child' ); } - - this[child][prop] = []; - return this; - }; - return clearer; -}; - -/** - Remove a value from the Array stored at a root property of the model. - - // example: - model.items = ['item1', 'item2']; - var splice = model.splice('items') // returns: model - splice('item1') // returns: model - // effect: model.items = ['item2'] - -**/ -module.exports.splice = function( prop ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - var splicer = function( val ){ - if( !this.hasOwnProperty(prop) ){ throw new PeliasModelError( 'invalid child' ); } - if( !Array.isArray(this[prop]) ){ throw new PeliasModelError( 'invalid child' ); } - - for(var i = this[prop].length - 1; i >= 0; i--) { - if(this[prop][i] === val) { - this[prop].splice(i, 1); - } - } - return this; - }; - return splicer; -}; - -/** - Remove a value from the Array stored at *second level* property of the model. - - // example: - model.items.child = ['item1', 'item2']; - var splice = model.spliceChild('items') // returns: model - splice('child','item1') // returns: model - // effect: model.items.child = ['item2'] - -**/ -module.exports.spliceChild = function( child ){ - if( !child ){ throw new PeliasModelError( 'invalid child' ); } - var setter = function( prop, val ){ - if( !prop ){ throw new PeliasModelError( 'invalid property' ); } - if( !this.hasOwnProperty(child) ){ throw new PeliasModelError( 'invalid child' ); } - if( 'object' !== typeof this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( null === this[child] ){ throw new PeliasModelError( 'invalid child' ); } - if( !this[child].hasOwnProperty(prop) ){ throw new PeliasModelError( 'invalid child' ); } - if( !Array.isArray(this[child][prop]) ){ throw new PeliasModelError( 'invalid child' ); } - - for(var i = this[child][prop].length - 1; i >= 0; i--) { - if(this[child][prop][i] === val) { - this[child][prop].splice(i, 1); - } - } - - // chain - return this; - }; - return setter; -}; - -/** - Utility function to run an Array of validators against a property -**/ -function validate( val, prop, validators ){ - if( validators ){ - validators.forEach( function( validator ) { - validator( val, prop ); - }); - } -} - -/** - Utility function to run an Array of transformers against a property -**/ -function transform( val, transformers ){ - if( transformers ){ - transformers.forEach( function( transformer ) { - val = transformer( val ); - }); - } - - return val; -} From bfbea99fd74e83dded4bb9a89c959d4727d0092f Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 6 Jul 2017 13:18:07 +0200 Subject: [PATCH 2/5] refactor: simplify util/valid.js --- Document.js | 70 ++++++++--------- test/util/valid.js | 6 +- util/valid.js | 184 ++++++++++++++++++++++----------------------- 3 files changed, 126 insertions(+), 134 deletions(-) diff --git a/Document.js b/Document.js index 10eeb62..d7361cb 100644 --- a/Document.js +++ b/Document.js @@ -1,7 +1,7 @@ var config = require('pelias-config').generate(); var pkg = require('./package'); -var valid = require('./util/valid'); +var validate = require('./util/valid'); var transform = require('./util/transform'); var _ = require('lodash'); @@ -91,8 +91,8 @@ Document.prototype.toESDocument = function() { Document.prototype.setId = function( id ){ id = transform.stringify()(id); - valid.type('string')(id); - valid.truthy()(id); + validate.type('string', id); + validate.truthy(id); this._meta.id = id; return this; @@ -105,8 +105,8 @@ Document.prototype.getId = function(){ // type Document.prototype.setType = function( type ){ - valid.type('string')(type); - valid.truthy()(type); + validate.type('string', type); + validate.truthy(type); this._meta.type = type; return this; @@ -120,8 +120,8 @@ Document.prototype.getType = function(){ Document.prototype.setSource = function( source ){ source = transform.lowercase()(source); - valid.type('string')(source); - valid.truthy()(source); + validate.type('string', source); + validate.truthy(source); this.source = source; return this; @@ -135,8 +135,8 @@ Document.prototype.getSource = function(){ Document.prototype.setLayer = function( layer ){ layer = transform.lowercase()(layer); - valid.type('string')(layer); - valid.truthy()(layer); + validate.type('string', layer); + validate.truthy(layer); this.layer = layer; return this; @@ -151,8 +151,8 @@ Document.prototype.setSourceId = function( source_id ){ source_id = transform.stringify()(source_id); source_id = transform.lowercase()(source_id); - valid.type('string')(source_id); - valid.truthy()(source_id); + validate.type('string', source_id); + validate.truthy(source_id); this.source_id = source_id; return this; @@ -166,9 +166,9 @@ Document.prototype.getSourceId = function(){ Document.prototype.setAlpha3 = function( alpha3 ){ alpha3 = transform.uppercase()(alpha3); - valid.type('string')(alpha3); - valid.truthy()(alpha3); - valid.length(3)(alpha3); + validate.type('string', alpha3); + validate.truthy(alpha3); + validate.length(3, alpha3); this.alpha3 = alpha3; return this; @@ -213,8 +213,8 @@ Document.prototype.delMeta = function( prop ){ // names Document.prototype.setName = function( prop, value ){ - valid.type('string')(value); - valid.truthy()(value); + validate.type('string', value); + validate.truthy(value); this.name[ prop ] = value; this.phrase[ prop ] = value; // must copy name to 'phrase' index @@ -248,8 +248,8 @@ Document.prototype.addParent = function( field, name, id, abbr ){ }.bind(this); var addValidate = function( prop, value ){ - valid.type('string')(value); - valid.truthy()(value); + validate.type('string', value); + validate.truthy(value); add( prop, value ); }.bind(this); @@ -313,9 +313,9 @@ Document.prototype.clearAllParents = function() { // address Document.prototype.setAddress = function( prop, value ){ - valid.property(addressFields)(value, prop); - valid.type('string')(value); - valid.truthy()(value); + validate.property(addressFields, prop); + validate.type('string', value); + validate.truthy(value); this.address_parts[ prop ] = value; return this; @@ -340,8 +340,8 @@ Document.prototype.delAddress = function( prop ){ // population Document.prototype.setPopulation = function( population ){ - valid.type('number')(population); - valid.nonnegative()(population); + validate.type('number', population); + validate.nonnegative(population); this.population = population; return this; @@ -355,8 +355,8 @@ Document.prototype.getPopulation = function(){ Document.prototype.setPopularity = function( popularity ){ popularity = transform.roundify()(popularity); - valid.type('number')(popularity); - valid.nonnegative()(popularity); + validate.type('number', popularity); + validate.nonnegative(popularity); this.popularity = popularity; return this; @@ -370,8 +370,8 @@ Document.prototype.getPopularity = function(){ Document.prototype.setLon = function( value ){ value = transform.floatify(6)(value); - valid.type('number')(value); - valid.geo('longitude')(value); + validate.type('number', value); + validate.geo('longitude', value); this.center_point.lon = value; return this; @@ -385,8 +385,8 @@ Document.prototype.getLon = function(){ Document.prototype.setLat = function( value ){ value = transform.floatify(6)(value); - valid.type('number')(value); - valid.geo('latitude')(value); + validate.type('number', value); + validate.geo('latitude', value); this.center_point.lat = value; return this; @@ -400,8 +400,8 @@ Document.prototype.getLat = function(){ Document.prototype.addCategory = function( value ){ value = transform.lowercase()(value); - valid.type('string')(value); - valid.truthy()(value); + validate.type('string', value); + validate.truthy(value); if( -1 === this.category.indexOf(value) ){ this.category.push(value); @@ -436,8 +436,8 @@ Document.prototype.getCentroid = function(){ // shape Document.prototype.setPolygon = function( value ){ - valid.type('object')(value); - valid.truthy()(value); + validate.type('object', value); + validate.truthy(value); this.shape = value; return this; @@ -452,8 +452,8 @@ Document.prototype.getPolygon = function(){ // marshaling into a ES-specific format Document.prototype.setBoundingBox = function( value ){ - valid.type('object')(value); - valid.boundingBox()(value); + validate.type('object', value); + validate.boundingBox(value); value = transform.toULLR()(value); this.bounding_box = value; diff --git a/test/util/valid.js b/test/util/valid.js index 3ca2d51..4a52bd8 100644 --- a/test/util/valid.js +++ b/test/util/valid.js @@ -4,17 +4,17 @@ module.exports.tests = {}; module.exports.tests.nonnegative = (test) => { test('nonnegative should throw error if value is less than 0', (t) => { - t.throws(valid.nonnegative().bind(null, -1), /invalid document type, expecting: 0 or a positive number, got: -1/); + t.throws(valid.nonnegative.bind(null, -1), /invalid document type, expecting: 0 or a positive number, got: -1/); t.end(); }); test('nonnegative should not throw error if value is 0', (t) => { - t.doesNotThrow(valid.nonnegative().bind(null, 0), '0 should be allowed'); + t.doesNotThrow(valid.nonnegative.bind(null, 0), '0 should be allowed'); t.end(); }); test('nonnegative should not throw error if value is greater than 0', (t) => { - t.doesNotThrow(valid.nonnegative().bind(null, 1), '1 should be allowed'); + t.doesNotThrow(valid.nonnegative.bind(null, 1), '1 should be allowed'); t.end(); }); diff --git a/util/valid.js b/util/valid.js index d4832f5..e753a3f 100644 --- a/util/valid.js +++ b/util/valid.js @@ -2,119 +2,111 @@ var _ = require('lodash'), PeliasModelError = require('../errors').PeliasModelError; -module.exports.type = function( type ){ - return function( val ){ - if( type.toLowerCase() === 'array' ){ - if( !Array.isArray( val ) ){ - throw new PeliasModelError( 'invalid document type, expecting: ' + type + ' got: ' + val ); - } - } else { - if( typeof val !== type ){ - throw new PeliasModelError( 'invalid document type, expecting: ' + type + ' got: ' + val ); - } - if( type === 'number' && isNaN( val ) ){ - throw new PeliasModelError( 'invalid document type, expecting: number, got NaN: ' + val ); - } - if( type === 'object' && Array.isArray( val ) ){ - throw new PeliasModelError( 'invalid document type, expecting: object, got array: ' + val ); - } - if( type === 'object' && null === val ){ - throw new PeliasModelError( 'invalid document type, expecting: object, got null: ' + val ); - } +module.exports.type = function( type, val ){ + if( type.toLowerCase() === 'array' ){ + if( !Array.isArray( val ) ){ + throw new PeliasModelError( 'invalid document type, expecting: ' + type + ' got: ' + val ); } - }; + } else { + if( typeof val !== type ){ + throw new PeliasModelError( 'invalid document type, expecting: ' + type + ' got: ' + val ); + } + if( type === 'number' && isNaN( val ) ){ + throw new PeliasModelError( 'invalid document type, expecting: number, got NaN: ' + val ); + } + if( type === 'object' && Array.isArray( val ) ){ + throw new PeliasModelError( 'invalid document type, expecting: object, got array: ' + val ); + } + if( type === 'object' && null === val ){ + throw new PeliasModelError( 'invalid document type, expecting: object, got null: ' + val ); + } + } + return this; }; -module.exports.truthy = function(){ - return function( val ){ - if( (typeof val === 'string' && !val.trim() ) || !val ){ - throw new PeliasModelError( 'invalid document type, expecting: truthy, got: ' + val ); - } - }; +module.exports.truthy = function( val ){ + if( (typeof val === 'string' && !val.trim() ) || !val ){ + throw new PeliasModelError( 'invalid document type, expecting: truthy, got: ' + val ); + } + return this; }; -module.exports.nonnegative = function(){ - return function( val ){ - if( val < 0 ){ - throw new PeliasModelError( 'invalid document type, expecting: 0 or a positive number, got: ' + val ); - } - }; +module.exports.nonnegative = function( val ){ + if( val < 0 ){ + throw new PeliasModelError( 'invalid document type, expecting: 0 or a positive number, got: ' + val ); + } + return this; }; -module.exports.length = function( length ){ - return function( val ){ - if( val.length !== length ){ - throw new PeliasModelError( 'invalid property length, expecting: ' + length + ' got: ' + val); - } - }; +module.exports.length = function( length, val ){ + if( val.length !== length ){ + throw new PeliasModelError( 'invalid property length, expecting: ' + length + ' got: ' + val); + } + return this; }; -module.exports.geo = function( axis ){ - return function( val ){ - if( typeof val !== 'number' ){ - throw new PeliasModelError( 'invalid geo ' + axis + ', expecting: number, got: ' + val ); +module.exports.geo = function( axis, val ){ + if( typeof val !== 'number' ){ + throw new PeliasModelError( 'invalid geo ' + axis + ', expecting: number, got: ' + val ); + } + if( axis === 'latitude' ){ + if( val < -90 || val > 90 ){ + throw new PeliasModelError( 'invalid axis: ' + axis + ', ' + val ); } - if( axis === 'latitude' ){ - if( val < -90 || val > 90 ){ - throw new PeliasModelError( 'invalid axis: ' + axis + ', ' + val ); - } - } else if( axis === 'longitude' ){ - if( val < -180 || val > 180 ){ - throw new PeliasModelError( 'invalid axis: ' + axis + ', ' + val ); - } - } else { - throw new PeliasModelError( 'invalid axis: ' + axis ); + } else if( axis === 'longitude' ){ + if( val < -180 || val > 180 ){ + throw new PeliasModelError( 'invalid axis: ' + axis + ', ' + val ); } - }; + } else { + throw new PeliasModelError( 'invalid axis: ' + axis ); + } + return this; }; // validate prop against a properties whitelist -module.exports.property = function( propList ){ - return function( val, prop ){ - if( -1 === propList.indexOf( prop ) ){ - throw new PeliasModelError( 'invalid property: ' + prop + ', should be one of: ' + propList.join(',') ); - } - }; +module.exports.property = function( propList, prop ){ + if( -1 === propList.indexOf( prop ) ){ + throw new PeliasModelError( 'invalid property: ' + prop + ', should be one of: ' + propList.join(',') ); + } + return this; }; -module.exports.boundingBox = function() { - return function ( val ) { - // upperLeft must be an object - if (!_.isObject(val.upperLeft)) { - throw new PeliasModelError('invalid boundingBox, non-object property \'upperLeft\''); - } - // must have upperLeft.lat within valid range - if (!_.isFinite(val.upperLeft.lat) || val.upperLeft.lat < -90 || val.upperLeft.lat > 90) { - throw new PeliasModelError('invalid boundingBox, property \'upperLeft\.lat\' must be within range -90 to 90'); - } - // must have upperLeft.lon within valid range - if (!_.isFinite(val.upperLeft.lon) || val.upperLeft.lon < -180 || val.upperLeft.lon > 180) { - throw new PeliasModelError('invalid boundingBox, property \'upperLeft\.lon\' must be within range -180 to 180'); - } - - // lowerRight must be an object - if (!_.isObject(val.lowerRight)) { - throw new PeliasModelError('invalid boundingBox, non-object property \'lowerRight\''); - } - // must have lowerRight.lat within valid range - if (!_.isFinite(val.lowerRight.lat) || val.lowerRight.lat < -90 || val.lowerRight.lat > 90) { - throw new PeliasModelError('invalid boundingBox, property \'lowerRight\.lat\' must be within range -90 to 90'); - } - // must have lowerRight.lon within valid range - if (!_.isFinite(val.lowerRight.lon) || val.lowerRight.lon < -180 || val.lowerRight.lon > 180) { - throw new PeliasModelError('invalid boundingBox, property \'lowerRight\.lon\' must be within range -180 to 180'); - } - - // geometry must be internally consistent - if (val.upperLeft.lat < val.lowerRight.lat) { - throw new PeliasModelError('invalid boundingBox, upperLeft.lat must be >= lowerRight.lat'); - } +module.exports.boundingBox = function( val ) { + // upperLeft must be an object + if (!_.isObject(val.upperLeft)) { + throw new PeliasModelError('invalid boundingBox, non-object property \'upperLeft\''); + } + // must have upperLeft.lat within valid range + if (!_.isFinite(val.upperLeft.lat) || val.upperLeft.lat < -90 || val.upperLeft.lat > 90) { + throw new PeliasModelError('invalid boundingBox, property \'upperLeft\.lat\' must be within range -90 to 90'); + } + // must have upperLeft.lon within valid range + if (!_.isFinite(val.upperLeft.lon) || val.upperLeft.lon < -180 || val.upperLeft.lon > 180) { + throw new PeliasModelError('invalid boundingBox, property \'upperLeft\.lon\' must be within range -180 to 180'); + } - // normalize lon values by adding 360 so that only positives are compared - if (val.upperLeft.lon+360 > val.lowerRight.lon+360) { - throw new PeliasModelError('invalid boundingBox, upperLeft.lon must be <= lowerRight.lon'); - } + // lowerRight must be an object + if (!_.isObject(val.lowerRight)) { + throw new PeliasModelError('invalid boundingBox, non-object property \'lowerRight\''); + } + // must have lowerRight.lat within valid range + if (!_.isFinite(val.lowerRight.lat) || val.lowerRight.lat < -90 || val.lowerRight.lat > 90) { + throw new PeliasModelError('invalid boundingBox, property \'lowerRight\.lat\' must be within range -90 to 90'); + } + // must have lowerRight.lon within valid range + if (!_.isFinite(val.lowerRight.lon) || val.lowerRight.lon < -180 || val.lowerRight.lon > 180) { + throw new PeliasModelError('invalid boundingBox, property \'lowerRight\.lon\' must be within range -180 to 180'); + } - }; + // geometry must be internally consistent + if (val.upperLeft.lat < val.lowerRight.lat) { + throw new PeliasModelError('invalid boundingBox, upperLeft.lat must be >= lowerRight.lat'); + } + // normalize lon values by adding 360 so that only positives are compared + if (val.upperLeft.lon+360 > val.lowerRight.lon+360) { + throw new PeliasModelError('invalid boundingBox, upperLeft.lon must be <= lowerRight.lon'); + } + + return this; }; From 81678a489ce7082d329c5371b2106d1784189223 Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 6 Jul 2017 13:23:43 +0200 Subject: [PATCH 3/5] refactor: simplify util/transform.js --- Document.js | 22 ++++++++--------- test/util/transform.js | 2 +- util/transform.js | 54 ++++++++++++++++-------------------------- 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/Document.js b/Document.js index d7361cb..8f042a0 100644 --- a/Document.js +++ b/Document.js @@ -90,7 +90,7 @@ Document.prototype.toESDocument = function() { // id Document.prototype.setId = function( id ){ - id = transform.stringify()(id); + id = transform.stringify(id); validate.type('string', id); validate.truthy(id); @@ -119,7 +119,7 @@ Document.prototype.getType = function(){ // source Document.prototype.setSource = function( source ){ - source = transform.lowercase()(source); + source = transform.lowercase(source); validate.type('string', source); validate.truthy(source); @@ -134,7 +134,7 @@ Document.prototype.getSource = function(){ // layer Document.prototype.setLayer = function( layer ){ - layer = transform.lowercase()(layer); + layer = transform.lowercase(layer); validate.type('string', layer); validate.truthy(layer); @@ -149,8 +149,8 @@ Document.prototype.getLayer = function(){ // source id Document.prototype.setSourceId = function( source_id ){ - source_id = transform.stringify()(source_id); - source_id = transform.lowercase()(source_id); + source_id = transform.stringify(source_id); + source_id = transform.lowercase(source_id); validate.type('string', source_id); validate.truthy(source_id); @@ -165,7 +165,7 @@ Document.prototype.getSourceId = function(){ // alpha3 Document.prototype.setAlpha3 = function( alpha3 ){ - alpha3 = transform.uppercase()(alpha3); + alpha3 = transform.uppercase(alpha3); validate.type('string', alpha3); validate.truthy(alpha3); validate.length(3, alpha3); @@ -354,7 +354,7 @@ Document.prototype.getPopulation = function(){ // popularity Document.prototype.setPopularity = function( popularity ){ - popularity = transform.roundify()(popularity); + popularity = transform.roundify(popularity); validate.type('number', popularity); validate.nonnegative(popularity); @@ -369,7 +369,7 @@ Document.prototype.getPopularity = function(){ // longitude Document.prototype.setLon = function( value ){ - value = transform.floatify(6)(value); + value = transform.floatify(6, value); validate.type('number', value); validate.geo('longitude', value); @@ -384,7 +384,7 @@ Document.prototype.getLon = function(){ // latitude Document.prototype.setLat = function( value ){ - value = transform.floatify(6)(value); + value = transform.floatify(6, value); validate.type('number', value); validate.geo('latitude', value); @@ -399,7 +399,7 @@ Document.prototype.getLat = function(){ // categories Document.prototype.addCategory = function( value ){ - value = transform.lowercase()(value); + value = transform.lowercase(value); validate.type('string', value); validate.truthy(value); @@ -454,7 +454,7 @@ Document.prototype.setBoundingBox = function( value ){ validate.type('object', value); validate.boundingBox(value); - value = transform.toULLR()(value); + value = transform.toULLR(value); this.bounding_box = value; return this; diff --git a/test/util/transform.js b/test/util/transform.js index c2151ab..b49fcf0 100644 --- a/test/util/transform.js +++ b/test/util/transform.js @@ -1,4 +1,4 @@ -var toULLR = require('../../util/transform.js').toULLR(); +var toULLR = require('../../util/transform.js').toULLR; module.exports.tests = {}; diff --git a/util/transform.js b/util/transform.js index cc07f3c..22223bd 100644 --- a/util/transform.js +++ b/util/transform.js @@ -1,45 +1,33 @@ -module.exports.uppercase = function(){ - return function( val ){ - return val.toUpperCase(); - }; +module.exports.uppercase = function( val ){ + return val.toUpperCase(); }; -module.exports.lowercase = function(){ - return function( val ){ - return val.toLowerCase(); - }; +module.exports.lowercase = function( val ){ + return val.toLowerCase(); }; -module.exports.stringify = function(){ - return function( val ){ - // because javascript: (''+undefined) === 'undefined' - if( 'undefined' === typeof val ){ - return ''; - } - return '' + val; - }; +module.exports.stringify = function( val ){ + // because javascript: (''+undefined) === 'undefined' + if( 'undefined' === typeof val ){ + return ''; + } + return '' + val; }; -module.exports.floatify = function( precision ){ - return function( val ){ - return parseFloat( val ).toFixed( precision || 10 )/1; - }; +module.exports.floatify = function( precision, val ){ + return parseFloat( val ).toFixed( precision || 10 )/1; }; -module.exports.roundify = function(){ - return function( val ){ - return Math.round(val); - }; +module.exports.roundify = function( val ){ + return Math.round(val); }; -module.exports.toULLR = function() { - return function( val ){ - return JSON.stringify({ - min_lat: val.lowerRight.lat, - max_lat: val.upperLeft.lat, - min_lon: val.upperLeft.lon, - max_lon: val.lowerRight.lon - }); - }; +module.exports.toULLR = function( val ) { + return JSON.stringify({ + min_lat: val.lowerRight.lat, + max_lat: val.upperLeft.lat, + min_lon: val.upperLeft.lon, + max_lon: val.lowerRight.lon + }); }; From 51556193b320666cdabba92d060b489102382d04 Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 6 Jul 2017 13:31:46 +0200 Subject: [PATCH 4/5] refactor: do not initialize all parent fields in constructor --- Document.js | 31 ++++++-------- test/Document.js | 3 -- test/document/toESDocument.js | 3 +- test/serialize/test.js | 77 +---------------------------------- 4 files changed, 15 insertions(+), 99 deletions(-) diff --git a/Document.js b/Document.js index 8f042a0..9d7de60 100644 --- a/Document.js +++ b/Document.js @@ -23,32 +23,14 @@ const parentFields = [ 'postalcode' ]; -// var parentInitJson = function(){ -// var memo = {}; -// parentFields.forEach( (field) => { -// memo[field] = []; -// memo[`${field}_a`] = []; -// memo[`${field}_id`] = []; -// }); -// return JSON.stringify( memo ); -// }(); - function Document( source, layer, source_id ){ this.name = {}; this.phrase = {}; this.parent = {}; - // this.parent = JSON.parse( parentInitJson ); this.address_parts = {}; this.center_point = {}; this.category = []; - // // initialize 'parent' fields to empty arrays - parentFields.forEach( (field) => { - this.parent[field] = []; - this.parent[`${field}_a`] = []; - this.parent[`${field}_id`] = []; - }, this); - // create a non-enumerable property for metadata Object.defineProperty( this, '_meta', { writable: true, value: {} }); this._meta.version = pkg.version; @@ -241,7 +223,13 @@ Document.prototype.delName = function( prop ){ Document.prototype.addParent = function( field, name, id, abbr ){ var add = function( prop, value ){ - // here + + // create new parent array if required + if( !this.parent.hasOwnProperty( prop ) ){ + this.parent[ prop ] = []; + } + + // add value to array if not already present if( -1 === this.parent[prop].indexOf(value) ){ this.parent[prop].push(value); } @@ -295,6 +283,11 @@ Document.prototype.addParent = function( field, name, id, abbr ){ // clear all all added values Document.prototype.clearParent = function(field) { + // field has never been set + if( !this.parent.hasOwnProperty( field ) ){ + return this; + } + this.parent[ field ] = []; this.parent[ field + '_id' ] = []; this.parent[ field + '_a' ] = []; diff --git a/test/Document.js b/test/Document.js index fc481d1..f32575f 100644 --- a/test/Document.js +++ b/test/Document.js @@ -37,9 +37,6 @@ module.exports.tests.constructor = function(test) { // initialize 'parent' fields to empty arrays t.equal(typeof doc.parent, 'object', 'initial value'); - doc.getParentFields().forEach( function(field){ - t.true(Array.isArray(doc.parent[field]), 'initial value'); - }); // setters called t.equal(doc.source, 'mysource', 'setter called'); diff --git a/test/document/toESDocument.js b/test/document/toESDocument.js index f83ead3..c05c4a6 100644 --- a/test/document/toESDocument.js +++ b/test/document/toESDocument.js @@ -28,7 +28,6 @@ module.exports.tests.toESDocument = function(test) { _id: 'myid', data: { layer: 'mylayer', - parent: {}, source: 'mysource', source_id: 'myid' } @@ -40,7 +39,7 @@ module.exports.tests.toESDocument = function(test) { // downstream to elasticsearch. t.false(esDoc.data.hasOwnProperty('address_parts'), 'does not include empty top-level maps'); t.false(esDoc.data.hasOwnProperty('category'), 'does not include empty top-level arrays'); - t.false(esDoc.data.parent.hasOwnProperty('country'), 'does not include empty parent arrays'); + t.false(esDoc.data.hasOwnProperty('parent'), 'does not include empty parent arrays'); t.end(); }); }; diff --git a/test/serialize/test.js b/test/serialize/test.js index 4770bb1..8e9cd6d 100644 --- a/test/serialize/test.js +++ b/test/serialize/test.js @@ -29,47 +29,7 @@ module.exports.tests.minimal = function(test) { 'source_id': 'myid', 'name': {}, 'phrase': {}, - 'parent': { - 'borough': [], - 'borough_a': [], - 'borough_id': [], - 'continent': [], - 'continent_a': [], - 'continent_id': [], - 'country': [], - 'country_a': [], - 'country_id': [], - 'county': [], - 'county_a': [], - 'county_id': [], - 'dependency': [], - 'dependency_a': [], - 'dependency_id': [], - 'localadmin': [], - 'localadmin_a': [], - 'localadmin_id': [], - 'locality': [], - 'locality_a': [], - 'locality_id': [], - 'macrocounty': [], - 'macrocounty_a': [], - 'macrocounty_id': [], - 'macroregion': [], - 'macroregion_a': [], - 'macroregion_id': [], - 'macrohood': [], - 'macrohood_a': [], - 'macrohood_id': [], - 'neighbourhood': [], - 'neighbourhood_a': [], - 'neighbourhood_id': [], - 'region': [], - 'region_a': [], - 'region_id': [], - 'postalcode': [], - 'postalcode_a': [], - 'postalcode_id': [] - }, + 'parent': {}, 'address_parts': {}, 'category': [], 'center_point': {} @@ -136,45 +96,12 @@ module.exports.tests.complete = function(test) { // WOF fields 'parent': { - 'borough': [], - 'borough_a': [], - 'borough_id': [], - 'continent': [], - 'continent_a': [], - 'continent_id': [], 'country': ['Great Britain'], 'country_a': ['GreatB'], 'country_id': ['1001'], - 'county': [], - 'county_a': [], - 'county_id': [], - 'dependency': [], - 'dependency_a': [], - 'dependency_id': [], - 'localadmin': [], - 'localadmin_a': [], - 'localadmin_id': [], - 'locality': [], - 'locality_a': [], - 'locality_id': [], - 'macrocounty': [], - 'macrocounty_a': [], - 'macrocounty_id': [], - 'macroregion': [], - 'macroregion_a': [], - 'macroregion_id': [], - 'macrohood': [], - 'macrohood_a': [], - 'macrohood_id': [], 'neighbourhood': ['Shoreditch'], 'neighbourhood_a': [null], - 'neighbourhood_id': ['2002'], - 'region': [], - 'region_a': [], - 'region_id': [], - 'postalcode': [], - 'postalcode_a': [], - 'postalcode_id': [] + 'neighbourhood_id': ['2002'] }, // geography From a5293ceff8ebfb7bafbdbc3ad006d4bd2c33325e Mon Sep 17 00:00:00 2001 From: missinglink Date: Thu, 6 Jul 2017 13:54:37 +0200 Subject: [PATCH 5/5] refactor: do not set meta.version as it will always be 0.0.0-semantic-release --- Document.js | 2 -- test/Document.js | 1 - 2 files changed, 3 deletions(-) diff --git a/Document.js b/Document.js index 9d7de60..002a241 100644 --- a/Document.js +++ b/Document.js @@ -1,6 +1,5 @@ var config = require('pelias-config').generate(); -var pkg = require('./package'); var validate = require('./util/valid'); var transform = require('./util/transform'); var _ = require('lodash'); @@ -33,7 +32,6 @@ function Document( source, layer, source_id ){ // create a non-enumerable property for metadata Object.defineProperty( this, '_meta', { writable: true, value: {} }); - this._meta.version = pkg.version; // mandatory properties this.setSource( source ); diff --git a/test/Document.js b/test/Document.js index f32575f..9276f7e 100644 --- a/test/Document.js +++ b/test/Document.js @@ -33,7 +33,6 @@ module.exports.tests.constructor = function(test) { t.deepEqual(doc.center_point, {}, 'initial value'); t.true(Array.isArray(doc.category), 'initial value'); t.true(doc.hasOwnProperty('_meta'), 'initial value'); - t.true(doc._meta.hasOwnProperty('version'), 'initial value'); // initialize 'parent' fields to empty arrays t.equal(typeof doc.parent, 'object', 'initial value');