From 92a38477ba00bf3a542d4759f3f8c0003d1e7d91 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Wed, 21 Oct 2026 10:37:45 +0400 Subject: [PATCH 1/2] =?UTF-8?q?=D0=A2=D0=B8=D0=BF=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B8=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B8=D1=80?= =?UTF-8?q?=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D0=B2=20=D1=81=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=B5=D0=BC=D0=B5=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=BE=D0=B4=D0=B8=D0=BD=20=D1=80=D0=B0=D0=B7=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=82=D0=B8=D0=BF=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8,=20=D0=B0=20=D0=BD=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BF?= =?UTF-8?q?=D0=B8=D0=BB=D0=B8=D1=80=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BA=D0=B0=D0=B6=D0=B4=D0=BE=D0=B9=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\260\320\277\320\270\321\201\321\214.md" | 10 +- ...20\260\320\277\320\270\321\201\321\214.os" | 67 ++++++++--- ...20\260\320\277\320\270\321\201\321\214.os" | 104 ++++++++++++++++++ 3 files changed, 164 insertions(+), 17 deletions(-) create mode 100644 "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" 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 7683c2b0..9b7f9d9a 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 42ad7002..f44c089b 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,16 @@ #Использовать decorator +#Использовать reflector +#Использовать collectionos + +// СинхронизированнаяКарта - Имена зарегистрированных типов записей: ключ - тип сущности, значение - имя типа. +Перем ТипыЗаписей; Функция ТипСущности(Сущность) Экспорт - ТипСущности = ТипЗнч(Сущность); - Если ТипСущности = Тип("Сценарий") Тогда - ТипСущности = ОбработкаДекоратора.ИсходныйТип(Сущность); + Если ОбработкаДекоратора.ЭтоДекоратор(Сущность) Тогда + Возврат ОбработкаДекоратора.ИсходныйТип(Сущность); КонецЕсли; - Возврат ТипСущности; + Возврат ТипЗнч(Сущность); КонецФункции // Создает экземпляр сущности, расширенный методами паттерна Active Record. @@ -15,6 +19,9 @@ // поэтому в методы записи его передавать не нужно, а операцию хранилище все равно делегирует // своему менеджеру. // +// Тип записи регистрируется в системе типов один раз на тип сущности: декоратор компилируется +// при первом создании, дальше экземпляры создаются как обычные объекты этого типа. +// // Параметры: // ОбъектМодели - ОбъектМодели - Модель сущности, экземпляр которой создается. // ХранилищеСущностей - ХранилищеСущностей - Хранилище типа этой сущности. @@ -23,32 +30,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 00000000..975ddd5d --- /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); +КонецПроцедуры From 2866e5c0a5e7ea18f006aa29be20ca7b38a1c862 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Wed, 21 Oct 2026 19:26:29 +0200 Subject: [PATCH 2/2] Remove unused reflector usage --- ...0\321\217\320\227\320\260\320\277\320\270\321\201\321\214.os" | 1 - 1 file changed, 1 deletion(-) 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 f44c089b..06f161d0 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,5 +1,4 @@ #Использовать decorator -#Использовать reflector #Использовать collectionos // СинхронизированнаяКарта - Имена зарегистрированных типов записей: ключ - тип сущности, значение - имя типа.