0.0.31 • Published 10 months ago

@itgenio/edik-reflection v0.0.31

Weekly downloads
-
License
ISC
Repository
-
Last release
10 months ago

Reflection

  • зависимость nanoid для генерации ref id

Общее

Type.$$gemeta

Все данные об объекте хранятся в его типе в поле $$gemeta. Удобно хранить связанные с типом мета-данные в его прототипе.

Дальнейшим развитием может быть сохранение не по строчному ключу, а символьному (Symbol, так делает Mobx)

Какие мета-данные есть у типа?

  • name:string используется для поиска типа в качестве уникального идентификатора.

Можно было бы использовать имя конструктора, но после минификации оно становится не пригодным для использования.

  • displayName?:string для красивости
  • isComponent?:boolean компонент или нет (доп. атрибут для инспектора)
  • fields?:{ name:string }[] поля
  • inline?:boolean нужно ли инлайнить,т.е. вставлять объекты такого типа по месту не вынося в map.

Для типа SerializedRootObject есть поддержка версионирования. (текущая версия схемы будет лежать в поле __meta.v)

Про обычные и inline-типы

Библиотека позволяет сериализовывать циклические зависимости, а так же множественные ссылки на один объект и т.д.

Это работает благодаря тому, что все обработанные объекты помещяются в отдельный список. Перед тем, как обработать следующий объект, вначале проверяется, что он еще не обработан(т.е. его нет в списке).

Если объект есть в списке - значит мы уже обработали такой объект и можно идти дальше.

При этом, в поле будет записан идентификатор ссылки на этот объект. Т.е. сколько бы полей не ссылалось на данный объект, все они будут ссылаться на один конкретный экземпляр.

Важно: при копировании полей объекта такие ссылки на объекты не будут копироваться.

Пример:

class SomeComponent extends CBaseComponent {
  field: CBaseComponent;// предположим наш компонент ссылается на какой-то компонент.
}

const comp:SomeComponent = /*...*/;

const copy = comp.clone(); //копируем

assert(comp.field === copy.field);//!!! хотя сам компонент скопировался, ссылка при этом указывает на старый объект!

Такие объекты имеют Обычный тип.

В некоторых случаях хочется, чтобы объект не выносился в такой список и не мог иметь ссылок на себя кроме одной.

Для этого нужны inline-типы.

Самый простой пример - тип Color. Мы не хотим чтобы на него ссылались разные объекты, при этом при копировании создается новый экземпляр данного типа.

Пример:

class SomeComponent extends CBaseComponent {
  color: Color; //Color - inline-тип
}

const some:SomeComponent = ...;
const copy = some.copy(); //копируем

assert(some.color !== copy.color);//ссылки разные, т.к. цвет скопировался!

TODO

  • сократить размер?
    • если в массиве(+ set и map) все элементы - ссылки, то можно применить специальный флаг.(т.к. в большинстве случаев массивы однотипны)

Процесс регистрации

  • берем тип
  • регистрируем его поля в мету
  • далее идет по иерархии ниже и регистрируем следующий тип, если нужно.
  • так же регистрируем в общей хранилище Object.$$gemetamap : Map<string, Type> чтобы в дальнейшем из любого места могли получить тип по имени.

Если на верхнем уровне есть поле с именем, как и на нижних уровнях, то будет браться поле из верхнего уровня.

class CBase {
  id: string; //для CBase регаем id=>string
}

class CSome extends CBase {
  id: number; //для CSome регаем id=>number;
}

//getField(CSome,'id') => number;
//getField(CBase,'id') => string;

Early(ранняя) регистрация

Возможна такая ситуация, когда базовый класс не успел зарегистрироваться, но уже регистрируется наследник.

В таком случае наступает ранняя регистрация для базового типа. Это значит:

  • мы не знаем точно про доп. атрибуты и имя
  • мы знаем только о полях

Поэтому, для базового типа регистрируются поля и ставиться флаг, что была ранняя регистрация.

Сериализация

Поддержка и фичи

Типы:

  • type Primitives = string | number | boolean
  • Array<any>
  • Set<any>
  • Map<string, any>

Есть таблица

type ObjId = string;
type ObjData = any;

const objects = new Map<ObjId, ObjData>();

Все объекты(за исключением inline-типов) сериализуются в таблицу. Ссылки на такие объекты сериализуются как строки-указатели на ID объекта из таблицы.

Сериализация

У Serialized*** есть поле __meta.map хранящее все объекты, на которые могут ссылаться внутренние объекты.

Десериализация

Для объектов:

  • ID
  • тип
  • остальные данные

Тип при этом должен быть зареган в хранилище типов либо указан явно при десериализации.

Десериализация происходит в два этапа.

Первый этап

На первом этапе создаются экземпляры объектов. При этом берется тип из сериализованного объекта.

Второй этап

На втором этапе объекты связываются друг с другом

Объекты

Запоминаем ID и помечаем поле как ссылка на объект

Массивы

Запоминаем ID и помечаем, какие индексы как ссылки

Что не работает (так задумано)

Ссылки на общий raw object, raw array

import { Serializer } from './serializer';

const s1 = new Type();
const s2 = new Type();

const sharedRawObj = { a: '1' };

s1.raw = s2.raw = sharedRawObj; //вот так не работает!

const obj = { s1, s2 };

const dobj = Serializer.serialize(Serializer.deserialize(obj));

//после десериализации, dobj.s1.raw !== dobj.s2.raw, ссылка не сохранилась

PoC / Ideas

External links

Иногда требуется сериализовать и десериализовать объект, но при этом, часть объектов, на которые ссылаемся, не находятся в иерархии и хочется сохранить ссылку на них после десериализации.

@gtype({ name: 'SomeType' })
class SomeType {
  @gserializable()
  id: string = 'randomId';

  @gserializable()
  externalLink?: SomeType;
}

const exernalObj = new SomeType();

const obj = new SomeType();
obj.externalLink = exernalObj;

Как сейчас:

const dobj = Serializer.deserialize(Serializer.serialize(obj));

assert(dobj.externalLink !== exernalObj); // сейчас вот так!

Предложение:

//serialize

const externalLinks = [externalObj];

const serialized = Serializer.serialize(obj, { externalLinks });

// deserialize

const dobj = Serializer.deserialize(serialized, { externalLinks: allObjsAndComps });

assert(dobj.externalLink === exernalObj);

interface ILinkable {
  id: string;
}

Подробнее:

Когда встречаем объект, который находится в externalLinks, подменяем тип на новый тип ExternalLink:

class ExternalLink {
  id: string;
}
0.0.31

10 months ago

0.0.31-beta.3

10 months ago

0.0.31-beta.1

10 months ago

0.0.31-beta.2

10 months ago

0.0.31-beta.0

10 months ago

0.0.26-beta.0

1 year ago

0.0.28-beta.0

1 year ago

0.0.30

1 year ago

0.0.26

1 year ago

0.0.26-beta.2

1 year ago

0.0.27

1 year ago

0.0.26-beta.1

1 year ago

0.0.28

1 year ago

0.0.29

1 year ago

0.0.24

1 year ago

0.0.25

1 year ago

0.0.24-beta.1

1 year ago

0.0.24-beta.0

1 year ago

0.0.21

1 year ago

0.0.22

1 year ago

0.0.23

1 year ago

0.0.21-beta.0

1 year ago

0.0.20

2 years ago

0.0.19

2 years ago

0.0.16

2 years ago

0.0.17

2 years ago

0.0.18

2 years ago

0.0.10

2 years ago

0.0.11

2 years ago

0.0.12

2 years ago

0.0.13

2 years ago

0.0.14

2 years ago

0.0.15

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago