Skip to content

Commit

Permalink
feat(reflection): create reflector for resolving all metadata on Type
Browse files Browse the repository at this point in the history
- move private metadata handling logic to reflector
  • Loading branch information
Hotell committed Dec 26, 2015
1 parent 010785e commit 637e54c
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 137 deletions.
1 change: 1 addition & 0 deletions ng-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import './test/providers.spec';

import './test/di/decorators.spec';
import './test/util/decorators.spec';
import './test/reflection/reflection.spec';

describe( 'ng-metadata', ()=> {

Expand Down
7 changes: 7 additions & 0 deletions src/reflection/reflection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {Reflector} from './reflector';

/**
* The {@link Reflector} used internally in Angular to access metadata
* about symbols.
*/
export var reflector = new Reflector();
96 changes: 96 additions & 0 deletions src/reflection/reflector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {Type,isPresent,isFunction} from "../facade/lang";

// This will be needed when we will used Reflect APIs
/*const Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}*/

/**
* @internal
* @private
* @type {string}
*/
export const CLASS_META_KEY = '__mAnnotations';
/**
* @internal
* @private
* @type {string}
*/
export const PARAM_META_KEY = '__mParameters';
/**
* @internal
* @private
* @type {string}
*/
export const PROP_META_KEY = '__mPropMetadata';


/**
* Provides access to reflection data about symbols. Used internally by Angular
* to power dependency injection and compilation.
*/
export class Reflector {

parameters( typeOrFunc: Type ): any[][] {
//return Reflect.getMetadata('parameters', cls);
return extractParameter( typeOrFunc );
}

registerParameters( parameters, type: Type ): void {
//Reflect.defineMetadata('parameters', parameters, cls);
type[ PARAM_META_KEY ] = parameters;
}

annotations( typeOrFunc: Type ): any[] {
//return Reflect.getOwnMetadata('annotations', cls);
return extractAnnotation( typeOrFunc );
}

registerAnnotation( annotations, type: Type ): void {
//Reflect.defineMetadata('annotations', annotations, cls);
type[ CLASS_META_KEY ] = annotations;
}

propMetadata( typeOrFunc: Type ): {[key: string]: any[]} {
//return Reflect.getOwnMetadata('propMetadata', target.constructor);
return extractProperty( typeOrFunc );
}

registerPropMetadata( propMetadata, type: Type ): void {
//Reflect.defineMetadata('propMetadata', meta, target.constructor);
type.constructor[ PROP_META_KEY ] = propMetadata;
}

}


function extract( metaKey: string ) {

return function ( cls: any ): any {

if ( isFunction( cls ) && cls.hasOwnProperty( metaKey ) ) {
// it is a decorator, extract annotation
return cls[ metaKey ];
}

}

}

function extractAnnotation( cls: any ): any[] {

return extract( CLASS_META_KEY )( cls );

}

function extractParameter( cls: any ): any[][] {

return extract( PARAM_META_KEY )( cls );

}
function extractProperty( cls: any ): {[name:string]:any[]} {

return extract( PROP_META_KEY )( cls );

}
82 changes: 8 additions & 74 deletions src/util/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
import {global, Type, isFunction, isString, isPresent} from '../facade/lang';


/**
* @internal
* @private
* @type {string}
*/
export const CLASS_META_KEY = '__mAnnotations';
/**
* @internal
* @private
* @type {string}
*/
export const PARAM_META_KEY = '__mParameters';
/**
* @internal
* @private
* @type {string}
*/
export const PROP_META_KEY = '__mPropMetadata';
import {reflector} from '../reflection/reflection';

/**
* An interface implemented by all Angular type decorators,
Expand All @@ -45,41 +26,6 @@ export interface TypeDecorator {

}

function extract( metaKey: string ) {

return function ( cls: any ): any {

if ( isFunction( cls ) && cls.hasOwnProperty( metaKey ) ) {
// it is a decorator, extract annotation
return cls[ metaKey ];
}

}

}
export function extractAnnotation( cls: any ): any {

return extract( CLASS_META_KEY )(cls);

}

export function extractParameter( cls: any ): any {

return extract( PARAM_META_KEY )(cls);

}
export function extractProperty( cls: any ): any {

return extract( PROP_META_KEY )(cls);

}

// This will be needed when we will used Reflect APIs
/*const Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}*/


export function makeDecorator(
AnnotationCls: any,
Expand All @@ -103,22 +49,16 @@ export function makeDecorator(

function TypeDecorator( cls ): TypeDecorator {

//var annotations = Reflect.getOwnMetadata('annotations', cls);
var annotations = cls[ CLASS_META_KEY ];
let annotations = reflector.annotations(cls);

annotations = annotations || [];
annotations.push( annotationInstance );

//Reflect.defineMetadata('annotations', annotations, cls);
cls[ CLASS_META_KEY ] = annotations;
reflector.registerAnnotation(annotations,cls);

return cls;

}

//TypeDecorator.annotations = chainAnnotation;
//TypeDecorator.Class = Class;

if ( chainFn ) {
chainFn( TypeDecorator );
}
Expand Down Expand Up @@ -168,8 +108,7 @@ export function makeParamDecorator( annotationCls, overrideParamDecorator: Funct

}

//var parameters: any[][] = Reflect.getMetadata('parameters', cls);
var parameters: any[][] = cls[ PARAM_META_KEY ];
let parameters: any[][] = reflector.parameters(cls);
parameters = parameters || [];

// there might be gaps if some in between parameters do not have annotations.
Expand All @@ -180,11 +119,10 @@ export function makeParamDecorator( annotationCls, overrideParamDecorator: Funct

parameters[ index ] = parameters[ index ] || [];

var annotationsForParam: any[] = parameters[ index ];
const annotationsForParam: any[] = parameters[ index ];
annotationsForParam.push( annotationInstance );

//Reflect.defineMetadata('parameters', parameters, cls);
cls[ PARAM_META_KEY ] = parameters;
reflector.registerParameters(parameters,cls);

return cls;

Expand Down Expand Up @@ -213,19 +151,15 @@ export function makePropDecorator( decoratorCls ): any {

return function PropDecorator( target: any, name: string ) {


//var meta = Reflect.getOwnMetadata('propMetadata', target.constructor);
var meta = target.constructor[ PROP_META_KEY ];
let meta = reflector.propMetadata(target);

meta = meta || {};
meta[ name ] = meta[ name ] || [];
meta[ name ].unshift( decoratorInstance );

reflector.registerPropMetadata(meta,target);


//Reflect.defineMetadata('propMetadata', meta, target.constructor);
target.constructor[ PROP_META_KEY ] = meta;

};

}
Expand Down
21 changes: 9 additions & 12 deletions test/di/decorators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ import {
SelfMetadata,
SkipSelfMetadata
} from '../../src/di/metadata';
import {
CLASS_META_KEY,
PARAM_META_KEY
} from '../../src/util/decorators';
import { reflector } from '../../src/reflection/reflection';
import {isArray,isBlank,isPresent} from '../../src/facade/lang'

describe( 'di/decorators', () => {
Expand All @@ -31,7 +28,7 @@ describe( 'di/decorators', () => {
class Test {
}

expect( Array.isArray( Test[ CLASS_META_KEY ] ) ).to.equal( true );
expect( Array.isArray( reflector.annotations( Test ) ) ).to.equal( true );

} );

Expand All @@ -55,21 +52,21 @@ describe( 'di/decorators', () => {
} );
it( 'should create param metadata on class', () => {

expect( Array.isArray( cls[ PARAM_META_KEY ] ) ).to.equal( true );
expect( Array.isArray( reflector.parameters(cls) ) ).to.equal( true );

} );
it( 'should create 2 dimensional param metadata', () => {

const [paramOne,paramTwo] = cls[ PARAM_META_KEY ];
const [paramOne,paramTwo] = reflector.parameters(cls);

expect( cls[ PARAM_META_KEY ].length ).to.equal( 2 );
expect( reflector.parameters(cls).length ).to.equal( 2 );
expect( paramOne.length ).to.equal( 1 );
expect( paramTwo.length ).to.equal( 2 );

} );
it( 'should put to proper indexes proper paramDecorator instance', () => {

const [paramOne,paramTwo] = cls[ PARAM_META_KEY ];
const [paramOne,paramTwo] = reflector.parameters(cls);

expect( paramOne[ 0 ] instanceof InjectMetadata ).to.equal( true );
expect( paramTwo[ 0 ] instanceof InjectMetadata ).to.equal( true );
Expand Down Expand Up @@ -104,7 +101,7 @@ describe( 'di/decorators', () => {

it( 'should not add instance to PARAM_META_KEY if used on non constructor', () => {

expect( isBlank( cls[ PARAM_META_KEY ] ) ).to.equal( true );
expect( isBlank( reflector.parameters(cls) ) ).to.equal( true );

} );

Expand Down Expand Up @@ -143,8 +140,8 @@ describe( 'di/decorators', () => {

const cls = TestBothInjectProvider;

expect( isBlank( cls[ PARAM_META_KEY ] ) ).to.equal( false );
expect( cls[ PARAM_META_KEY ][0][0] instanceof InjectMetadata ).to.equal( true );
expect( isBlank( reflector.parameters(cls) ) ).to.equal( false );
expect( reflector.parameters(cls)[0][0] instanceof InjectMetadata ).to.equal( true );
expect( isBlank( cls.$inject ) ).to.equal( true );

expect( isArray( cls.prototype.$get.$inject ) ).to.equal( true );
Expand Down
74 changes: 74 additions & 0 deletions test/reflection/reflection.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {expect} from 'chai';
import {makePropDecorator} from '../../src/util/decorators';
import {Injectable} from "../../src/di/decorators";
import {Inject} from "../../src/di/decorators";
import {InjectableMetadata} from "../../src/di/metadata";
import {Host} from "../../src/di/decorators";
import {HostMetadata} from "../../src/di/metadata";
import {assign} from "../../src/facade/lang";
import {InjectMetadata} from "../../src/di/metadata";
import {reflector} from "../../src/reflection/reflection";


describe( `reflection/reflector`, ()=> {

it( `should extract class annotation if present`, ()=> {

@Injectable()
class Test{}

const actual = reflector.annotations(Test);
const expected = [InjectableMetadata.prototype];

expect(actual).to.deep.equal(expected);

} );
it( `should extract class property metadata if present`, ()=> {

class FooMetadata{
toString(): string { return `@Foo()`; }
}
const Foo = makePropDecorator(FooMetadata);

class Test{
@Foo() jedi: string;

constructor(){
this.jedi = 'Obi-wan Kenobi';
}
}



const actual = reflector.propMetadata(Test);
const expected = { jedi: [ FooMetadata.prototype ] };

expect(actual).to.deep.equal(expected);

} );
it( `should extract constructor params metadata if present`, ()=> {

function _createProto( Type, props ) {
const instance = Object.create(Type.prototype);
return assign(instance,props);
}
class Test{
constructor(
@Inject('$http') private $http: ng.IHttpService,
@Inject('$log') private $log: ng.ILogService,
@Host() @Inject('ngModel') ngModel: ng.INgModelController
){}
}

const actual = reflector.parameters(Test);
const expected = [
[_createProto(InjectMetadata,{token:'$http'})],
[_createProto(InjectMetadata,{token:'$log'})],
[_createProto(InjectMetadata,{token:'ngModel'}),_createProto(HostMetadata,null)]
];

expect(actual).to.deep.equal(expected);

} );

} );
Loading

0 comments on commit 637e54c

Please sign in to comment.