This commit is contained in:
crusader 2017-12-27 19:41:52 +09:00
parent d3d477d8d1
commit b1ec678681
26 changed files with 504 additions and 91 deletions

View File

@ -1,52 +1,109 @@
import { import {
Class, Class,
Method,
} from '@overflow/commons/core/reflect'; } from '@overflow/commons/core/reflect';
import { ClassType } from '@overflow/commons/core/type'; import { ClassType } from '@overflow/commons/core/type';
import { ApplicationContext } from './context'; import { ApplicationContext } from './context';
import { import {
InjectableClass, ConfigurationAnnotation,
InjectableFactory, InstanceAnnotation,
InjectableValue, RunnerAnnotation,
WebApplicationAnnotation, WebApplicationAnnotation,
} from './decorators'; } from './decorators';
import { InstanceDefinition } from '@overflow/commons/di/factory';
export class Application { export class Application {
private primaryClass: ClassType; private _primaryClass: ClassType;
private applicationContext: ApplicationContext; private _applicationContext: ApplicationContext;
public static run(primaryClass: ClassType): void { public static async run(primaryClass: ClassType): Promise<ApplicationContext> {
let app: Application = new Application(primaryClass); let app: Application = new Application(primaryClass);
return app.run();
} }
public constructor(primaryClass: ClassType) { public constructor(primaryClass: ClassType) {
this.primaryClass = primaryClass; this._primaryClass = primaryClass;
this.createContext(); this.createContext();
} }
private createContext(): void { /**
let clazz: Class = Class.forClass(this.primaryClass); * run
*/
public async run(): Promise<ApplicationContext> {
let runner = this.findRunner();
if (null === runner) {
throw new Error(`There is not exist @Runner on Application[${this._primaryClass.name}]`);
}
let instanceFactory = this._applicationContext.instanceFactory;
let instance = instanceFactory.getInstance(null, this._primaryClass);
await runner.invoke(instance);
return this._applicationContext;
}
private findRunner(): Method {
let clazz: Class = Class.forClass(this._primaryClass);
let methods = clazz.getMethods();
for (let i = 0; i < methods.length; i++) {
const method = methods[i];
let runnerAnnotation = method.getOwnAnnotation(RunnerAnnotation);
if (undefined === runnerAnnotation) {
continue;
}
return method;
}
return null;
}
private createContext(): ApplicationContext {
let clazz: Class = Class.forClass(this._primaryClass);
let wa: WebApplicationAnnotation = clazz.getOwnAnnotation(WebApplicationAnnotation); let wa: WebApplicationAnnotation = clazz.getOwnAnnotation(WebApplicationAnnotation);
if (undefined === wa) { if (undefined === wa) {
console.warn(`Class is not WebApplication type. add @WebApplication annotation to class[${this.primaryClass.name}]`); throw new Error(`Class is not WebApplication type. add @WebApplication annotation to class[${this._primaryClass.name}]`);
return;
} }
let context: ApplicationContext = new ApplicationContext(); this._applicationContext = new ApplicationContext(wa.attributes.injectables);
// context.registerInjectables(wa.attributes.injectables);
wa.attributes.injectables.forEach(injectable => { this.registerJSONSources(wa.attributes.jsonSources);
if (undefined !== (<InjectableClass>injectable).injectableType) { this.registerConfigurations(wa.attributes.configurations);
context.registerInjectable()
} else if (undefined !== (<InjectableFactory>injectable).injectableFactory) { return this._applicationContext;
console.log(`InjectableFactory`); }
} else if (undefined !== (<InjectableValue>injectable).injectableValue) {
console.log(`InjectableValue`); private registerJSONSources(jsonSources: any[]): void {
} else if (typeof injectable === 'function') { if (undefined === jsonSources || null === jsonSources) {
console.log(`Class`); return;
}
let instanceFactory = this._applicationContext.instanceFactory;
let jsons = Object.assign({}, ...jsonSources);
instanceFactory.registerJSON(jsons);
}
private registerConfigurations(configurationTypes: ClassType[]): void {
if (undefined === configurationTypes || null === configurationTypes) {
return;
}
let instanceFactory = this._applicationContext.instanceFactory;
configurationTypes.forEach(configurationType => {
let clazz: Class = Class.forClass(configurationType);
let configAnnotation: ConfigurationAnnotation = clazz.getOwnAnnotation(ConfigurationAnnotation);
if (undefined === configAnnotation) {
throw new Error(`Class is not Configuration type. add @Configuration annotation to class[${configurationType.name}]`);
} }
let instance = instanceFactory.getInstance(null, configurationType);
clazz.getMethods().forEach(method => {
let instanceAnnotation = method.getOwnAnnotation(InstanceAnnotation);
if (undefined === instanceAnnotation) {
return;
}
let singletonName = null === instanceAnnotation.attributes.name ? method.getName() : instanceAnnotation.attributes.name;
let result = method.invoke(instance);
instanceFactory.registerSingleton(singletonName, method.getReturnType(), result);
});
}); });
} }
} }

View File

@ -1,3 +1,40 @@
import {
ClassType,
} from '@overflow/commons/core/type';
import {
InstanceDefinition,
InstanceFactory,
} from '@overflow/commons/di/factory';
export class ApplicationContext { export class ApplicationContext {
private _instanceFactory: InstanceFactory;
public constructor(injectables: ClassType[]) {
this._instanceFactory = new InstanceFactory();
this.initializeInjectables(injectables);
}
private initializeInjectables(injectables: ClassType[]): void {
if (undefined === injectables) {
return;
}
injectables.forEach(injectable => {
let name = injectable.name;
let definition: InstanceDefinition = new InstanceDefinition();
definition.instanceClass = injectable;
definition.name = name;
this._instanceFactory.registerInstanceDefinition(name, definition);
});
}
/**
* instanceFactory
*/
public get instanceFactory(): InstanceFactory {
return this._instanceFactory;
}
} }

View File

@ -1,2 +1,4 @@
export * from './configuration'; export * from './configuration';
export * from './instance';
export * from './runner';
export * from './web_application'; export * from './web_application';

View File

@ -0,0 +1,47 @@
import {
Annotation,
Decorator,
} from '@overflow/commons/core/reflect';
import {
PropertyKeyType,
TypeUtil,
} from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export interface InstanceAnnotationAttributes {
name?: InstanceNameType;
}
export class InstanceAnnotation implements Annotation {
public readonly attributes: InstanceAnnotationAttributes;
public constructor(name: InstanceNameType | InstanceNameType[] | InstanceAnnotationAttributes) {
if (undefined === name) {
this.attributes = {
name: null,
};
return;
}
if (TypeUtil.isString(name)) {
this.attributes = {
name: <InstanceNameType>name,
};
} else {
this.attributes = <InstanceAnnotationAttributes>name;
}
}
public methodDecorator<T>(target: Object, propertyKey: PropertyKeyType,
descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void {
console.log('Instance');
}
}
export const Instance = Decorator.create(InstanceAnnotation);

View File

@ -0,0 +1,19 @@
import {
Annotation,
Decorator,
} from '@overflow/commons/core/reflect';
import {
PropertyKeyType,
} from '@overflow/commons/core/type';
export class RunnerAnnotation implements Annotation {
public methodDecorator<T>(target: Object, propertyKey: PropertyKeyType,
descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void {
console.log('Runner');
}
}
export const Runner = Decorator.create(RunnerAnnotation);

View File

@ -8,25 +8,9 @@ import {
PropertyKeyType, PropertyKeyType,
} from '@overflow/commons/core/type'; } from '@overflow/commons/core/type';
export interface InjectableClass {
injectType: ClassType;
injectableType: ClassType;
}
export interface InjectableFactory {
injectType: ClassType;
injectableFactory: (...params: any[]) => any;
}
export interface InjectableValue {
injectName: PropertyKeyType;
injectableValue: any;
}
export type InjectablesType = (ClassType | InjectableClass | InjectableFactory | InjectableValue)[];
export interface WebApplicationAnnotationAttributes { export interface WebApplicationAnnotationAttributes {
injectables?: InjectablesType; jsonSources?: any[];
injectables?: ClassType[];
configurations?: ClassType[]; configurations?: ClassType[];
} }

View File

@ -0,0 +1 @@
export * from './type';

View File

@ -0,0 +1,22 @@
// import {
// ClassType,
// } from '@overflow/commons/core/type';
// import { InstanceNameType } from '@overflow/commons/di/type';
// export interface InjectableClass {
// injectType: ClassType;
// injectableType: ClassType;
// }
// export interface InjectableFactory {
// injectType: ClassType;
// injectableFactory: (...params: any[]) => any;
// }
// export interface InjectableValue {
// injectName: InstanceNameType;
// injectableValue: any;
// }
// export type InjectablesType = ClassType | InjectableClass | InjectableFactory | InjectableValue;

View File

@ -1,3 +1,6 @@
export * from './inject'; export * from './inject';
export * from './injectable'; export * from './injectable';
export * from './post_construct';
export * from './pre_destroy';
export * from './resource'; export * from './resource';
export * from './value';

View File

@ -8,15 +8,18 @@ import {
TypeUtil, TypeUtil,
} from '@overflow/commons/core/type'; } from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export interface InjectAnnotationAttributes { export interface InjectAnnotationAttributes {
name?: string[]; name?: InstanceNameType[];
} }
export class InjectAnnotation implements Annotation { export class InjectAnnotation implements Annotation {
public readonly attributes: InjectAnnotationAttributes; public readonly attributes: InjectAnnotationAttributes;
public constructor(name: string | string[] | InjectAnnotationAttributes) { public constructor(name: InstanceNameType | InstanceNameType[] | InjectAnnotationAttributes) {
if (undefined === name) { if (undefined === name) {
this.attributes = { this.attributes = {
@ -26,11 +29,11 @@ export class InjectAnnotation implements Annotation {
if (TypeUtil.isString(name)) { if (TypeUtil.isString(name)) {
this.attributes = { this.attributes = {
name: [<string>name], name: [<InstanceNameType>name],
}; };
} else if (TypeUtil.isArray(name)) { } else if (TypeUtil.isArray(name)) {
this.attributes = { this.attributes = {
name: <string[]>name, name: <InstanceNameType[]>name,
}; };
} else { } else {
this.attributes = <InjectAnnotationAttributes>name; this.attributes = <InjectAnnotationAttributes>name;

View File

@ -8,14 +8,18 @@ import {
TypeUtil, TypeUtil,
} from '@overflow/commons/core/type'; } from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export interface InjectableAnnotationAttributes { export interface InjectableAnnotationAttributes {
name?: string[]; name?: InstanceNameType[];
} }
export class InjectableAnnotation implements Annotation { export class InjectableAnnotation implements Annotation {
public readonly attributes: InjectableAnnotationAttributes; public readonly attributes: InjectableAnnotationAttributes;
public constructor(name: string | string[] | InjectableAnnotationAttributes) { public constructor(name: InstanceNameType | InstanceNameType[] | InjectableAnnotationAttributes) {
if (undefined === name) { if (undefined === name) {
this.attributes = { this.attributes = {
@ -25,7 +29,7 @@ export class InjectableAnnotation implements Annotation {
if (TypeUtil.isString(name)) { if (TypeUtil.isString(name)) {
this.attributes = { this.attributes = {
name: [<string>name], name: [<InstanceNameType>name],
}; };
} else { } else {
this.attributes = <InjectableAnnotationAttributes>name; this.attributes = <InjectableAnnotationAttributes>name;

View File

@ -0,0 +1,42 @@
import {
Annotation,
Decorator,
} from '@overflow/commons/core/reflect';
import {
PropertyKeyType,
TypeUtil,
} from '@overflow/commons/core/type';
export interface PostConstructAnnotationAttributes {
name?: string[];
}
export class PostConstructAnnotation implements Annotation {
public readonly attributes: PostConstructAnnotationAttributes;
public constructor(name: string | string[] | PostConstructAnnotationAttributes) {
if (undefined === name) {
this.attributes = {
};
return;
}
if (TypeUtil.isString(name)) {
this.attributes = {
name: [<string>name],
};
} else {
this.attributes = <PostConstructAnnotationAttributes>name;
}
}
public methodDecorator<T>(target: Object, propertyKey: PropertyKeyType,
descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void {
console.log('PostConstruct');
}
}
export const PostConstruct = Decorator.create(PostConstructAnnotation);

View File

@ -0,0 +1,42 @@
import {
Annotation,
Decorator,
} from '@overflow/commons/core/reflect';
import {
PropertyKeyType,
TypeUtil,
} from '@overflow/commons/core/type';
export interface PreDestroyAnnotationAttributes {
name?: string[];
}
export class PreDestroyAnnotation implements Annotation {
public readonly attributes: PreDestroyAnnotationAttributes;
public constructor(name: string | string[] | PreDestroyAnnotationAttributes) {
if (undefined === name) {
this.attributes = {
};
return;
}
if (TypeUtil.isString(name)) {
this.attributes = {
name: [<string>name],
};
} else {
this.attributes = <PreDestroyAnnotationAttributes>name;
}
}
public methodDecorator<T>(target: Object, propertyKey: PropertyKeyType,
descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void {
console.log('PreDestroy');
}
}
export const PreDestroy = Decorator.create(PreDestroyAnnotation);

View File

@ -8,14 +8,18 @@ import {
TypeUtil, TypeUtil,
} from '@overflow/commons/core/type'; } from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export interface ResourceAnnotationAttributes { export interface ResourceAnnotationAttributes {
name: string[]; name: InstanceNameType[];
} }
export class ResourceAnnotation implements Annotation { export class ResourceAnnotation implements Annotation {
public readonly attributes: ResourceAnnotationAttributes; public readonly attributes: ResourceAnnotationAttributes;
public constructor(name: string | ResourceAnnotationAttributes) { public constructor(name: InstanceNameType | ResourceAnnotationAttributes) {
if (undefined === name) { if (undefined === name) {
throw new Error(`name attribute must be specified.`); throw new Error(`name attribute must be specified.`);
@ -23,7 +27,7 @@ export class ResourceAnnotation implements Annotation {
if (TypeUtil.isString(name)) { if (TypeUtil.isString(name)) {
this.attributes = { this.attributes = {
name: [<string>name], name: [<InstanceNameType>name],
}; };
} else { } else {
this.attributes = <ResourceAnnotationAttributes>name; this.attributes = <ResourceAnnotationAttributes>name;

View File

@ -0,0 +1,42 @@
import {
Annotation,
Decorator,
} from '@overflow/commons/core/reflect';
import {
PropertyKeyType,
TypeUtil,
} from '@overflow/commons/core/type';
export interface ValueAnnotationAttributes {
value: string;
}
export class ValueAnnotation implements Annotation {
public readonly attributes: ValueAnnotationAttributes;
public constructor(value: string | ValueAnnotationAttributes) {
if (undefined === value) {
throw new Error(`value attribute must be specified.`);
}
if (TypeUtil.isString(value)) {
this.attributes = {
value: <string>value,
};
} else {
this.attributes = <ValueAnnotationAttributes>value;
}
}
public propertyDecorator?(target: Object, propertyKey: PropertyKeyType): void {
console.log('Value');
}
public parameterDecorator?(target: Object, propertyKey: PropertyKeyType, parameterIndex: number): void {
console.log('Value');
}
}
export const Value = Decorator.create(ValueAnnotation);

View File

@ -0,0 +1,3 @@
export * from './instance_definition_registry';
export * from './instance_definition';
export * from './instance_factory';

View File

@ -2,13 +2,26 @@ import {
ClassType, ClassType,
} from '@overflow/commons/core/type'; } from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export class InstanceDefinition { export class InstanceDefinition {
private _instanceClass: ClassType; private _instanceClass: ClassType;
private _name: InstanceNameType;
public get InstanceClass(): ClassType { public get instanceClass(): ClassType {
return this._instanceClass; return this._instanceClass;
} }
public set InstanceClass(instanceClass: ClassType) { public set instanceClass(instanceClass: ClassType) {
this._instanceClass = instanceClass; this._instanceClass = instanceClass;
} }
}
public get name(): InstanceNameType {
return this._name;
}
public set name(name: InstanceNameType) {
this._name = name;
}
}

View File

@ -0,0 +1,15 @@
import {
InstanceNameType,
} from '@overflow/commons/di/type';
import {InstanceDefinition} from './instance_definition';
export interface InstanceDefinitionRegistry {
registerInstanceDefinition(name: InstanceNameType, instanceDefinition:InstanceDefinition): void;
removeInstanceDefinition(name: InstanceNameType): void;
getInstanceDefinition(name: InstanceNameType): InstanceDefinition;
containsInstanceDefinition(name: InstanceNameType): boolean;
getInstanceDefinitionNames(): InstanceNameType[];
getInstanceDefinitionCount(): number;
isInstanceNameInUse(name: InstanceNameType): boolean;
}

View File

@ -0,0 +1,65 @@
import {
ClassType,
} from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
import {InstanceDefinitionRegistry} from './instance_definition_registry';
import {InstanceDefinition} from './instance_definition';
export class InstanceFactory implements InstanceDefinitionRegistry {
/**
* getInstance
*/
public getInstance(name: InstanceNameType, requiredType: ClassType, ...args: any[]): any {
return null;
}
/**
* containsInstance
*/
public containsInstance(name: InstanceNameType): boolean {
return false;
}
public registerJSON(json: any): void {
//
}
public registerSingleton(name: InstanceNameType, instanceType: any, instance: any): void {
//
}
public registerInstanceDefinition(name: InstanceNameType, instanceDefinition: InstanceDefinition): void {
//
}
public removeInstanceDefinition(name: InstanceNameType): void {
//
}
public getInstanceDefinition(name: InstanceNameType): InstanceDefinition {
//
}
public containsInstanceDefinition(name: InstanceNameType): boolean {
//
}
public getInstanceDefinitionNames(): InstanceNameType[] {
//
}
public getInstanceDefinitionCount(): number {
//
}
public isInstanceNameInUse(name: InstanceNameType): boolean {
//
}
}

View File

@ -1,18 +1,18 @@
import { PropertyKeyType } from '@overflow/commons/core/type'; import { PropertyKeyType } from '@overflow/commons/core/type';
export class PropertyValue { export class PropertyValue {
private readonly name: PropertyKeyType; private readonly _name: PropertyKeyType;
private readonly value: any; private readonly _value: any;
public constructor(name: PropertyKeyType, value: any) { public constructor(name: PropertyKeyType, value: any) {
this.name = name; this._name = name;
this.value = value; this._value = value;
} }
public get Name(): PropertyKeyType { public get name(): PropertyKeyType {
return this.name; return this._name;
} }
public get Value(): any { public get value(): any {
return this.value; return this._value;
} }
} }

View File

@ -0,0 +1 @@
export * from './type';

View File

@ -0,0 +1,5 @@
import {
IdentityType,
} from '@overflow/commons/core/type';
export type InstanceNameType = IdentityType<string>;

View File

@ -1,22 +1,10 @@
import { InjectablesType } from '@overflow/commons/application/decorators'; import {
ClassType,
} from '@overflow/commons/core/type';
import { WebAppDispatcherReducer } from '../redux'; import { WebAppDispatcherReducer } from '../redux';
export const injectables: ClassType[] = [
export const injectables: InjectablesType = [
WebAppDispatcherReducer, WebAppDispatcherReducer,
{
injectType: WebAppDispatcherReducer,
injectableType: WebAppDispatcherReducer,
},
{
injectType: WebAppDispatcherReducer,
injectableFactory: function(): any {
return new WebAppDispatcherReducer();
},
},
{
injectName: 'Name',
injectableValue: 'TestValue',
},
]; ];

View File

@ -4,17 +4,13 @@ import {
import { import {
Configuration, Configuration,
Instance,
} from '@overflow/commons/application/decorators'; } from '@overflow/commons/application/decorators';
import {
Injectable,
} from '@overflow/commons/di/decorators';
@Configuration() @Configuration()
export class ReducerConfiguration { export class ReducerConfiguration {
@Injectable() @Instance()
public store(): Store<any> { public store(): Store<any> {
return null; return null;
} }

View File

@ -1,7 +1,15 @@
import { Configuration } from '@overflow/commons/application/decorators'; import {
Configuration,
Instance,
} from '@overflow/commons/application/decorators';
@Configuration() @Configuration()
export class WebAppConfiguration { export class WebAppConfiguration {
/**
* container
*/
@Instance()
public container(): HTMLElement {
return null;
}
} }

View File

@ -29,8 +29,14 @@ import {
} from 'react-hot-loader'; } from 'react-hot-loader';
import { Application } from '@overflow/commons/application'; import { Application } from '@overflow/commons/application';
import { WebApplication } from '@overflow/commons/application/decorators'; import {
import { Inject } from '@overflow/commons/di/decorators'; Runner,
WebApplication,
} from '@overflow/commons/application/decorators';
import {
Inject,
Resource,
} from '@overflow/commons/di/decorators';
import config, { import config, {
injectables, injectables,
@ -65,7 +71,10 @@ class WebAppApplication {
private static isProduction:boolean = process.env.NODE_ENV === 'production' ? true : false; private static isProduction:boolean = process.env.NODE_ENV === 'production' ? true : false;
private static useReduxDevTools:boolean = window.devToolsExtension && !WebAppApplication.isProduction ? true : false; private static useReduxDevTools:boolean = window.devToolsExtension && !WebAppApplication.isProduction ? true : false;
@Resource()
private container: HTMLElement; private container: HTMLElement;
@Inject()
private store: Store<any>; private store: Store<any>;
private history: History; private history: History;
@ -77,6 +86,7 @@ class WebAppApplication {
this.history = createHashHistory(); this.history = createHashHistory();
} }
@Runner()
public async run(): Promise<void> { public async run(): Promise<void> {
try { try {
this.renderLoading(); this.renderLoading();