vrod

vrod

Forgive me for maybe a noob question. I am reading much about configuration – maybe this in Elixir is one of the most confusing things? I read this Configuring your Elixir Application at Runtime with Vapor | AppSignal Blog and it says “if it can be set at runtime, it should be set at runtime.” This makes much sense to me – this is how other languages work with .env files – but maybe the comparison is not fair with dynamic languages?

My question is what things can not be runtime config? I imagine a very simple setup from working with PHP where there was only 1 config and 2 .env files (regular .env and a .test.env) and the test suite would know to use the .test.env. But dynamic languages do not have the same concerns. I am thinking maybe a config for a module name like saying which HTTP client the app should use. A system ENV maybe has string for “HTTPoison”, but you must convert this to atom like Elixir.HTTPoison. It is ugly, but I think this works fine. What things will not work this way and must be set in a config file for compile time config?

Thank you for your replies!

First 10 of 15 Posts Switch mode

lud

lud

The first thing I can think off are modules names that macros will be used from, or will be modules generated by macros.

Basically, anything that changes the compilation result.

vrod

vrod OP

How to know what modules have macros?

lud

lud

Well if we are talking about your application then you already know it. And if we are talking about a library then it is either documented in the configuration section of the documentation, if it exists, or you have to dive in the code:

Module that define macros call defmacro. Module that use macros call use SomeModule or require SomeModule.

I am not sure if import SomeModule also “requires” the module to be able to call its macros.

Unfortunately this only gets you so far…

That is why, when they say " if it can be set at runtime, it should be set at runtime", then I would say “yes, if it is environment dependent (a hostname, a port, a database connection string, anything that you would put in your .env files)”. Otherwise, if it is something that doesn’t change, the compile-time config is fine.

vrod

vrod OP

I am wondering how to move most configs into runtime.exs. This works simple for local running or on production server when real values are required. runtime.exs looks something like

import Config

config :myapp,
  x: System.get_env("X"),
  y: System.get_env("Y"),
  z: System.get_env("Z"),
  # ... etc...

But how to override this for testing?

RudManusachi

RudManusachi

You can use Config.config_env/0 to differentiate between envs

import Config
... 
if config_env() == :test do
  config :myapp, foo: "bar"
end
lud

lud

You can use Application.put_env in your tests directly.

vrod

vrod OP

Forgive me for my struggle to understand a good solution. I think my options are to do maybe these:

Option 1: fill runtime.exs:

if config_env() == :test do
  config :myapp, 
      x: "test-x",
      y: "test-y",
      z: System.get_env("TEST_Z", "test-z"),
     # ... etc...
else
  config :myapp, 
      x: System.get_env("X"),
      y: System.get_env("Y"),
      z: System.get_env("Z"),
     # ... etc...
end

Pro: declarative, clear
Con: double long runtime.exs. Also if-statements in configs are always for me a smell.

Option 2: use test_helper.exs

Use simple runtime.exs and add test values to test_helper.exs like maybe this:

  config :myapp, 
      x: System.get_env("X"),
      y: System.get_env("Y"),
      z: System.get_env("Z"),
     # ... etc...

and then in test_helper.exs I can done Application.put_env like

Application.put_env(:myapp, :x, "test-x")
Application.put_env(:myapp, :y, "test-y")
Application.put_env(:myapp, :z, "test-z")

Pro: simple runtime.exs (no double, no if-statement)
Con: cannot enforce System ENV vars using System.fetch_env! – only get_env would work so that execution does not stop before test_helper.exs runs and a value is supplied.

Option 3: Vapor

I looked also at Vapor. I like this: it could replace runtime.exs 100% I think. But it is still a problem when you must manually take a value and use Application.put_env to put it into the correct place so your app and deps can find it. For example, imagine a dependency with config like this:

config :some_dep, 
   deep: %{
      thing1: "a",
      thing2: System.get_env("THING2")
   }

With vapor, I can get THING2 ENV var from .env but I must be careful with any deep config like this example dependancy. Where to put it once I have it? I think I must merge map and then put inside carefully into Application process dictionary.

Pro: support for .env and .env.{ENV} files for each override. Errors when config is not correct.
Con: difficult for complex config that rely on Application.get_env.

I think there must be other ways. Sorry for my confusion. I admit I thought this was a easy topic but I am understanding more that config has subtle!

RudManusachi

RudManusachi

What about having in config/test.exs

  config :myapp, 
      x: "test-x",
      y: "test-y",
      z: System.get_env("TEST_Z", "test-z"),

and in config/runtime.exs

if config_env() == :prod do
  config :myapp, 
      x: System.get_env("X"),
      y: System.get_env("Y"),
      z: System.get_env("Z"),
     # ... etc...
end

[/quote]

if-statements in configs are always for me a smell.

For me if-statement is smell almost everywhere but configs :grinning_face_with_smiling_eyes:

Take a look at the example how configs are organized in hexpm/config at main · hexpm/hexpm · GitHub

Nicd

Nicd

This suffers from being long and containing ifs, but I’m posting it anyway for reference. I went 100% on runtime.exs in my project and only have a couple of things that need to be set at compile time in the config.exs.

This has worked well for me. Even though the runtime config file is big, it’s not really that big once you get to know it, and I can have unified config for both development and release time (just using .env in development for convenience).

vrod

vrod OP

This is a beautiful little package! Thank you for sharing your approach – it works well!

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement