Skip to content

WIP: Add more decorators #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/decorators/Attribute.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import PropertyDecorator from '../contracts/PropertyDecorator'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

/**
* Create a attribute decorator.
*/
export default function Attribute (value: any = null): PropertyDecorator {
return Field(model => model.attr(value))
export function Attribute (value: FunctorOrValue<any> = null): PropertyDecorator {
return Field(model => model.attr(unwrapFunctorOrValue(value)))
}

export default Attribute
13 changes: 13 additions & 0 deletions src/decorators/BelongsTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function BelongsTo (
parent: FunctorOrValue<typeof Model | string>,
foreignKey: FunctorOrValue<string>, ownerKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.belongsTo as any)(...[parent, foreignKey, ownerKey].map(unwrapFunctorOrValue)))
}

export default BelongsTo
15 changes: 15 additions & 0 deletions src/decorators/BelongsToMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function BelongsToMany (
related: FunctorOrValue<typeof Model | string>,
pivot: FunctorOrValue<typeof Model | string>,
foreignPivotKey: FunctorOrValue<string>, relatedPivotKey: FunctorOrValue<string>,
parentKey?: FunctorOrValue<string>, relatedKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.belongsToMany as any)(...[related, pivot, foreignPivotKey, relatedPivotKey, parentKey, relatedKey].map(unwrapFunctorOrValue)))
}

export default BelongsToMany
7 changes: 5 additions & 2 deletions src/decorators/Bool.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import PropertyDecorator from '../contracts/PropertyDecorator'
import { TypeOptions } from '../options/Options'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Primitive from './Primitive'

/**
* Create a bool decorator.
*/
export default function Bool (value: boolean | null, options?: TypeOptions): PropertyDecorator {
return Primitive(model => model.boolean(value), options)
export function Bool (value?: FunctorOrValue<boolean> | null, options?: TypeOptions): PropertyDecorator {
return Primitive(model => model.boolean(unwrapFunctorOrValue(value)), options)
}

export default Bool
41 changes: 41 additions & 0 deletions src/decorators/DecoratedModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Model } from '@vuex-orm/core'
import InheritanceTypes from '@vuex-orm/core/lib/model/contracts/InheritanceTypes'

// Creates an optional class decorator to be used as means to add static fields
export function DecoratedModel (
entityName: string,
options?: {
parentEntity?: string,
types?: InheritanceTypes,
typeKey?: string
}
): (target: any) => any | void {
return (target: any): any | void => {
const model = target.constructor as typeof Model

// Do this temporarily until upstream vuex-orm declare this field officially
// I want to use this to notify hot reloader in the future
;(model as any).isDecorated = true

model.entity = entityName

// generate base entity key assignment
if (options?.parentEntity) {
model.baseEntity = options.parentEntity
}

// generate type discriminator
if (options?.types) {
model.types = () => options.types!

// assignment type key for this entity
if (options?.typeKey) {
model.typeKey = options.typeKey
}
}

return target
}
}

export default DecoratedModel
10 changes: 6 additions & 4 deletions src/decorators/Field.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Model, Attribute } from '@vuex-orm/core'
import { Attribute, Model } from '@vuex-orm/core'
import PropertyDecorator from '../contracts/PropertyDecorator'

type Callback = (model: typeof Model) => Attribute
type Callback = (model: typeof Model, propertyKey: string) => Attribute

/**
* Create a generic field decorator.
*/
export default function Field (callback: Callback): PropertyDecorator {
export function Field (callback: Callback): PropertyDecorator {
return (target: Model, propertyKey: string): void => {
const model = target.constructor as typeof Model

Expand All @@ -18,6 +18,8 @@ export default function Field (callback: Callback): PropertyDecorator {
model.cachedFields[model.entity] = {}
}

model.cachedFields[model.entity][propertyKey] = callback(model)
model.cachedFields[model.entity][propertyKey] = callback(model, propertyKey)
}
}

export default Field
13 changes: 13 additions & 0 deletions src/decorators/HasMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function HasMany (
related: FunctorOrValue<typeof Model | string>, foreignKey: FunctorOrValue<string>,
localKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.hasMany as any)(...[related, foreignKey, localKey].map(unwrapFunctorOrValue)))
}

export default HasMany
13 changes: 13 additions & 0 deletions src/decorators/HasManyBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function HasManyBy (
parent: FunctorOrValue<typeof Model | string>, foreignKey: FunctorOrValue<string>,
ownerKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.hasManyBy as any)(...[parent, foreignKey, ownerKey].map(unwrapFunctorOrValue)))
}

export default HasManyBy
15 changes: 15 additions & 0 deletions src/decorators/HasManyThrough.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function HasManyThrough (
related: FunctorOrValue<typeof Model | string>,
through: FunctorOrValue<typeof Model | string>,
firstKey: FunctorOrValue<string>, secondKey: FunctorOrValue<string>,
localKey?: FunctorOrValue<string>, secondLocalKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.hasManyThrough as any)(...[related, through, firstKey, secondKey, localKey, secondLocalKey].map(unwrapFunctorOrValue)))
}

export default HasManyThrough
13 changes: 13 additions & 0 deletions src/decorators/HasOne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function HasOne (
related: FunctorOrValue<typeof Model | string>, foreignKey: FunctorOrValue<string>,
localKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.hasOne as any)(...[related, foreignKey, localKey].map(unwrapFunctorOrValue)))
}

export default HasOne
8 changes: 8 additions & 0 deletions src/decorators/Increment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import PropertyDecorator from '../contracts/PropertyDecorator'
import Field from './Field'

export function Increment (): PropertyDecorator {
return Field(model => model.increment())
}

export default Increment
13 changes: 13 additions & 0 deletions src/decorators/MorphMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function MorphMany (
related: FunctorOrValue<typeof Model | string>,
id: FunctorOrValue<string>, type: FunctorOrValue<string>, localKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.morphMany as any)(...[related, id, type, localKey].map(unwrapFunctorOrValue)))
}

export default MorphMany
13 changes: 13 additions & 0 deletions src/decorators/MorphOne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function MorphOne (
related: FunctorOrValue<typeof Model | string>,
id: FunctorOrValue<string>, type: FunctorOrValue<string>, localKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.morphOne as any)(...[related, id, type, localKey].map(unwrapFunctorOrValue)))
}

export default MorphOne
11 changes: 11 additions & 0 deletions src/decorators/MorphTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function MorphTo (
id: FunctorOrValue<string>, type: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.morphTo as any)(...[id, type].map(unwrapFunctorOrValue)))
}

export default MorphTo
15 changes: 15 additions & 0 deletions src/decorators/MorphToMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function MorphToMany (
related: FunctorOrValue<typeof Model | string>,
pivot: FunctorOrValue<typeof Model | string>,
relatedId: FunctorOrValue<string>, id: FunctorOrValue<string>, type: FunctorOrValue<string>,
parentKey?: FunctorOrValue<string>, relatedKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.morphToMany as any)(...[related, pivot, relatedId, id, type, parentKey, relatedKey].map(unwrapFunctorOrValue)))
}

export default MorphToMany
15 changes: 15 additions & 0 deletions src/decorators/MorphedByMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Field from './Field'

export function MorphedByMany (
related: FunctorOrValue<typeof Model | string>,
pivot: FunctorOrValue<typeof Model | string>,
relatedId: FunctorOrValue<string>, id: FunctorOrValue<string>, type: FunctorOrValue<string>,
parentKey?: FunctorOrValue<string>, relatedKey?: FunctorOrValue<string>
): PropertyDecorator {
return Field(model => (model.morphedByMany as any)(...[related, pivot, relatedId, id, type, parentKey, relatedKey].map(unwrapFunctorOrValue)))
}

export default MorphedByMany
7 changes: 5 additions & 2 deletions src/decorators/Num.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import PropertyDecorator from '../contracts/PropertyDecorator'
import { TypeOptions } from '../options/Options'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Primitive from './Primitive'

/**
* Create a num decorator.
*/
export default function Num (value: number | null, options?: TypeOptions): PropertyDecorator {
return Primitive(model => model.number(value), options)
export function Num (value?: FunctorOrValue<number> | null, options?: TypeOptions): PropertyDecorator {
return Primitive(model => model.number(unwrapFunctorOrValue(value)), options)
}

export default Num
21 changes: 21 additions & 0 deletions src/decorators/PrimaryKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import PropertyDecorator from '@/contracts/PropertyDecorator'
import { Model } from '@vuex-orm/core'

export function PrimaryKey (): PropertyDecorator {
return (target: Model, propertyKey: string): void => {
const model = target.constructor as typeof Model

// making the primary key values an array
// even if you have one and only one value there's no effect in functionality
if (!model.primaryKey) {
model.primaryKey = []
} else if (typeof model.primaryKey === 'string') {
const oldPrimaryKey = model.primaryKey
model.primaryKey = [oldPrimaryKey]
}

model.primaryKey.push(propertyKey)
}
}

export default PrimaryKey
25 changes: 16 additions & 9 deletions src/decorators/Primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,27 @@ type Callback = (model: typeof Model) => Type
/**
* Create a generic type decorator.
*/
export default function Primitive (callback: Callback, options?: TypeOptions): PropertyDecorator {
return Field((model) => {
export function Primitive (callback: Callback, options?: TypeOptions): PropertyDecorator {
return Field((model, propertyKey) => {
const type = callback(model)

if (type.value === null && !options?.nullable) {
throw new Error(
"[Vuex ORM] You've defined the default value of a field as `null` " +
'without enabling `nullable` option. If you want the field to accept' +
'`null`, set `nullable` option to `true`.'
)
if (
typeof type?.value !== 'number' // if that number is 0, what will happen?! always remember it was coerced to false
&& !(type?.value || options?.nullable)
) {
throw new Error(`
[Vuex ORM] You've defined the default value of a field as \`null\` without enabling \`nullable\` option.
If you want the field to accept\`null\`, set \`nullable\` option to \`true\`.
Problematic class name: ${model.name}; Related property key: ${propertyKey}
`)
}

options?.nullable && type.nullable()
if (options?.nullable) {
type.nullable()
}

return type
})
}

export default Primitive
7 changes: 5 additions & 2 deletions src/decorators/Str.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import PropertyDecorator from '../contracts/PropertyDecorator'
import { TypeOptions } from '../options/Options'
import { FunctorOrValue, unwrapFunctorOrValue } from '../utils'
import Primitive from './Primitive'

/**
* Create a str decorator.
*/
export default function Str (value: string | null, options?: TypeOptions): PropertyDecorator {
return Primitive(model => model.string(value), options)
export function Str (value?: FunctorOrValue<string> | null, options?: TypeOptions): PropertyDecorator {
return Primitive(model => model.string(unwrapFunctorOrValue(value)), options)
}

export default Str
20 changes: 20 additions & 0 deletions src/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export { Attribute } from './Attribute'
export { BelongsTo } from './BelongsTo'
export { BelongsToMany } from './BelongsToMany'
export { Bool } from './Bool'
export { DecoratedModel } from './DecoratedModel'
export { Field } from './Field'
export { HasMany } from './HasMany'
export { HasManyBy } from './HasManyBy'
export { HasManyThrough } from './HasManyThrough'
export { HasOne } from './HasOne'
export { Increment } from './Increment'
export { MorphedByMany } from './MorphedByMany'
export { MorphMany } from './MorphMany'
export { MorphOne } from './MorphOne'
export { MorphTo } from './MorphTo'
export { MorphToMany } from './MorphToMany'
export { Num } from './Num'
export { PrimaryKey } from './PrimaryKey'
export { Primitive } from './Primitive'
export { Str } from './Str'
Loading