This repository has been archived by the owner on Apr 17, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #110 from mcnielsen/master
v1.2.18 - Base URLs and merge helper
- Loading branch information
Showing
6 changed files
with
204 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ) ); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} ); | ||
} ); | ||
} ); |