Конфигурация elixir-приложений
Предисловие
На этот очевидный пост меня побудили люди, которые перешли из мира интерпретируемых языков, но так и не поняли, куда они попали.
В чём проблема-то?
А проблема в том, что elixir — язык компилируемый. Поэтому писать в config.exs
выражение System.get_env
— полнейшая глупость.
config :hello_project,
some_super_configurable_value: System.get_env("SOME_SUPER_CONFIGURABLE_VALUE")
или
config :hello_project,
some_super_configurable_value: System.get_env("SOME_SUPER_CONFIGURABLE_VALUE") || "some default value"
Вот такой код я зачастую вижу от недавних рельсоделов.
К чему это приведёт? А вот к чему.
Компилируемый конфиг
Конфиг, что характерно, тоже скомпилируется. А это значит, что код System.get_env
выполнится во время компиляции. И в конфигурацию попадёт значение из окружения сборки приложения, а не исполнения.
Кулхак для обхода ситуации
Сообщество elixir-программистов нашло выход. Выход не в виде переделывания сборки языка, а в виде соглашения.
Для динамического (времени исполнения программы, а не времени компиляции) чтения переменных окружения принято писать в конфиг значение {:system, "SOME_VARIABLE_NAME"}
, где SOME_VARIABLE_NAME
— имя переменной окружения.
Но просто так это не сработает. Нужно, чтобы читающий код умел такие случаи обрабатывать.
Вот пример кода обработчика:
defmodule ReadConfig
def read_config({:system, environment_variable_name}) do
System.get_env(environment_variable_name)
end
def read_config(value) do
value
end
end
Соответственно, значение получаем в коде не так:
Application.get_env(:my_application, :my_configuration)
а так:
ReadConfig.read_config(Application.get_env(:my_application, :my_configuration))
(Приведённый код является условным и намеренно ухудшеным/упрощённым)
А теперь про библиотеки
И вот тут начинается самое интересное. Для того, чтобы это всё работало не только в вашем коде, но и в библиотечном, нужно, чтобы библиотека поддерживала такое же чтение конфига для {:system, "SOME_VAR_NAME"}
. С этим, на самом деле, всё не очень хорошо.
Пример из phoenix, который мне много чего сломал:
url: [host: {:system, "HOST"}, port: {:system, "PORT"}, scheme: "http"],
А если я хочу крутить протоколом доступа из переменных окружения? А вот нельзя. Пиши руками в конфиге. Или пиши в dev
/prod
/test
конфиги отдельно. Стыд-то какой! Поменял протокол — собирай и деплой приложение. И не важно, что https терминируется за 3-4 узла до elixir-части, а тут он только для генерации ссылок написан. Эх, Крис, зачем так-то?
Примеров полно. Как хороших библиотек, так и не очень.
Отсюда вывод такой: если будете писать свой пакет в экосистеме elixir, то, пожалуйста, пишите его с учётом динамической конфигурации всего, что вообще конфигурируется. Это спасёт многих разработчиков от истерики и костылей.
Вместо заключения
Я думал, что описанное мной выше (про компилируемость языка) является очевидным. Но по какой-то причине бывшие рельсоделы не могут это понять. А жаль.