Skip to content

Commit

Permalink
feat(OpaqueToken): added OpaqueToken for creating const string tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
timkindberg committed Oct 17, 2015
1 parent 5ce7124 commit 0563cb5
Show file tree
Hide file tree
Showing 12 changed files with 43 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import events from './util/events';
// Takes an array of bindings and separates it into decorated classes and string
// names. Usually these string names are the names of angular modules.
import filterProviders from './util/filter-bindings';
import {Provider} from './util/provider';
import {Provider} from './classes/provider';

// ## Bundle
// The bundle function. Pass it the name of the module you want to generate, the root
Expand Down
2 changes: 1 addition & 1 deletion src/bundle.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Component} from './decorators/providers/component';
import {Directive} from './decorators/providers/directive';
import {Inject} from './decorators/inject';
import {DecoratedModule} from './module';
import {Provider} from './util/provider';
import {Provider} from './classes/provider';

let fooProvider = new Provider('foo', {useValue: 'bar'});

Expand Down
4 changes: 4 additions & 0 deletions src/classes/opaque-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class OpaqueToken {
constructor(_desc) { this._desc = _desc }
toString() { return `Token ${this._desc}`; }
}
7 changes: 3 additions & 4 deletions src/util/provider.js → src/classes/provider.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { appWriter, providerWriter } from '../writers';
import Module from '../module';
import {Inject} from '../decorators/inject';
import {getInjectableNameWithJitCreation} from './get-injectable-name';
import {getInjectableNameWithJitCreation} from './../util/get-injectable-name';
import extend from 'extend';

const TYPE = 'provider';
Expand All @@ -21,11 +21,10 @@ export class Provider {
} catch (e) {
throw new Error('new Provider() Error: Invalid token');
}
try {

extend(this, {useClass, useValue, useConstant, useFactory});
extend(this, {useClass, useValue, useConstant, useFactory});

} catch (e) {
if (!useClass && !useValue && !useConstant && !useFactory) {
throw new Error('new Provider() Error: No usage provided (i.e. useClass, useValue, useConstant, useFactory)')
}

Expand Down
110 changes: 21 additions & 89 deletions src/util/provider.spec.js → src/classes/provider.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import bootstrap from '../bootstrap';
import { Inject } from '../decorators/inject';
import { Component } from '../decorators/providers/component';
import { TestComponentBuilder, providers } from '../tests';
import { OpaqueToken } from '../classes/opaque-token';

class SomeToken {}

Expand All @@ -22,19 +23,23 @@ describe('Provider Class', () => {
});

it('binds a decorated service token', () => {
let b = new Provider(SomeToken, {});
let b = new Provider(SomeToken, {useValue:true});
b.token.should.equal('someToken');
});

it('binds a string-based service token', () => {
let b = new Provider('$http', {});
let b = new Provider('$http', {useValue:true});
b.token.should.equal('$http');
});

it('throws Invalid token if no token provided', () => {
() => new Provider().should.throw;
});

it('throws No usage provided if no use method is provided', () => {
() => new Provider('foo').should.throw;
});

it('binds to a value', () => {
let b = new Provider(SomeToken, { useValue: someValue });
b.useValue.should.equal('val');
Expand Down Expand Up @@ -84,7 +89,7 @@ describe('angular integration', () => {
angular = ng.useReal();
});

describe('Providers with String Tokens', () => {
describe('Provider\'s "use" Methods', () => {

it('creates an angular value with useVal', () => {
buildRootTestWithProvider(new Provider('foo', { useValue: 'bar' }));
Expand Down Expand Up @@ -168,99 +173,26 @@ describe('angular integration', () => {
});
});

describe('Providers with Class Tokens', () => {

let f;

beforeEach(() => {
f = class Foo {};
});

it('creates an angular value with useVal', () => {
buildRootTestWithProvider(new Provider(f, { useValue: 'bar' }));
let name = providerWriter.get('name', f);
root.debugElement.getLocal(name).should.be.eql('bar');
});

it('creates an angular constant with useConstant', () => {
buildRootTestWithProvider(new Provider(f, { useConstant: 'bar' }));
let name = providerWriter.get('name', f);
root.debugElement.getLocal(name).should.be.eql('bar');
});

it('creates an angular service with useClass', () => {
class Bar {
constructor() {
this.property = 'bar';
}
method() {
return 'baz';
}
}

buildRootTestWithProvider(new Provider(f, { useClass: Bar }));
let name = providerWriter.get('name', f);

let foo = root.debugElement.getLocal(name);
foo.should.be.an.instanceOf(Bar);
foo.property.should.be.eql('bar');
foo.method().should.be.eql('baz');
});

it('creates an injected angular service with useClass', () => {
@Inject('$q')
class Bar {
constructor($q) {
this.$q = $q;
}
}

buildRootTestWithProvider(new Provider(f, { useClass: Bar }));
let name = providerWriter.get('name', f);

let foo = root.debugElement.getLocal(name);
foo.should.be.an.instanceOf(Bar);
foo.$q.should.be.eql(root.debugElement.getLocal('$q'));
foo.$q.should.have.property('resolve');
});

it('creates an angular factory from a function with useFactory', () => {
function getBar() {
return 'bar';
}
describe('Provider Tokens', () => {

buildRootTestWithProvider(new Provider(f, { useFactory: getBar }));
let name = providerWriter.get('name', f);
it('supports string tokens', () => {
buildRootTestWithProvider(new Provider('foo', { useValue: 'bar' }));

root.debugElement.getLocal(name).should.eql('bar');
root.debugElement.getLocal('foo').should.eql('bar');
});

it('creates an angular factory with ng1 dependencies from a function with useFactory', () => {
function getQ($q) {
return $q;
}

buildRootTestWithProvider(new Provider(f, { useFactory: getQ, deps: ['$q'] }));
let name = providerWriter.get('name', f);
it('supports OpaqueToken tokens', () => {
let t = new OpaqueToken('foo');
buildRootTestWithProvider(new Provider(t, { useValue: 'bar' }));

root.debugElement.getLocal(name).should.eql(root.debugElement.getLocal('$q'));
root.debugElement.getLocal(t).should.eql('bar');
});

it.skip('creates an angular factory with class dependencies from a function with useFactory', () => {
function getBar(foo) {
return foo.bar;
}

class Foo {
constructor() {
this.bar = 'bar';
}
}

buildRootTestWithProvider(new Provider(f, { useFactory: getBar, deps: [Foo] }));

root.debugElement.getLocal('foo').should.eql('bar');
it('supports class tokens', () => {
class Foo {}
buildRootTestWithProvider(new Provider(Foo, { useValue: 'bar' }));
let name = providerWriter.get('name', Foo);
root.debugElement.getLocal(name).should.be.eql('bar');
});
});

});
2 changes: 1 addition & 1 deletion src/decorators/providers/component.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Component} from './component';
import '../../tests/frameworks';
import {ng} from '../../tests/angular';
import {providerWriter, componentWriter, appWriter} from '../../writers';
import {Provider} from '../../util/provider';
import {Provider} from '../../classes/provider';
import events from '../../util/events';
import Module from '../../module';

Expand Down
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import bootstrap from './bootstrap';
import { Inject } from './decorators/inject';
import { Injectables } from './decorators/injectables';
import { EventEmitter } from './util/event-emitter';
import { provide, Provider } from './util/provider';
import { provide, Provider } from './classes/provider';
import { OpaqueToken } from './classes/opaque-token';

import { Animation } from './decorators/providers/animation';
import { Component } from './decorators/providers/component';
Expand All @@ -28,6 +29,7 @@ export {
EventEmitter,
provide,
Provider,
OpaqueToken,

Component,
Directive,
Expand Down
2 changes: 1 addition & 1 deletion src/tests/providers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { provide } from '../util/provider';
import { provide } from '../classes/provider';

/**
* Stores references to all bindings. Is cleared by TestComponentBuilder after a create call.
Expand Down
2 changes: 1 addition & 1 deletion src/util/filter-bindings.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {providerWriter} from '../writers';
import flattenArray from './flatten-array';
import {Provider} from './provider';
import {Provider} from './../classes/provider';

const STRING_TEST = a => typeof a === 'string';
const PROVIDER_TEST = a => (typeof a === 'function' || a instanceof Provider) && providerWriter.has('name', a);
Expand Down
2 changes: 1 addition & 1 deletion src/util/filter-bindings.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import '../tests/frameworks';
import filterProviders from './filter-bindings';
import {providerWriter} from '../writers';
import {Provider} from '../util/provider';
import {Provider} from '../classes/provider';

const Test = t => {
providerWriter.set('type', 'test', t);
Expand Down
5 changes: 3 additions & 2 deletions src/util/get-injectable-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { providerWriter } from '../writers';
// Import the `@Service` decorator. We'll apply it to functions/classes that are
// injected that are missing provider metadata. Convenience!
import { Service } from '../decorators/providers/service';
import { OpaqueToken } from '../classes/opaque-token';

export const getInjectableName = (injectable) => {
// Return it if it is already a string like `'$http'` or `'$state'`
if(typeof injectable === 'string') {
return injectable;
if(typeof injectable === 'string' || injectable instanceof OpaqueToken) {
return injectable.toString();
}
// If the injectable is not a string but has provider information, use
// the provider name. This is set by the collection of provider decorators
Expand Down
6 changes: 3 additions & 3 deletions src/util/get-injectable-name.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('getInjectableName', function(){
class Foo {}
providerWriter.set('type', 'foo', Foo);
providerWriter.set('name', 'foo', Foo);
getInjectableName(Foo).should.eql('foo');
getInjectableName(Foo).should.contain('foo');
});
});

Expand All @@ -25,11 +25,11 @@ describe('getInjectableNameWithJitCreation', function(){
class Foo {}
providerWriter.set('type', 'foo', Foo);
providerWriter.set('name', 'foo', Foo);
getInjectableNameWithJitCreation(Foo).should.eql('foo');
getInjectableNameWithJitCreation(Foo).should.contain('foo');
});

it('registers normal classes as a Service and then returns their name', function(){
class Foo {}
getInjectableNameWithJitCreation(Foo).should.eql('Foo');
getInjectableNameWithJitCreation(Foo).should.contain('Foo');
});
});

0 comments on commit 0563cb5

Please sign in to comment.