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 {
Class,
Method,
} from '@overflow/commons/core/reflect';
import { ClassType } from '@overflow/commons/core/type';
import { ApplicationContext } from './context';
import {
InjectableClass,
InjectableFactory,
InjectableValue,
ConfigurationAnnotation,
InstanceAnnotation,
RunnerAnnotation,
WebApplicationAnnotation,
} from './decorators';
import { InstanceDefinition } from '@overflow/commons/di/factory';
export class Application {
private primaryClass: ClassType;
private applicationContext: ApplicationContext;
private _primaryClass: ClassType;
private _applicationContext: ApplicationContext;
public static run(primaryClass: ClassType): void {
public static async run(primaryClass: ClassType): Promise<ApplicationContext> {
let app: Application = new Application(primaryClass);
return app.run();
}
public constructor(primaryClass: ClassType) {
this.primaryClass = primaryClass;
this._primaryClass = primaryClass;
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);
if (undefined === wa) {
console.warn(`Class is not WebApplication type. add @WebApplication annotation to class[${this.primaryClass.name}]`);
return;
throw new Error(`Class is not WebApplication type. add @WebApplication annotation to class[${this._primaryClass.name}]`);
}
let context: ApplicationContext = new ApplicationContext();
// context.registerInjectables(wa.attributes.injectables);
this._applicationContext = new ApplicationContext(wa.attributes.injectables);
wa.attributes.injectables.forEach(injectable => {
if (undefined !== (<InjectableClass>injectable).injectableType) {
context.registerInjectable()
} else if (undefined !== (<InjectableFactory>injectable).injectableFactory) {
console.log(`InjectableFactory`);
} else if (undefined !== (<InjectableValue>injectable).injectableValue) {
console.log(`InjectableValue`);
} else if (typeof injectable === 'function') {
console.log(`Class`);
this.registerJSONSources(wa.attributes.jsonSources);
this.registerConfigurations(wa.attributes.configurations);
return this._applicationContext;
}
private registerJSONSources(jsonSources: any[]): void {
if (undefined === jsonSources || null === jsonSources) {
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 {
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 './instance';
export * from './runner';
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,
} 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 {
injectables?: InjectablesType;
jsonSources?: any[];
injectables?: 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 './injectable';
export * from './post_construct';
export * from './pre_destroy';
export * from './resource';
export * from './value';

View File

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

View File

@ -8,14 +8,18 @@ import {
TypeUtil,
} from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export interface InjectableAnnotationAttributes {
name?: string[];
name?: InstanceNameType[];
}
export class InjectableAnnotation implements Annotation {
public readonly attributes: InjectableAnnotationAttributes;
public constructor(name: string | string[] | InjectableAnnotationAttributes) {
public constructor(name: InstanceNameType | InstanceNameType[] | InjectableAnnotationAttributes) {
if (undefined === name) {
this.attributes = {
@ -25,7 +29,7 @@ export class InjectableAnnotation implements Annotation {
if (TypeUtil.isString(name)) {
this.attributes = {
name: [<string>name],
name: [<InstanceNameType>name],
};
} else {
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,
} from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export interface ResourceAnnotationAttributes {
name: string[];
name: InstanceNameType[];
}
export class ResourceAnnotation implements Annotation {
public readonly attributes: ResourceAnnotationAttributes;
public constructor(name: string | ResourceAnnotationAttributes) {
public constructor(name: InstanceNameType | ResourceAnnotationAttributes) {
if (undefined === name) {
throw new Error(`name attribute must be specified.`);
@ -23,7 +27,7 @@ export class ResourceAnnotation implements Annotation {
if (TypeUtil.isString(name)) {
this.attributes = {
name: [<string>name],
name: [<InstanceNameType>name],
};
} else {
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,
} from '@overflow/commons/core/type';
import {
InstanceNameType,
} from '@overflow/commons/di/type';
export class InstanceDefinition {
private _instanceClass: ClassType;
private _name: InstanceNameType;
public get InstanceClass(): ClassType {
public get instanceClass(): ClassType {
return this._instanceClass;
}
public set InstanceClass(instanceClass: ClassType) {
public set instanceClass(instanceClass: ClassType) {
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';
export class PropertyValue {
private readonly name: PropertyKeyType;
private readonly value: any;
private readonly _name: PropertyKeyType;
private readonly _value: any;
public constructor(name: PropertyKeyType, value: any) {
this.name = name;
this.value = value;
this._name = name;
this._value = value;
}
public get Name(): PropertyKeyType {
return this.name;
public get name(): PropertyKeyType {
return this._name;
}
public get Value(): any {
return this.value;
public get value(): any {
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';
export const injectables: InjectablesType = [
export const injectables: ClassType[] = [
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 {
Configuration,
Instance,
} from '@overflow/commons/application/decorators';
import {
Injectable,
} from '@overflow/commons/di/decorators';
@Configuration()
export class ReducerConfiguration {
@Injectable()
@Instance()
public store(): Store<any> {
return null;
}

View File

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

View File

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