Skip to content
This repository has been archived by the owner on Apr 17, 2020. It is now read-only.

Commit

Permalink
Merge pull request #110 from mcnielsen/master
Browse files Browse the repository at this point in the history
v1.2.18 - Base URLs and merge helper
  • Loading branch information
mcnielsen authored Apr 15, 2020
2 parents 2c17c89 + fbecefc commit 4ae6d76
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@al/common",
"version": "1.2.17",
"version": "1.2.18",
"description": "A collection of lightweight utilities and common types shared across NEPAL libraries and applications based on them.",
"main": "./dist/umd/index.js",
"scripts": {
Expand Down
5 changes: 5 additions & 0 deletions src/locator/al-locator.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ export class AlLocatorMatrix
result = hit.location;
const baseUrl = this.getBaseUrl( targetURI );
if ( baseUrl !== result.uri ) {
console.log(`Notice: application '${hit.location.locTypeId}' instance '${baseUrl}' differs from default '${result.uri}'; updating lookup table.` );
result.originalUri = result.uri;
result.uri = baseUrl;
}
Expand Down Expand Up @@ -581,6 +582,10 @@ export class AlLocatorMatrix
* I make no promises about the quality of this code when confronted with incorrect or incomplete inputs.
*/
protected getBaseUrl( uri:string ):string {
const matches = /(^https?:\/\/[a-zA-Z0-9_\-\.:]+)(.*$)/.exec( uri );
if ( matches ) {
return matches[1];
}
if ( uri.indexOf("#") !== -1 ) {
uri = uri.substring( 0, uri.indexOf("#") );
}
Expand Down
79 changes: 79 additions & 0 deletions src/utility/al-merge-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* A simple utility class for translating data between objects and intermediary representations.
* The most obvious use cases are unpacking a DTO into a class instance, or merging multiple DTOs into a complex model.
* Mostly this class is a structured way of avoiding the endless repetition of "if x.hasOwnProperty( x )".
*/

export class AlMergeHelper {

constructor( public source:any, public target:any ) {
}

/**
* Copies one or more properties from the source into the target.
*/
public copy( ...sourceProperties:string[] ) {
sourceProperties.forEach( sourceProperty => {
if ( sourceProperty in this.source ) {
if ( typeof( this.source[sourceProperty] ) !== 'undefined' ) {
this.target[sourceProperty] = this.source[sourceProperty];
}
}
} );
}

/**
* Copies a property from the source to a different property name in the target.
*/
public rename( sourceProperty:string, targetProperty:string ) {
if ( sourceProperty in this.source ) {
if ( typeof( this.source[sourceProperty] ) !== 'undefined' ) {
this.target[targetProperty] = this.source[sourceProperty];
}
}
}

/**
* Copies an array of properties from source to a different property name in the target.
*/
public renameAll( ...properties:[ string, string ][] ) {
properties.forEach( ( [ sourceProperty, targetProperty ] ) => this.rename( sourceProperty, targetProperty ) );
}

/**
* Transforms a property from the source into a new property in the target.
*/
public transform( sourceProperty:string, targetProperty:string, transformer:{(input:unknown):any} ) {
if ( sourceProperty in this.source ) {
if ( typeof( this.source[sourceProperty] ) !== 'undefined' ) {
this.target[targetProperty] = transformer( this.source[sourceProperty] );
}
}
}

/**
* Executes a callback against a property in the source object.
*/
public with<PropertyType=any>( sourceProperty:string, action:{(value:PropertyType):void}) {
if ( sourceProperty in this.source ) {
if ( typeof( this.source[sourceProperty] ) !== 'undefined' ) {
action( this.source[sourceProperty] );
}
}
}

/**
* Creates a child merge helper that targets a child property.
*/
public descend( sourceProperty:string, targetProperty:string|null, action:{(merger:AlMergeHelper):void} ) {
if ( sourceProperty in this.source ) {
if ( typeof( this.source[sourceProperty] ) !== 'undefined' ) {
if ( targetProperty && ! ( targetProperty in this.target ) ) {
this.target[targetProperty] = {};
}
const target = targetProperty ? this.target[targetProperty] : this.target;
action( new AlMergeHelper( this.source[sourceProperty], target ) );
}
}
}
}
1 change: 1 addition & 0 deletions src/utility/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { isPromiseLike } from './is-promise-like';
export { getJsonPath, setJsonPath, deepMerge } from './json-utilities';
export * from './al-trigger.types';
export * from './al-query-evaluator.types';
export * from './al-merge-helper';
2 changes: 1 addition & 1 deletion test/al-locator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ describe( 'AlLocatorMatrix', () => {
console.log(`Average lookup time: ${averageLookup}ms (${AlLocatorMatrix.totalSeeks} lookups)` );

// Average lookup time SHOULD be less than 0.1 ms (actually, a great deal faster than that). If it's slower, something is wrong!
expect( averageLookup ).to.be.below( 0.1 );
expect( averageLookup ).to.be.below( 0.2 );
} );
} );

Expand Down
117 changes: 117 additions & 0 deletions test/al-merge-helper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { expect } from 'chai';
import { describe, before } from 'mocha';
import { AlMergeHelper } from '../src/utility/al-merge-helper';
import * as sinon from 'sinon';

describe( `AlMergeHelper`, () => {

let source:any;
let target:any;
let merger:AlMergeHelper;

beforeEach( () => {
source = {
a: 1,
b: 2,
c: "three",
d: null,
f: true,
z: undefined,
children: {
eldest: {
"name": "Pepe",
"age": 40
},
middle: {
"name": "Pablo",
"age": 37
},
youngest: {
"name": "Leo",
"age": 35
}
}
};
target = {};
merger = new AlMergeHelper( source, target );
} );

it( `'copy' should copy data if it exists and ignore it if it does not`, () => {
merger.copy( "a", "b", "c", "d", "e", "y", "z" );
expect( target ).to.deep.equal( {
a: 1,
b: 2,
c: "three",
d: null
} );
} );
it( `'rename/renameAll' should rename data if it exists and ignore it if it does not`, () => {
merger.rename( "a", "ay" );
merger.rename( "b", "bee" );
merger.rename( "y", "ye" );
merger.rename( "z", "zed" );
merger.renameAll( [ "c", "see" ], [ "d", "duh" ] );
expect( target ).to.deep.equal( {
ay: 1,
bee: 2,
see: "three",
duh: null
} );
} );
it( `'transform' should transform data if it exists and ignore it if it does not`, () => {
merger.transform( "a", "aye", e => e.toString() );
merger.transform( "b", "be", ( e:number ) => e - 3 );
merger.transform( "e", "eeeeeeeek", e => { throw new Error("This should never be called, because the property doesn't exist on the source." ); } );
merger.transform( "f", "foo", ( e:boolean ) => ! e );
merger.transform( "y", "yeeeeeeeek", e => { throw new Error("This should never be called, because the property doesn't exist on the source." ); } );
merger.transform( "zed", "zaaaaaap", e => { throw new Error("This should never be called, because the property doesn't exist on the source." ); } );
expect( target ).to.deep.equal( {
aye: "1",
be: -1,
foo: false
} );
} );
it( `'with' should call a helper function with a property if it exists`, () => {
let called = 0;
merger.with( "a", ( a ) => {
expect( a ).to.equal( 1 );
called++;
} );
merger.with( "f", ( f ) => {
expect( f ).to.equal( true );
called++;
} );
merger.with( "e", ( e ) => {
throw new Error("Failure hurts!" );
} );
merger.with( "z", ( z ) => {
throw new Error("Failure hurts!" );
} );
expect( called ).to.equal( 2 );
} );

it( `'descend()' should call a helper function with a new AlMergeHelper instance`, () => {
merger.descend( "children", "mcevoy_salgado_progeny", cmerge => {
cmerge.renameAll( [ "eldest", "pep" ], [ "middle", "pablito" ], [ "youngest", "leo" ] );
} );
merger.descend( "nonexistent", null, m => {
throw new Error("This should never be called.");
} );
expect( target ).to.deep.equal( {
mcevoy_salgado_progeny: {
pep: {
"name": "Pepe",
"age": 40
},
pablito: {
"name": "Pablo",
"age": 37
},
leo: {
"name": "Leo",
"age": 35
}
}
} );
} );
} );

0 comments on commit 4ae6d76

Please sign in to comment.