Egobrain

Erlang ORM. часть 3

Прошло уже очень много времени с момента как я писал про свои эксперименты с Erlang ORM.
И что я могу сказать?
Первое и самое, вероятно, печальное, подход оказался не жизнеспособным, Второе – несмотря на недостатки ORM ее использование помогло набраться опыта работы с postgresql из Erlang и получить представление о том как все действительно должно работать.

Где я допустил ошибки:
– Нельзя мешать в одной модели описание того как данные хранятся в базе и как будут отдаваться клиенту
– Не стоило мешать генерацию sql и непосредственную работу с БД в одном проекте
– Подход ActiveRecord – не самая лучшая идея для Erlang
– Слишком много parse_transform-a

За эти 2 года я очень часто сталкивался с генерацией sql в чужих проектах на Erlang и, к сожалению, единого, общепризнанного, да что там, хотя бы просто удобного решения нигде не встречал. Везде лишь ад из case блоков, сверток и iolist-ов. Что еще печальнее, коллеги все чаще стали поглядывать в сторону Elixir с его ecto. А уж совсем не хочется изучать, а тем более использовать в продакшене еще один язык.

Так на свет появился стек для работы с postgresql и моделями данных:
epgpool – простой пулл подключений к postgres. Очередной велосипед, если есть предложения чего-то получше – могу рассмотреть
dbschema – автоматические миграции наше все. Библиотек позволяет исполнять sql и erl up/down инструкции. Что убирает кучу работы по ручной раскладке и позволяет автоматизировать тестирование
emodel – библиотека для валидации входных данных. Та самая прослойка, которая должна отделять чистые, проверенные данные от мусора, который к нам прилетает. Отличается тем, что возвращает сразу все ошибки до которых может дотянуться.
equery – генерация sql, вдохновленная подходом Ecto
repo – одна из возможных реализаций CRUD библиотеки поверх equery и epgpool.

Сегодня я расскажу про repo и equery.

Erlang ORM. Часть 1.

Всем известно, что в Erlang нет привычных объектов из “классического” ООП, но они, по сути, и не нужны. Под словом объект, в данном случае, будет пониматься связка структура (record) и модуль, в котором описаны все функции для работы с этой структурой.

Приведу пример, который буду рассматривать на протяжении всех своих рассуждений:

Классический объект – User с полями login, email, password и salt (куда без нее). Его можно описать так (приставка db_ – необходима, т.к. в erlang уже есть модуль с таким именем).

db_user.erl
1
2
3
4
5
6
7
8
9
10
11
-module(db_user).

-record(db_user, {
    login,
    email,
    password,
    salt
}).

new() ->
    #db_user{}.

Я хочу описать два своих проекта tq_transform и tq_db. Которые суммарно генерируют объект позволяющий:

  1. Прятать реализацию, т.е. объекты за пределами модуля db_user не должны работать с record-ом напрямую. Это позволит в случае необходимости с легкостью поменять record, к примеру, на map.
  2. Задавать значения по умолчанию.
  3. Работать с “внешним миром”. Существует возможность указать какие поля и доступны для отправки внешнему пользователю ( к примеру через REST api ) и в каком виде, какие нет, какие только для чтения, какие только для записи.
  4. Конвертировать содержимое полей в/из proplist.
  5. Выводить список измененных полей.
  6. Задавать валидаторы как отдельных полей, так и всей модели в целом.
  7. Проводить загрузку модели в/из БД, притом представление полей в БД и Erlang может отличаться.
    1. При обновлении модели, подставлять в SQL запрос только обновленные поля.
    2. Задавать хуки на события: before_save, after_save, before_delete, after_delete

Обработка ошибок по умолчанию AngularJS

При работе c REST Api со страницы SPA часто возникает необходимость обработки ошибок. Все бы ничего, но часть ответов об ошибках касаются запрашиваемых данных, часть – могут быть ошибками общего вида c которыми тоже надо что-то делать. Предлагаю свое решение данной проблемы.