forked from decahedronio/entity
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
321 additions
and
251 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,103 @@ | ||
import { Entity, PartialPropsJson } from './Entity'; | ||
import { Buildable, Constructor } from './support/Type'; | ||
import { TypeMetadata } from './support/metadata/TypeMetadata'; | ||
import { defaultMetadataStorage } from './support/storage'; | ||
import { isEntityType } from './support/isEntityType'; | ||
import { StringHelper } from './support/StringHelper'; | ||
import { Entity, PartialPropsJson } from "./Entity"; | ||
import { Buildable, Constructor } from "./support/Type"; | ||
import { TypeMetadata } from "./support/metadata/TypeMetadata"; | ||
import { defaultMetadataStorage } from "./support/storage"; | ||
import { isEntityType } from "./support/isEntityType"; | ||
import { StringHelper } from "./support/StringHelper"; | ||
|
||
export class EntityBuilder { | ||
public static buildOne<T extends Entity>( | ||
buildClass: Constructor<T>, | ||
sourceData: PartialPropsJson<T>, | ||
): T { | ||
const entity = new (buildClass)(); | ||
return entity.fromJson(sourceData); | ||
} | ||
public static buildOne<T extends Entity>( | ||
buildClass: Constructor<T>, | ||
sourceData: PartialPropsJson<T>, | ||
): T { | ||
const entity = new buildClass(); | ||
return entity.fromJson(sourceData); | ||
} | ||
|
||
public static buildMany<T extends Entity>( | ||
buildClass: Constructor<T>, | ||
sourceData: Array<PartialPropsJson<T>>, | ||
): Array<T> { | ||
return sourceData.map( | ||
entityData => this.buildOne(buildClass, entityData), | ||
); | ||
} | ||
public static buildMany<T extends Entity>( | ||
buildClass: Constructor<T>, | ||
sourceData: Array<PartialPropsJson<T>>, | ||
): Array<T> { | ||
return sourceData.map((entityData) => | ||
this.buildOne(buildClass, entityData), | ||
); | ||
} | ||
|
||
public static fill<T extends Entity>(entity: T, data: PartialPropsJson<T>): T { | ||
for (let key in data) { | ||
EntityBuilder.fillProperty<T>(entity, key, data[key]); | ||
} | ||
|
||
return entity; | ||
public static fill<T extends Entity>( | ||
entity: T, | ||
data: PartialPropsJson<T>, | ||
): T { | ||
for (let key in data) { | ||
EntityBuilder.fillProperty<T>(entity, key, data[key]); | ||
} | ||
|
||
private static fillProperty<T extends Entity>(entity: T, key: string, value: any): void { | ||
// Don't even bother for undefined values. | ||
if (typeof value === 'undefined') { | ||
return; | ||
} | ||
return entity; | ||
} | ||
|
||
const metadata: TypeMetadata = defaultMetadataStorage.findTypeMetadata(entity.constructor, key); | ||
private static fillProperty<T extends Entity>( | ||
entity: T, | ||
key: string, | ||
value: any, | ||
): void { | ||
// Don't even bother for undefined values. | ||
if (typeof value === "undefined") { | ||
return; | ||
} | ||
|
||
if (metadata) { | ||
EntityBuilder.fillTypeDecoratedProperty<T>(entity, metadata, value); | ||
return; | ||
} | ||
const metadata: TypeMetadata = defaultMetadataStorage.findTypeMetadata( | ||
entity.constructor, | ||
key, | ||
); | ||
|
||
// No type definition means scalar value, and we can just set that as is. | ||
entity.setProp(StringHelper.toCamel(key), value); | ||
if (metadata) { | ||
EntityBuilder.fillTypeDecoratedProperty<T>(entity, metadata, value); | ||
return; | ||
} | ||
|
||
private static fillTypeDecoratedProperty<T extends Entity>(entity: T, metadata: TypeMetadata, value: InstanceType<Buildable>) { | ||
// We shouldn't copy objects to our entity, as the entity should be responsible for constructing these itself. | ||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) { | ||
if (isEntityType(metadata.type)) { | ||
entity.setProp(metadata.propertyName, EntityBuilder.buildOne(metadata.type, value)); | ||
} else { | ||
entity.setProp(metadata.propertyName, new metadata.type(value)) | ||
} | ||
// No type definition means scalar value, and we can just set that as is. | ||
entity.setProp(StringHelper.toCamel(key), value); | ||
} | ||
|
||
return; | ||
} | ||
private static fillTypeDecoratedProperty<T extends Entity>( | ||
entity: T, | ||
metadata: TypeMetadata, | ||
value: InstanceType<Buildable>, | ||
) { | ||
// We shouldn't copy objects to our entity, as the entity should be responsible for constructing these itself. | ||
if (typeof value === "object" && value !== null && !Array.isArray(value)) { | ||
if (isEntityType(metadata.type)) { | ||
entity.setProp( | ||
metadata.propertyName, | ||
EntityBuilder.buildOne(metadata.type, value), | ||
); | ||
} else { | ||
entity.setProp(metadata.propertyName, new metadata.type(value)); | ||
} | ||
|
||
// if we have an array, we check if it contains objects, in which case the entity itself should be assumed | ||
// responsible to construct the array of entities. | ||
if (Array.isArray(value) && value.length > 0) { | ||
if (isEntityType(metadata.type)) { | ||
entity.setProp(metadata.propertyName, EntityBuilder.buildMany(metadata.type, value)); | ||
} else { | ||
entity.setProp(metadata.propertyName, value.map(item => new metadata.type(item))); | ||
} | ||
return; | ||
} | ||
|
||
return; | ||
} | ||
// if we have an array, we check if it contains objects, in which case the entity itself should be assumed | ||
// responsible to construct the array of entities. | ||
if (Array.isArray(value) && value.length > 0) { | ||
if (isEntityType(metadata.type)) { | ||
entity.setProp( | ||
metadata.propertyName, | ||
EntityBuilder.buildMany(metadata.type, value), | ||
); | ||
} else { | ||
entity.setProp( | ||
metadata.propertyName, | ||
value.map((item) => new metadata.type(item)), | ||
); | ||
} | ||
|
||
// Since all other scenarios have been exhausted, we're dealing with a primitive of some form. | ||
// This can be an empty array of objects too, but since it's empty, there's no need for us | ||
// to build an entity. As such, we can just assign it. The same goes for all primitives. | ||
entity.setProp(metadata.propertyName, value); | ||
return; | ||
} | ||
|
||
// Since all other scenarios have been exhausted, we're dealing with a primitive of some form. | ||
// This can be an empty array of objects too, but since it's empty, there's no need for us | ||
// to build an entity. As such, we can just assign it. The same goes for all primitives. | ||
entity.setProp(metadata.propertyName, value); | ||
} | ||
} |
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 |
---|---|---|
@@ -1,4 +1,9 @@ | ||
export { Entity } from './Entity'; | ||
export type { Props, PropsJson, PartialProps, PartialPropsJson } from './Entity'; | ||
export { EntityBuilder } from './EntityBuilder'; | ||
export { Type } from './support/Type'; | ||
export { Entity } from "./Entity"; | ||
export type { | ||
Props, | ||
PropsJson, | ||
PartialProps, | ||
PartialPropsJson, | ||
} from "./Entity"; | ||
export { EntityBuilder } from "./EntityBuilder"; | ||
export { Type } from "./support/Type"; |
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 |
---|---|---|
@@ -1,9 +1,9 @@ | ||
import { defaultMetadataStorage } from './storage'; | ||
import {JsonExcludeMetadata} from './metadata/JsonExcludeMetadata'; | ||
import { defaultMetadataStorage } from "./storage"; | ||
import { JsonExcludeMetadata } from "./metadata/JsonExcludeMetadata"; | ||
|
||
export function JsonExclude() { | ||
return function (target: any, key: string) { | ||
const metadata = new JsonExcludeMetadata(target.constructor, key); | ||
defaultMetadataStorage.addExcludeProperty(metadata); | ||
}; | ||
return function (target: any, key: string) { | ||
const metadata = new JsonExcludeMetadata(target.constructor, key); | ||
defaultMetadataStorage.addExcludeProperty(metadata); | ||
}; | ||
} |
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 |
---|---|---|
@@ -1,37 +1,37 @@ | ||
export class StringHelper { | ||
/** | ||
* Convert a string to camelCase. | ||
* @param source | ||
* @returns {string} | ||
*/ | ||
public static toCamel(source: string): string { | ||
// Inspired by http://www.devcurry.com/2011/07/javascript-convert-camelcase-to-dashes.html | ||
// Remove underscores and turn the next character into uppercase | ||
const str: string = source.replace(/_+(.)/g, (x, chr) => chr.toUpperCase()); | ||
/** | ||
* Convert a string to camelCase. | ||
* @param source | ||
* @returns {string} | ||
*/ | ||
public static toCamel(source: string): string { | ||
// Inspired by http://www.devcurry.com/2011/07/javascript-convert-camelcase-to-dashes.html | ||
// Remove underscores and turn the next character into uppercase | ||
const str: string = source.replace(/_+(.)/g, (x, chr) => chr.toUpperCase()); | ||
|
||
// If we have a leading uppercase (either the source started with an | ||
// uppercase character or with an underscore), lowercase the first. | ||
return this.lowercaseFirst(str); | ||
} | ||
// If we have a leading uppercase (either the source started with an | ||
// uppercase character or with an underscore), lowercase the first. | ||
return this.lowercaseFirst(str); | ||
} | ||
|
||
/** | ||
* Convert a string to snake_case. | ||
* | ||
* @param source | ||
* @returns {any} | ||
*/ | ||
public static toSnake(source: string): string { | ||
const str: string = this.lowercaseFirst(source); | ||
return str.replace(/([A-Z])/g, x => '_' + x.toLowerCase()); | ||
} | ||
/** | ||
* Convert a string to snake_case. | ||
* | ||
* @param source | ||
* @returns {any} | ||
*/ | ||
public static toSnake(source: string): string { | ||
const str: string = this.lowercaseFirst(source); | ||
return str.replace(/([A-Z])/g, (x) => "_" + x.toLowerCase()); | ||
} | ||
|
||
/** | ||
* Lowercase the first letter of a string. | ||
* | ||
* @param str | ||
* @returns {string} | ||
*/ | ||
private static lowercaseFirst(str: string) { | ||
return str.charAt(0).toLowerCase() + str.slice(1); | ||
} | ||
/** | ||
* Lowercase the first letter of a string. | ||
* | ||
* @param str | ||
* @returns {string} | ||
*/ | ||
private static lowercaseFirst(str: string) { | ||
return str.charAt(0).toLowerCase() + str.slice(1); | ||
} | ||
} |
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 |
---|---|---|
@@ -1,6 +1,8 @@ | ||
import { Buildable, Constructor } from './Type'; | ||
import { Entity } from '../Entity'; | ||
import { Buildable, Constructor } from "./Type"; | ||
import { Entity } from "../Entity"; | ||
|
||
export function isEntityType(buildable: Buildable): buildable is Constructor<Entity> { | ||
return buildable?.prototype instanceof Entity; | ||
export function isEntityType( | ||
buildable: Buildable, | ||
): buildable is Constructor<Entity> { | ||
return buildable?.prototype instanceof Entity; | ||
} |
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 |
---|---|---|
@@ -1,3 +1,6 @@ | ||
export class JsonExcludeMetadata { | ||
constructor(public target: Function, public propertyName: string) {} | ||
constructor( | ||
public target: Function, | ||
public propertyName: string, | ||
) {} | ||
} |
Oops, something went wrong.