diff --git "a/docs/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.md" "b/docs/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.md" index 7683c2b..9b7f9d9 100644 --- "a/docs/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.md" +++ "b/docs/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.md" @@ -1,5 +1,9 @@ ## АктивнаяЗапись +Сущность, созданная менеджером (`СоздатьЭлемент`) или прочитанная из базы, — запись с методами сохранения, чтения и удаления себя. + +Тип записи регистрируется в системе типов один раз на тип сущности под именем `ЗаписьСущности_<ТипСущности>`: у всех записей одного типа сущности один и тот же тип, `ТипЗнч(Запись)` возвращает его, а не `Сценарий`. Исходный тип сущности дает `АктивнаяЗапись.ТипСущности(Запись)`. Хранилище и модель данных запись получает от создавшего ее менеджера, поэтому записи разных менеджеров одного типа сущности сохраняются каждая в свой источник. + ### Сохранить ```bsl @@ -8,12 +12,12 @@ Процедура Сохранить() ``` -### Получить +### Прочитать ```bsl -// Прочитать текущую сущность. Для чтения в сущностей должно быть заполнено поле, являющееся идентификатором. +// Прочитать текущую сущность. Для чтения у сущности должно быть заполнено поле, являющееся идентификатором. // -Процедура Получить() +Процедура Прочитать() ``` ### Удалить diff --git "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" index 42ad700..06f161d 100644 --- "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" +++ "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" @@ -1,12 +1,15 @@ #Использовать decorator +#Использовать collectionos + +// СинхронизированнаяКарта - Имена зарегистрированных типов записей: ключ - тип сущности, значение - имя типа. +Перем ТипыЗаписей; Функция ТипСущности(Сущность) Экспорт - ТипСущности = ТипЗнч(Сущность); - Если ТипСущности = Тип("Сценарий") Тогда - ТипСущности = ОбработкаДекоратора.ИсходныйТип(Сущность); + Если ОбработкаДекоратора.ЭтоДекоратор(Сущность) Тогда + Возврат ОбработкаДекоратора.ИсходныйТип(Сущность); КонецЕсли; - Возврат ТипСущности; + Возврат ТипЗнч(Сущность); КонецФункции // Создает экземпляр сущности, расширенный методами паттерна Active Record. @@ -15,6 +18,9 @@ // поэтому в методы записи его передавать не нужно, а операцию хранилище все равно делегирует // своему менеджеру. // +// Тип записи регистрируется в системе типов один раз на тип сущности: декоратор компилируется +// при первом создании, дальше экземпляры создаются как обычные объекты этого типа. +// // Параметры: // ОбъектМодели - ОбъектМодели - Модель сущности, экземпляр которой создается. // ХранилищеСущностей - ХранилищеСущностей - Хранилище типа этой сущности. @@ -23,32 +29,64 @@ // Произвольный - Экземпляр класса сущности с методами Прочитать, Сохранить и Удалить. // Функция Создать(ОбъектМодели, ХранилищеСущностей) Экспорт - Сущность = Новый(ОбъектМодели.ТипСущности()); + Запись = Новый(ИмяТипаЗаписи(ОбъектМодели.ТипСущности())); + + // Хранилище и модель у каждого менеджера свои, поэтому они привязываются к экземпляру, + // а не к зарегистрированному типу. + Рефлектор = Новый Рефлектор(); + Рефлектор.УстановитьСвойство(Запись, "_ХранилищеСущностей", ХранилищеСущностей); + Рефлектор.УстановитьСвойство(Запись, "_ОбъектМодели", ОбъектМодели); + + Возврат Запись; +КонецФункции - Декоратор = Новый ПостроительДекоратора(Сущность) +// Регистрирует тип записи для типа сущности. Вызывается картой типов под ее блокировкой. +// +// Параметры: +// ТипСущности - Тип - Тип сущности. +// +// Возвращаемое значение: +// Строка - Имя зарегистрированного типа записи. +// +Функция ЗарегистрироватьТипЗаписи(ТипСущности) Экспорт + ИмяТипа = "ЗаписьСущности_" + ТипСущности; + + Построитель = Новый ПостроительДекоратора(ТипСущности) .Импорт(Новый Импорт("decorator")) - .Поле(Новый Поле("_ХранилищеСущностей") - .ЗначениеПоУмолчанию(ХранилищеСущностей)) + .Поле(Новый Поле("_ХранилищеСущностей")) - .Поле(Новый Поле("_ОбъектМодели") - .ЗначениеПоУмолчанию(ОбъектМодели)) + .Поле(Новый Поле("_ОбъектМодели")) .Метод(Новый Метод("Прочитать") .Публичный() .ТелоМетода("_ДанныеСущности = _ХранилищеСущностей.ПолучитьОдно(_ОбъектМодели.ПолучитьЗначениеИдентификатора(ЭтотОбъект)); |ОбработкаДекоратора.СинхронизироватьПоля(_ДанныеСущности, ЭтотОбъект);")) - .Метод(Новый Метод("Сохранить") .Публичный() .ТелоМетода("_ХранилищеСущностей.Сохранить(ЭтотОбъект);")) .Метод(Новый Метод("Удалить") .Публичный() - .ТелоМетода("_ХранилищеСущностей.Удалить(ЭтотОбъект);")) + .ТелоМетода("_ХранилищеСущностей.Удалить(ЭтотОбъект);")); + + Построитель.ЗарегистрироватьВСистемеТипов(ИмяТипа); - .Построить(); + Возврат ИмяТипа; +КонецФункции + +Функция ИмяТипаЗаписи(ТипСущности) + ИмяТипа = ТипыЗаписей.ПолучитьИлиУмолчание(ТипСущности, Неопределено); + Если ИмяТипа <> Неопределено Тогда + Возврат ИмяТипа; + КонецЕсли; - Возврат Декоратор; + // Проверка и регистрация под одной блокировкой: тип регистрируется ровно один раз на процесс. + Возврат ТипыЗаписей.ВычислитьЕслиОтсутствует( + ТипСущности, + "(ТипСущности) -> АктивнаяЗапись.ЗарегистрироватьТипЗаписи(ТипСущности)" + ).Получить(); КонецФункции + +ТипыЗаписей = Новый СинхронизированнаяКарта(Новый КартаСоответствие()); diff --git "a/tests/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" "b/tests/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" new file mode 100644 index 0000000..975ddd5 --- /dev/null +++ "b/tests/\320\220\320\272\321\202\320\270\320\262\320\275\320\260\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" @@ -0,0 +1,104 @@ +// Записи сущностей: один зарегистрированный тип на тип сущности, привязка записи к своему менеджеру. +// +// BSLLS:LatinAndCyrillicSymbolInWord-off +// BSLLS:DuplicateStringLiteral-off + +#Использовать ".." +#Использовать "utils" + +// ИсточникДанных - Источники, созданные тестом; закрываются после каждого теста. +Перем Источники; + +&ПередКаждым +Процедура ПередЗапускомТеста() Экспорт + ПодключитьСценарий("tests/fixtures/ПростойОбъект.os", "ПростойОбъект"); + Источники = Новый Массив; +КонецПроцедуры + +&ПослеКаждого +Процедура ПослеЗапускаТеста() Экспорт + Для Каждого Источник Из Источники Цикл + Источник.Закрыть(); + КонецЦикла; +КонецПроцедуры + +Функция НовыйМенеджер(ИмяИсточника) + Источник = Новый ИсточникДанных(ИмяИсточника, Тип("КоннекторInMemory"), ИмяИсточника); + Источник.УстановитьАвтоЗакрытие(Ложь); + Источники.Добавить(Источник); + + Менеджер = Новый МенеджерСущностей(Источник); + Менеджер.ДобавитьКлассВМодель(Тип("ПростойОбъект")); + Менеджер.Инициализировать(); + + Возврат Менеджер; +КонецФункции + +&Тест +Процедура ЗаписиОдногоТипаСущностиИмеютОдинТип() Экспорт + Менеджер = НовыйМенеджер("записи одного типа"); + + Первая = Менеджер.СоздатьЭлемент(Тип("ПростойОбъект")); + Вторая = Менеджер.СоздатьЭлемент(Тип("ПростойОбъект")); + + Ожидаем.Что(ТипЗнч(Первая), "тип первой записи").Равно(Тип("ЗаписьСущности_ПростойОбъект")); + Ожидаем.Что(ТипЗнч(Вторая), "тип второй записи").Равно(ТипЗнч(Первая)); + Ожидаем.Что(Первая = Вторая, "записи - разные объекты").ЭтоЛожь(); +КонецПроцедуры + +&Тест +Процедура ЗаписьСохраняетЧитаетИУдаляетСебя() Экспорт + Менеджер = НовыйМенеджер("методы записи"); + + Запись = Менеджер.СоздатьЭлемент(Тип("ПростойОбъект")); + Запись.Идентификатор = "1"; + Запись.Поле = "до"; + Запись.Сохранить(); + + Найденная = Менеджер.ПолучитьОдно(Тип("ПростойОбъект"), "1"); + Ожидаем.Что(Найденная.Поле, "поле после сохранения").Равно("до"); + + Найденная.Поле = "после"; + Найденная.Сохранить(); + Запись.Прочитать(); + Ожидаем.Что(Запись.Поле, "поле после чтения").Равно("после"); + + Запись.Удалить(); + Ожидаем.Что(Менеджер.Получить(Тип("ПростойОбъект")).Количество(), "записей после удаления").Равно(0); +КонецПроцедуры + +&Тест +Процедура ЗаписьПривязанаКСвоемуМенеджеру() Экспорт + Первый = НовыйМенеджер("первый менеджер"); + Второй = НовыйМенеджер("второй менеджер"); + + ЗаписьПервого = Первый.СоздатьЭлемент(Тип("ПростойОбъект")); + ЗаписьПервого.Идентификатор = "1"; + ЗаписьПервого.Поле = "первый"; + ЗаписьПервого.Сохранить(); + + ЗаписьВторого = Второй.СоздатьЭлемент(Тип("ПростойОбъект")); + ЗаписьВторого.Идентификатор = "2"; + ЗаписьВторого.Поле = "второй"; + ЗаписьВторого.Сохранить(); + + Ожидаем.Что(Первый.Получить(Тип("ПростойОбъект")).Количество(), "записей у первого").Равно(1); + Ожидаем.Что(Первый.ПолучитьОдно(Тип("ПростойОбъект"), "1").Поле, "поле записи первого").Равно("первый"); + Ожидаем.Что(Второй.Получить(Тип("ПростойОбъект")).Количество(), "записей у второго").Равно(1); + Ожидаем.Что(Второй.ПолучитьОдно(Тип("ПростойОбъект"), "2").Поле, "поле записи второго").Равно("второй"); +КонецПроцедуры + +&Тест +Процедура МенеджерПринимаетЗаписьПоТипуСущности() Экспорт + Менеджер = НовыйМенеджер("запись через менеджер"); + + Запись = Менеджер.СоздатьЭлемент(Тип("ПростойОбъект")); + Запись.Идентификатор = "1"; + Запись.Поле = "через менеджер"; + Менеджер.Сохранить(Запись); + + Ожидаем.Что(Менеджер.ПолучитьОдно(Тип("ПростойОбъект"), "1").Поле, "поле").Равно("через менеджер"); + + Менеджер.Удалить(Запись); + Ожидаем.Что(Менеджер.Получить(Тип("ПростойОбъект")).Количество(), "записей после удаления").Равно(0); +КонецПроцедуры