Mojolicious & Mojo
отдельно от Mojolicious, другие, в т.ч. автор, не хотят.
Ну так, забавно, как они дерутся.
Интересно поведение wantarray и функций, пытающихся угадать поведение пользователя. Вот такой простой код позволяет нам одной подпрограммой выдавать и скаляр, и массив, смотря что нас попросят:
А вот интересное поведение этой же функции, для меня кажущееся крайне неожиданным.
Недавно с этим столкнулся, надо бы запомнить: goto запрещено использовать в given-when.
$ perl -E '$a = 1; given($a) { when(1) { goto ENDING } } ENDING: say "ENDING"'
panic: goto at -e line 1.
Никто не делал, наверное, полноценное тестирование производительности всех или почти всех методов и подходов, реализующих объекты в перле. Никто не делал, и я не буду.
Вместо этого я понаделал тупые тесты при помощи команды time perl myscript.pl. Просто для того, чтобы кое-что прояснить для себя.
1. Чистый перл, атрибуты в массиве
Массивы в перле быстрые. Пробуем такой код:
Для проверки код почти всегда один и тот же:
Что показывает time:
real 0m0.029s
user 0m0.024s
sys 0m0.000s
2. Чистый перл, атрибуты в хеше
Хеши в перле медленные, медленнее, чем массивы, поэтому, возможно, если делать акцессоры на хеше, то будет медленнее. Пробуем:
Время выполнения:
real 0m0.033s
user 0m0.020s
sys 0m0.008s
В общем, по ощущениям, разницы нет, хотя, судя по замерам, таки массивы быстрее. Вероятнее всего, разница может существенно проявиться при более сложной архитектуре классов, но это еще предстоит протестировать.
3. Используем Mojo::Base
Mojo::Base — удивительно легковесная штука с очень простым, незамутненным синтаксисом, реализующая базовые возможности объектов. Давно хотел протестить, насколько велика цена синтаксического сахара Mojo::Base:
Код сократился, а время выполнения? Смотрим:
real 0m0.032s
user 0m0.024s
sys 0m0.000s
Вообще, в среднем, такой код получается сравним по скорости с предыдущим, или, если все же придираться, то чууууть-чуть медленнее. Но есть Mojo::Base::XS, который, возможно, решит такую проблему. Неожиданно приятно.
4. Используем Class::Struct
Class::Struct очень старый, но, тем не менее, очень известный модуль, к которому приложились довольно известные, именитые перл-программисты. Поэтому его было тоже интересно.
В итоге:
real 0m0.050s
user 0m0.012s
sys 0m0.016s
В целом медленнее, причем заметно.
5. Используем Moose
Moose в моем списке играет роль пугала, не более того. Всерьез я его рассматривать бы не стал: ни по синтаксису, ни по производительности он мне лично не подходит.
Шок:
real 0m0.419s
user 0m0.320s
sys 0m0.032s
В заключение еще раз повторю, что тесты тупые, о производительности тех или иных решений можно судить относительно. Mojo::Base интересен и, на мой взгляд, нуждается в более взрослых сравнительных тестах. Но это в будущем.
В новой версии добавлены некоторые полезные функции, например, при повторной загрузке таблицы, предыдущая версия удаляется из памяти. Это сделано для того, чтобы не вызывать падение системы и иметь некий предсказуемый положительный результат.
Загрузка таблиц теперь происходит в любом месте программы, благодаря новому хелперу any_data, который доступен теперь для вызова методов load_data и func в любом месте кода, как экшенов, так и темплейтов (хе-хе), а не только во время запуска приложения, как раньше.
Исправлены некоторые мелкие баги, добавлена/улучшена документация.
На гитхабе: https://github.com/shootnix/Mojolicious-Plugin-AnyData
На CPAN'e: http://search.cpan.org/~shootnix/Mojolicious-Plugin-AnyData-1.20/lib/Mojolicious/Plugin/AnyData.pm
По-прежнему, принимаются замечания или предложения для дальнейшего развития плагина. Заранее всем спасибо.
Если внутри функции использовать массив @_, то это повлияет на целостность данных вне функции:
Если из массива вынимать данные и использовать их, то с внешними переменными все будет в порядке:
Эту разницу нужно учитывать или даже использовать, кому как больше нравится.
Какое-то время назад я, просматривая на досуге один фреймворк на джаве, был обрадован интересной фиче — возможности прямо из yaml'а грузить в память некоторые структурированные данные и работать с ними с помощью ORM (Hybernate, кажется) так, как будто это данные, взятые из базы данных.
Тогда я подумал, нет ли способа организовать что-то похожее для Mojolicious. Был найден годный модуль DBD::AnyData, осталось только сделать плагин. Помимо всего прочего, добавлено несколько методов (пока только два) для удобства пользования плагином.
Ссылка: https://github.com/shootnix/Mojolicious-Plugin-AnyData. Модуль пока не публикую на спане, желая для начала собрать некоторое кол-во багрепортов и предложений по улучшению. Отсюда просьба: критикуйте, замечайте, высказывайтесь, буду очень рад любому фидбэку. Ну и от посильной помощи не откажусь ;-)
Известно, что Perl — динамический язык. Кроме всего прочего это означает создание переменных «на лету». Бороться с внезапно созданными переменными из-за простой опечатки мы знаем как — use strict.
Но динамически создаются, как мы знаем, не только переменные, но и ключи хэша или ссылки на хэш, в этом случае use strict не помогает.
Допустим такую задачу: у вас есть массив, из которого нужно взять пять элементов. В качестве элементов у нас — хэш. Задача решается просто:
Думаю, объяснять этот пример не нужно, однако, есть условие, которое довольно просто не заметить: что, если $arrayref был изначально пуст? Что станет с ним после этих манипуляций? Правильно, он будет заполнен!
В данном конкретном случае Perl заполнит $arrayref пустыми хэшами, которые создались динамически при помощи вызова $arrayref->[$i]->{somekey}.
Что делать, если это не входит в наши планы? Самый простой и надежный способ, конечно, следить внимательно за всем самому, не допуская подобного со стороны Perl'a. Но, немного порывшись на спане, было найдено решение для тех, кто ценит заботу о своем коде со стороны автоматических программ и решений:
Теперь прагма будет следить за тем, чтобы Perl не бесчинствовал почем зря, создавая динамически ненужные хэши.
Например, такая задача. У нас есть переменная, в которую нужно поместить или значение по-умолчанию или значение из функции. Функция возвращает или значение, или undef. В дополнение ко всему, есть два замечания:
1. Функция или вызывается или нет, т.е. это дополнительное условие.
2. Переменная должна _всегда_ иметь положительное значение, т.е., если функция возвратит undef, нужно поместить в нее значение по-умолчанию.
Вариантов решения много. Самый простой вариант напрашивается еще до того, как прочитаешь задачу до конца.
Недостаток только один: нужно еще проверить, что содержит $value на выходе и присвоить значение по-умолчанию, если там undef.
Конечно, этот вариант быстро правится на такой:
Тут в случае, если function() вернет undef, мы имеем возможность подкорректировать значение переменной «на лету». Немного смущает такое количество строк в решении, в сущности, тривиальной задачи, поэтому перепишем с использованием тернарного оператора:
Все хорошо, однако выглядит по-прежнему не очень из-за повторяющихся кусков. Подумав, решение приходит: