@alt-point/active-models

Reactive DTO models with Proxy and more useful classes, decorators, Black-jack and whores

Usage no npm install needed!

<script type="module">
  import altPointActiveModels from 'https://cdn.skypack.dev/@alt-point/active-models';
</script>

README

@alt-point/active-models

Пакет с базовыми классами на es6/TS для упрощения работы со структурами данных.

Какие проблемы поможет решить?

  • Реализовать модели данных с реактивными свойствами (ActiveModel);
  • Контролировать целостность структур данных (ActiveModel.fillable, ActiveModel.hidden, ActiveModel.protected);
  • Контролировать тип и целостность данных в каждом конкретном свойстве в рантайме;

Installation

yarn

yarn add @alt-point/active-models

npm

npm install --save @alt-point/active-models

ActiveModel

Класс реализован с использованием Proxy.

Назначение: контроль целостности структуры и типов данных моделей приходящих из внешних источников/подсистем (DTO)

Пример, иллюстрирующий применение, пример с декораторами

Есть 2 стула 2 варианта, либо использовать статическое api, либо юзать декораторы:

@Decorators

@ActiveField(opts: ActiveFieldDescriptor)

type ActiveFieldDescriptor = object & {
    setter?: Setter<any> // ассессор на установку значения
    getter?: Getter<any> // ацессор на получение значения
    validator?: Validator<any> // валидатор на установку значения
    readonly?: boolean // поле модели будет доступно только на чтение
    hidden?: boolean // поле скрыто из перечисляемых свойств
    fillable?: boolean // поле доступно для установки и изменения
    protected?: boolean // запрещено удалять поле из модели
    attribute?: any // Значение по умолчанию для поля модели в момент создания объекта
    value?: any // алиас для `attribute`
}

Notice:

Для того, что бы были задействованы атрибуты (значения по умолчанию для полей модели) необходимо создавать экземпляр модели через фабричный метод ActiveModel.create(data), либо же определить логику установки значений в конструкторе явно, например так

 class SomeModeal extends ActiveModel {
   constructor (data?: any) {
       super(data)
       if (data) {
           this.fill(data)
       }
   }
 }

Static Api:

static get $attributes

Default value: {}

Description: Структура объекта по умолчанию, а так же значения свойств по умолчанию. Даже если значение не передано в data в конструкторе, будет установлено из $attributes

Example:

    static get $attributes () {
        return {
            id: '',
            login: '',
            password: '',
            createdAt: ''
        }                          
    }

static get fillable

Default value: []

Description: Массив имён свойств, которые могут быть установлены через конструктор. Если массив пуст, то можно устанавливать любые свойства. Если массив заполнен, то в модель можно установить только перечисленные свойства и их значения.

Example:

  static get fillable () {
    return [
        'id',
        'password',
        'property',
        'createdAt'
    ]
}

static protected

Default value: []

Description: Массив имён свойств, которые запрещено удалять у объекта, если массив пустой, то удалять можно любые свойства.

Example:

    static get protected () {
        return [
            'id',
            'login',
            'password',
            'createdAt'
        ]
    }

static hidden

Default value: []

Description: Массив имён свойств, которые не должны быть перечисляемыми, а следовательно и не будут попадать в вывод Object.keys, Reflect.ownKeys, JSON.stringify, etc.

Example:

    static get hidden () {
        return [
            'password'
        ]
    }

static setter${PascalPropertyName}(model, prop, value, receiver)

Description: Для того что бы определить сеттер для свойства модели необходимо добавить классу, унаследованному от ActiveModel статический метод с префиксом setter + имя в PascalCase свойства (разделители [-_.+*/:? ] не будут учитываться)

Example:

    static setterMyIntegerPropertyName (model, prop, value = 0, receiver) {
        Reflect.set(model, prop, Number.parseInt(value), receiver)
    }

static getter{PascalPropertyName}(model, prop)

Description: Для того что бы определить геттер для свойства модели необходимо добавить классу, унаследованному от ActiveModel статический метод с префиксом getter + имя в PascalCase свойства (разделители [-_.+*/:? ] не будут учитываться)

Example:

    static getterGoodsSumWeight (model) {
        return model.goods.reduce((a, { weight }) => a + weight, 0)        
    }

static sanitize(data)

Description: Статический метод преобразования/очистки данных переданных в конструктор.

По умолчанию возвращает lodash.cloneDeep(data).


CallableModel

Базовый класс, реализованный также через Proxy, чтобы можно было обращаться с объектом как с функцией.

Пример использования:


import { CallableModel } from '@alt-point/active-models'

class Notify extends CallableModel {
  // Define 
  __call (...args) {
      return this.success(...args)
  }
  
  success (successMessage) {
     alert(successMessage)
  }
  
  silent (message) {
    console.log('Silent message:' + message)
  }

}

Дальше можем создать объект класса Notify как плагин в Nuxt.js/Vue.js и использовать:

// плагин
export default (ctx, inject) => {
  ctx.$notify = new Notify()
  inject('notify', new Notify())
}


// в компоненте теперь можно юзать: 
this.$notify.silent('Write notice to console!')
this.$notify('Alert!')

Enum

enum реализованный на Map


const OrderStatuses = new Enum(['new', 'complete', 'shipping'], 'new')

OrderStatuses.values() // ['new', 'complete', 'shipping']
OrderStatuses.validate('G-gurda') // throw Value must be include one of type: new, complete, 'shipping; Provide value "G-gurda"
OrderStatuses.default // 'new'

TODO:

  • Добавить больше примеров использования;
  • Тесты;
  • TS;
  • Добавить примеры использования моделей на node.js;
  • Translate to english and others languages;

Credits

Alex D. Bubenchikov, surrealistik@alt-point.com