Skip to content

Commit

Permalink
feat: add uid attribute (#16)
Browse files Browse the repository at this point in the history
Co-authored-by: Cue <[email protected]>
  • Loading branch information
kiaking and cuebit authored May 15, 2020
1 parent d4c7765 commit e676dd1
Show file tree
Hide file tree
Showing 21 changed files with 568 additions and 26 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@
"vuex": ">=3.1.0"
},
"dependencies": {
"normalizr": "^3.6.0"
"@types/uuid": "^7.0.3",
"normalizr": "^3.6.0",
"uuid": "^8.0.0"
},
"devDependencies": {
"@microsoft/api-documenter": "^7.8.0",
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function createEntry(config) {
: config.env !== 'production'
}))

c.plugins.push(resolve())
c.plugins.push(resolve({ browser: true }))
c.plugins.push(commonjs())

c.plugins.push(ts({
Expand Down
4 changes: 4 additions & 0 deletions src/index.cjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Attr } from './model/decorators/attributes/types/Attr'
import { Str } from './model/decorators/attributes/types/Str'
import { Num } from './model/decorators/attributes/types/Num'
import { Bool } from './model/decorators/attributes/types/Bool'
import { Uid } from './model/decorators/attributes/types/Uid'
import { HasOne } from './model/decorators/attributes/relations/HasOne'
import { BelongsTo } from './model/decorators/attributes/relations/BelongsTo'
import { HasMany } from './model/decorators/attributes/relations/HasMany'
Expand All @@ -19,6 +20,7 @@ import { Attr as AttrAttr } from './model/attributes/types/Attr'
import { String as StringAttr } from './model/attributes/types/String'
import { Number as NumberAttr } from './model/attributes/types/Number'
import { Boolean as BooleanAttr } from './model/attributes/types/Boolean'
import { Uid as UidAttr } from './model/attributes/types/Uid'
import { Relation } from './model/attributes/relations/Relation'
import { HasOne as HasOneAttr } from './model/attributes/relations/HasOne'
import { HasMany as HasManyAttr } from './model/attributes/relations/HasMany'
Expand All @@ -38,6 +40,7 @@ export default {
Str,
Num,
Bool,
Uid,
HasOne,
BelongsTo,
HasMany,
Expand All @@ -47,6 +50,7 @@ export default {
StringAttr,
NumberAttr,
BooleanAttr,
UidAttr,
Relation,
HasOneAttr,
HasManyAttr,
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from './model/decorators/attributes/types/Attr'
export * from './model/decorators/attributes/types/Str'
export * from './model/decorators/attributes/types/Num'
export * from './model/decorators/attributes/types/Bool'
export * from './model/decorators/attributes/types/Uid'
export * from './model/decorators/attributes/relations/HasOne'
export * from './model/decorators/attributes/relations/BelongsTo'
export * from './model/decorators/attributes/relations/HasMany'
Expand All @@ -20,6 +21,7 @@ export { Attr as AttrAttr } from './model/attributes/types/Attr'
export { String as StringAttr } from './model/attributes/types/String'
export { Number as NumberAttr } from './model/attributes/types/Number'
export { Boolean as BooleanAttr } from './model/attributes/types/Boolean'
export { Uid as UidAttr } from './model/attributes/types/Uid'
export * from './model/attributes/relations/Relation'
export { HasOne as HasOneAttr } from './model/attributes/relations/HasOne'
export { BelongsTo as BelongsToAttr } from './model/attributes/relations/BelongsTo'
Expand Down Expand Up @@ -49,6 +51,7 @@ import { Attr as AttrAttr } from './model/attributes/types/Attr'
import { String as StringAttr } from './model/attributes/types/String'
import { Number as NumberAttr } from './model/attributes/types/Number'
import { Boolean as BooleanAttr } from './model/attributes/types/Boolean'
import { Uid as UidAttr } from './model/attributes/types/Uid'
import { Relation } from './model/attributes/relations/Relation'
import { HasOne as HasOneAttr } from './model/attributes/relations/HasOne'
import { HasMany as HasManyAttr } from './model/attributes/relations/HasMany'
Expand All @@ -70,6 +73,7 @@ export default {
StringAttr,
NumberAttr,
BooleanAttr,
UidAttr,
Relation,
HasOneAttr,
HasManyAttr,
Expand Down
72 changes: 57 additions & 15 deletions src/model/Model.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Store } from 'vuex'
import { isArray, assert } from '../support/Utils'
import { isNullish, isArray, assert } from '../support/Utils'
import { Element, Item, Collection } from '../data/Data'
import { Database } from '../database/Database'
import { Attribute } from './attributes/Attribute'
import { Attr } from './attributes/types/Attr'
import { String as Str } from './attributes/types/String'
import { Number as Num } from './attributes/types/Number'
import { Boolean as Bool } from './attributes/types/Boolean'
import { Uid } from './attributes/types/Uid'
import { Relation } from './attributes/relations/Relation'
import { HasOne } from './attributes/relations/HasOne'
import { BelongsTo } from './attributes/relations/BelongsTo'
Expand All @@ -17,6 +19,7 @@ export type ModelRegistries = Record<string, ModelRegistry>
export type ModelRegistry = Record<string, () => Attribute>

export interface ModelOptions {
fill?: boolean
relations?: boolean
}

Expand Down Expand Up @@ -57,10 +60,12 @@ export class Model {
/**
* Create a new model instance.
*/
constructor(attributes?: Element, options?: ModelOptions) {
constructor(attributes?: Element, options: ModelOptions = {}) {
this.$boot()

this.$fill(attributes, options)
const fill = options.fill ?? true

fill && this.$fill(attributes, options)

// Prevent `_store` from becoming cyclic object value and causing
// v-bind side-effects by negating enumerability.
Expand Down Expand Up @@ -114,32 +119,61 @@ export class Model {
this.schemas = {}
}

/**
* Clear registries.
*/
static clearRegistries(): void {
this.registries = {}
}

/**
* Create a new model instance without field values being populated.
*
* This method is mainly fo the internal use when registering models to the
* database. Since all pre-registered models are for referencing its model
* setting during the various process, but the fields are not required.
*
* Use this method when you want create a new model instance for:
* - Registering model to a component (eg. Repository, Query, etc.)
* - Registering model to attributes (String, Has Many, etc.)
*/
static newRawInstance<M extends typeof Model>(this: M): InstanceType<M> {
return new this(undefined, { fill: false }) as InstanceType<M>
}

/**
* Create a new Attr attribute instance.
*/
static attr(value: any): Attr {
return new Attr(new this(), value)
return new Attr(this.newRawInstance(), value)
}

/**
* Create a new String attribute instance.
*/
static string(value: string | null): Str {
return new Str(new this(), value)
return new Str(this.newRawInstance(), value)
}

/**
* Create a new Number attribute instance.
*/
static number(value: number | null): Num {
return new Num(new this(), value)
return new Num(this.newRawInstance(), value)
}

/**
* Create a new Boolean attribute instance.
*/
static boolean(value: boolean | null): Bool {
return new Bool(new this(), value)
return new Bool(this.newRawInstance(), value)
}

/**
* Create a new Uid attribute instance.
*/
static uid(): Uid {
return new Uid(this.newRawInstance())
}

/**
Expand All @@ -150,11 +184,11 @@ export class Model {
foreignKey: string,
localKey?: string
): HasOne {
const model = new this()
const model = this.newRawInstance()

localKey = localKey ?? model.$getLocalKey()

return new HasOne(model, new related(), foreignKey, localKey)
return new HasOne(model, related.newRawInstance(), foreignKey, localKey)
}

/**
Expand All @@ -165,11 +199,11 @@ export class Model {
foreignKey: string,
ownerKey?: string
): BelongsTo {
const instance = new related()
const instance = related.newRawInstance()

ownerKey = ownerKey ?? instance.$getLocalKey()

return new BelongsTo(new this(), instance, foreignKey, ownerKey)
return new BelongsTo(this.newRawInstance(), instance, foreignKey, ownerKey)
}

/**
Expand All @@ -180,11 +214,11 @@ export class Model {
foreignKey: string,
localKey?: string
): HasMany {
const model = new this()
const model = this.newRawInstance()

localKey = localKey ?? model.$getLocalKey()

return new HasMany(model, new related(), foreignKey, localKey)
return new HasMany(model, related.newRawInstance(), foreignKey, localKey)
}

/**
Expand All @@ -207,6 +241,13 @@ export class Model {
return this._store
}

/**
* Get the database instance.
*/
get $database(): Database {
return this.$store.$database
}

/**
* Get the entity for this model.
*/
Expand Down Expand Up @@ -316,10 +357,11 @@ export class Model {
/**
* Get the index id of this model or for a given record.
*/
$getIndexId(record?: Element): string {
$getIndexId(record?: Element): string | null {
const target = record ?? this
const id = target[this.$primaryKey]

return String(target[this.$primaryKey])
return isNullish(id) ? null : String(id)
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/model/ModelConstructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Model } from './Model'

export interface ModelConstructor<M extends Model> {
new (...args: any[]): M
newRawInstance(): M
}
11 changes: 11 additions & 0 deletions src/model/attributes/types/Uid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { v1 as uuid } from 'uuid'
import { Type } from './Type'

export class Uid extends Type {
/**
* Make the value for the attribute.
*/
make(value: any): string {
return value ?? uuid()
}
}
10 changes: 10 additions & 0 deletions src/model/decorators/attributes/types/Uid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PropertyDecorator } from '../../Contracts'

/**
* Create a Uid attribute property decorator.
*/
export function Uid(): PropertyDecorator {
return (target, propertyKey) => {
target.$self.setRegistry(propertyKey, () => target.$self.uid())
}
}
3 changes: 3 additions & 0 deletions src/plugin/Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Attr } from '../model/attributes/types/Attr'
import { String } from '../model/attributes/types/String'
import { Number } from '../model/attributes/types/Number'
import { Boolean } from '../model/attributes/types/Boolean'
import { Uid } from '../model/attributes/types/Uid'
import { Relation } from '../model/attributes/relations/Relation'
import { HasOne } from '../model/attributes/relations/HasOne'
import { HasMany } from '../model/attributes/relations/HasMany'
Expand Down Expand Up @@ -38,6 +39,7 @@ export interface VuexORMPluginComponents {
String: typeof String
Number: typeof Number
Boolean: typeof Boolean
Uid: typeof Uid
Relation: typeof Relation
HasOne: typeof HasOne
HasMany: typeof HasMany
Expand Down Expand Up @@ -67,6 +69,7 @@ export const components: VuexORMPluginComponents = {
String,
Number,
Boolean,
Uid,
Relation,
HasOne,
HasMany,
Expand Down
6 changes: 3 additions & 3 deletions src/query/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ export class Query<M extends Model = Model> {
const recordsArray = isArray(records) ? records : [records]

return recordsArray.reduce<Collection<M>>((collection, record) => {
const model = this.pick(this.model.$getIndexId(record))
const model = this.pick(this.model.$getIndexId(record)!)

model && collection.push(model.$fill(record))

Expand Down Expand Up @@ -615,7 +615,7 @@ export class Query<M extends Model = Model> {
* Get an array of ids from the given collection.
*/
protected getIndexIdsFromCollection(models: Collection<M>): string[] {
return models.map((model) => model.$getIndexId())
return models.map((model) => model.$getIndexId()!)
}

/**
Expand Down Expand Up @@ -644,7 +644,7 @@ export class Query<M extends Model = Model> {
const modelArray = isArray(models) ? models : [models]

return modelArray.reduce<Elements>((records, model) => {
records[model.$getIndexId()] = model.$getAttributes()
records[model.$getIndexId()!] = model.$getAttributes()
return records
}, {})
}
Expand Down
9 changes: 5 additions & 4 deletions src/repository/Repository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Store } from 'vuex'
import { assert } from '../support/Utils'
import { Constructor } from '../types'
import { assert } from '../support/Utils'
import { Element, Item, Collection, Collections } from '../data/Data'
import { Model } from '../model/Model'
import { ModelConstructor } from '../model/ModelConstructor'
import { Query } from '../query/Query'
import {
WherePrimaryClosure,
Expand Down Expand Up @@ -44,10 +45,10 @@ export class Repository<M extends Model = Model> {
/**
* Initialize the repository by setting the model instance.
*/
initialize(model?: Constructor<M>): this {
initialize(model?: ModelConstructor<M>): this {
// If there's a model passed in, just use that and return immediately.
if (model) {
this.model = new model().$setStore(this.store)
this.model = model.newRawInstance().$setStore(this.store)

return this
}
Expand All @@ -57,7 +58,7 @@ export class Repository<M extends Model = Model> {
// In this case, we'll check if the user has set model to the `use`
// property and instantiate that.
if (this.use) {
this.model = (new this.use() as M).$setStore(this.store)
this.model = (this.use.newRawInstance() as M).$setStore(this.store)

return this
}
Expand Down
Loading

0 comments on commit e676dd1

Please sign in to comment.