Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@vuex-orm/core": "^0.33.0"
},
"devDependencies": {
"@testdeck/jest": "0.0.6",
"@types/jest": "^24.0.23",
"codecov": "^3.5.0",
"jest": "^24.8.0",
Expand Down
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