OneScript
OneScript copied to clipboard
Регистрация функции, вызываемой автоматически при инициализации неизвестного типа/обращению к неизвестному модулю
Хотелось бы иметь автозагрузчик сценариев.
Для этого достаточно возможности указать какую функцию нужно вызвать, если тип не определен.
При создании объекта с неизвестным типом движком будет вызван указанный метод. Если после завершения работы метода тип стал определен - создать объект этого типа и продолжить работу, в противном случае вызвать текущее исключение ("конструктор не найден")
// эта процедура будет подключать сценарии к системе типов
Процедура ПриПодключенииТипа(ИмяТипа)
ПодключитьСценарий("lib/" + ИмяТипа + ".os", ИмяТипа);
КонецПроцедуры
// устанавливаем процедуру, которая будет подключать сценарии
ПодключитьОбработчикЗагрузкиТипов("ПриПодключенииТипа", ЭтотОбъект);
// Тип ЗагрузчикМодулей здесь не определен
// Будет вызвана зарегистрированная ранее процедура, которая попробует подключить сценарий
Загрузчик = Новый ЗагрузчикМодулей;
Идея интересная, но хотелось бы узнать, что натолкнуло на нее? Лишние строчки кода для ПодключитьСценарий/ЗагрузитьСценарий? Неинтуитивный порядок загрузки? Проблема с подключением собственных библиотек?
Задача ясна, а вот цель непонятна. Какую задачу вы хотите решить?
Да, цель непонятна. Хотелось бы еще увидеть структуру проекта и клиентский код, который будет использовать указанную реализацию.
Похоже на #257
Я хочу решить сразу несколько проблем:
- Не хочу писать везде
ЗагрузитьСценарий()
илиИспользовать
при работе с собственными и чужими библиотеками, не входящими в стандартную
- Во-первых мне лень.
- во-вторых не хочу распространять по скриптам знание о том, что библиотеки лежат по определенным путям в файловой системе. Хочу, чтобы знание о том где брать модуль было сосредоточено только в одном объекте.
-
Меня не устраивают принципы работы opm. Я уже столкнулся с кучей проблем с пересечением имен, неправильной работой с зависимостями, боязнью развивать стандартную библиотеку из-за страха внести breaking change и другими. Глобальные пакеты приводят к проблемам. Хочу чтобы зависимости устанавливались, подключались и обновлялись локально для проекта, из директорию vendor/.
-
opm сосредоточен в одних руках и не дает возможности устанавливать пакеты, не входящие в стандартные. Чтобы попасть в стандартные нужно тоже связываться с владельцами opm и его хаба и зависеть от их решения. Я хочу создать более либеральный менеджер зависимостей, работа которого не будет зависеть от третьих лиц. Загрузил пакет на гитхаб - его сразу можно скачать/обновить.
В основном идея идет от менеджера зависимостей PHP Composer, который в свою очередь вдохновлен npm (node.js) и bundler (ruby).
main.os
/vendor
/leemuar
/стек.os
/queue.os
/silverbulleters
/src
/deployka.os
// main.os
// загрузит /vendor/leemuar/стек.os
Cтек = Новый leemuar_Стек;
// загрузит /vendor/silverbulleters/src/deployka.os
Деплойка = Новый silverbulleters_deployka;
Можно было бы реализовать все это через функцию-хелпер, но это во-первых дополнительные js-стайл сложности, во-вторых в oscript нет возможности добавлять глобальные функции.
Мне хотелось бы, чтобы была возможность регистрировать стек методов для подключения новых типов и символов (модулей). Это позволит гибко настраивать работу приложений. Ближайший аналог желаемого - http://php.net/manual/en/function.spl-autoload-register.php
- Подключение всего можно сделать в одной точке входа уже сейчас. Для этого надо просто создать головной скрипт и подключить там через Использовать весь подчиненный каталог.
- Работа с локальными зависимостями в опм тоже уже есть - это параметр -l при установке. Он ставит все зависимости в каталог oscript_modules, по аналогии с npm.
Переопоеделение путей поиска при подключении библиотек по именам тоже уже есть - для этого служит файл oscript.cfg рядом с точкой входа в приложение и/или скриптами, где идёт подключение.
Предлагаю все-таки недостатки устранить в рамках одного инструмента - opm. Тем более что в нем есть часть желаемого
ок, а возможность добавления автозагрузчика-то добавлять будете? Я бы тогда в opm его использование добавил
Авозагрузчика чего? Я пропустил возможно...
@EvilBeaver ну собственно то о чем в первом сообщении этого ишуза говориться: автозагрузчик типов и модулей
Я предлагаю следующее:
в движке есть стек, хранящий функции вызова. Когда с помощью оператора "Новый" инициализируется неизвестный движку тип - вызываются процедуры из этого стека по очереди. Если после их вызова новый тип все еще не определен - вызывать исключение. То же самое при обращении к неизвестному символу (для модулей).
В языке нужны функции, позволяющие добавить и удалить методы из этого стека.. Или глобальный объект-менеджер:
Процедура ОбработчикЗагрузки() КонецПроцедуры
Процедура ЕщеОдинОбработчикЗагрузки() КонецПроцедуры
МенеджерАвтозагрузки.ДобавитьОбработчик(ЭтотОбъект, "ОбработчикЗагрузки");
МенеджерАвтозагрузки.ДобавитьОбработчик(ЭтотОбъект, "ЕщеОдинОбработчикЗагрузки");
// первым будет вызываться последний добавленный обработчик
// сначала ЕщеОдинОбработчик()
// потом ОбработчикЗагрузки()
МойОбъект = Новый Мой_Супер_Объект;
Идея понятна. Но на уровне движка потребует серьезной допилки. И потом, нужен ведь не хендлер, а скорее сама возможность дополнения глобального контекста, разве нет?
@EvilBeaver возможность дополнения глобального контекста (по крайней мере для типов) уже есть в движке: ПодключитьСценарий(). #Использовать уже умеет создавать глобальные переменные (модули)), этого не достаточно?
Нужна только возможность "подписаться" на событие движка "тип не определен" и "символ (модуль) не определен", я думал в обработчике события использовать ПодключитьСценарий() и то что вызывает инструкция #Использовать для подключения глобальных переменных
Я так понял, что сейчас в языке нет возможности определять глобальные переменные, хотя инструкция #Использовать это умеет. т.е. в движке уже заложена возможность дополнять глоб контекст, просто нет в самом языке способов это сделать
Могу предложить чтобы не добавлять доп. конструкции в язык для дополнения глоб. контекста сделать так: добавить подписку на событие "символ не найден", обработчик которого должен вернуть объект или неопределено. Если обработчик вернет объект - он будет движком зарегистрирован в глоб контексте под указанным именем как модуль. Если обработчик вернет "неопределено" - ничего не делать.
Процедура ОбработчикЗагрузкиМодуля(ИмяМодуля) Экспорт
Если ФайлСуществует("src/" + ИмяМодуля + ".os")
Возврат ЗагрузитьСценарий("src/" + ИмяМодуля + ".os");
Иначе
// файла нет, подключать нечего
Возврат Неопределено;
КонецЕсли;
КонецПроцедуры
ДобавитьОбработчикАвтозагрузкиМодулей(ЭтотОбъект, "ОбработчикЗагрузкиМодуля");
// переменная ЮнитТестирование тут не определена
ЮнитТестирование.ПроверитьРавно();
// будет вызвана ОбработчикЗагрузкиМодуля с параметром "ЮнитТестирование"
// если она вернет Неопределено - вызовется исключение
// если вернет объект - движок зарегистрирует глобальную переменную с переданным в параметре именем (ЮнитТестирование). в котором будет храниться возвращенный объект
На данный момент будет достаточно хотя бы подписаться на событие "Тип не определен", чтобы в нем можно было использовать ПодключитьСценарий().
Я так понял, что сейчас в языке нет возможности определять глобальные переменные, хотя инструкция #Использовать это умеет. т.е. в движке уже заложена возможность дополнять глоб контекст, просто нет в самом языке способов это сделать
В языке есть методы ДобавитьКласс и ДобавитьМодуль. Они вызываются в package-loader
@nixel2007 Спасибо! Тогда вообще никаких проблем, нужны только подписки на события "тип не определен" и "символ не определен" - в обработчиках уже средствами языка подключим, используя эти функции.
насчет глобальных: такая возможность закладывалась, но сейчас Использовать этого не умеет из-за особенностей реализации "памяти" виртуальной машины. Расширять гл. контекст можно только свойствами, но не методами.
Неглобально - как описал @nixel2007 через кастомный загрузчик. А документация на методы - есть. Вот она: http://oscript.io/docs/page/package-loader
EvilBeaver проблема в том, что ошибка неизвестного символа генерируется во время компиляции модуля. А обработчик должен работать во время выполнения, т.е. когда компиляция уже завершена. Получается проблема курицы и яйца.
Получается проблема курицы и яйца.
Я правильно понял, что вот из-за этого мы ставим wontfix и закрываем?
Получается проблема курицы и яйца. Я правильно понял, что вот из-за этого мы ставим wontfix и закрываем?
имхо проблемы курицы и яйца на текущий момент нет, все однозначно.
До тех пор, пока в языке не было возможности создавать пользовательские типы - проверка во время компиляции была полностью оправдана. Как только в движке появилась возможность динамически создавать типы во время выполнения (ПодключитьСценарий()) - имхо проверка типов теперь тоже должна быть перенесена в рантайм.
Я бы не закрывал и оставил на потом в низком приоритете
@leemuar не вижу связи между "проверкой типов" и ошибкой "Символ не определен" на этапе компиляции. Разверни мысль, если не сложно?
@EvilBeaver
не вижу связи между "проверкой типов" и ошибкой "Символ не определен" на этапе компиляции
Да, ты прав, связи нет. Я ошибся и всех запутал. Для такого кода:
а = Новый МойОбъект;
выдается не ошибка компиляции "Символ не определен", а рантайм ошибка "Конструктор не обнаружен".
Если Конструктор не обнаружен - значит такого типа не зарегистрировано. метод "ПодключитьСценарий" регистрирует тип и он потом доступен. Я по-прежнему не понял вопроса...
Прочитал и захотел высказаться. С толкнулся с проблемой, следующего характера, невозможно добавить глобальный класс Как это делается в пакедж-лоадере через функцию "ДобавитьКласс", необходимо было для динамической подгрузки используемых классов в gitsync при подключении плагина. Сейчас решил кастылем: Автоматическим формированием файла package-loader.os в определенном каталоге. ( что не позволяет переопределить сам каталог и прочие не удобства) В том числе сразу про контролировать перечень загруженных классов/модулей, их потом приходить читать в файле lib.config (а если его нет? )
//////////////////////////////////////////////////////
// ДАННЫЙ ФАЙЛ ФОРМИРУЕТСЯ АВТОМАТИЧЕСКИ //
// ВСЕ ВНЕСЕННЫЕ ИЗМЕНЕНИЯ В РУЧНУЮ БУДУТ ПОТЕРЯНЫ //
//////////////////////////////////////////////////////
///////////////////////////////////
// ИМПОРТ БИБЛИОТЕКИ ПЛАГИНА "preinstalled_plugins_gitsync"
#Использовать "./preinstalled_plugins_gitsync"
///////////////////////////////////
Процедура ПриЗагрузкеБиблиотеки(Путь, СтандартнаяОбработка, Отказ)
СтандартнаяОбработка = Ложь;
КонецПроцедуры
Если Конструктор не обнаружен - значит такого типа не зарегистрировано. метод "ПодключитьСценарий" регистрирует тип и он потом доступен. Я по-прежнему не понял вопроса...
@EvilBeaver совершенно верно. Однако в рантайме нельзя подключить модуль:
ДобавитьКласс("Мой.os", "МойМодуль");
МойМодуль.Вперед()
приводит к ошибке компиляции "Неизвестный символ". Об этом и пишет @khorevaa : неудобно подключать модуль через #использовать, хотелось бы подключать модули в рантайме напрямую вызовом спецфункции.
Ведь такая конструкция работает без ошибок компиляции:
МойМодуль = ЗагрузитьСценарий("Мой.os");
МойМодуль.Вперед();
Это напрямую влияет на это ишуз: если проверка модулей происходит на этапе компиляции, значит добавить собственный обработчик на oscript для этого события будет нельзя. А хочется, чтобы было можно.
@khorevaa я не понял вопроса, извини.
@leemuar смотри: package-loader - это класс движка, имеющий собственный модуль. Как в 1С: документ это платформенный класс, но его "модуль документа" это скрипт, который "видит" метод Записать(), свойства Номер, коллекцию Табличных частей и т.п.
Также и loader. Это платформенный класс с модулем. Метод ДобавитьКласс и ДобавитьМодуль - это методы платформенного класса инкапсулированные внутри него и видимые только из модуля этого класса.
Загружать "Модули" в рантайме нельзя, т.е. это изменит глобальный контекст. Метод "ЗагрузитьСценарий" не создает "модуль" в глобальном контексте, он создает обычный объект, который возвращает тебе же.
Загружать "Модули" в рантайме нельзя, т.е. это изменит глобальный контекст
@EvilBeaver понятно что нельзя сейчас. И о причинах ты писал - особенности реализации памяти. Мы с @khorevaa хотели бы, чтобы стало можно когда-нибудь
У меня вот мозг почему-то хочет воспринимать классы как обработки. Может быть это и удовлетворит всех? Т.е. создание обработки из файла скрипта по аналогии с 1С. Это же будет функционально тот же класс
ps Или ЗагрузитьСценарий() сейчас так и работает?