I’ve looked in Programming Elixir, and Elixir in Action as well as on this forum (even looking at proposals), in manuals and via search engines; I’ve not really found a sizable body of writing that is dedicated to “config”.
Is there any source that covers how to best format config files, what not to put in them, how to know what mix environments’ responsibilities are (regarding config), how and when to override default environment paths, where to place configuration files (apart from /config) as part of convention, and how import_config works with various args?
Apologies if I missed this material somewhere, but I have search up and down for a couple of days. What I have found is that understanding this aspect of app development in elixir can be critical later on with more advanced collaborative projects.
thing to know is that there are two important files:
config.exs which is compile-time configuration
runtime.exs which is run-time configuration
Often config.exs will be set up to punt to “.exs” but this must be explicit. Note that configuration is overridden “as written” so if something is set in test.exs it can be overridden by anything after your import_config call.
Also note that if you’re writing a library, all the stuff in your config.exs will NOT be called when your library is being compiled by your library’s user.
Ah right, part of it led me to think there was a bit of a philosophical element to it in terms of what does and doesn’t belong in config. In any event, I’m not aware of any such a resource beyond what you have likely already read in the docs.
Theoretically Home · Elixir School is where such a resource would be found if it existed. Maybe an effort could be organized to submit something. I certainly agree it is an important piece that deserves a comprehensive overview, especially with confusion around “config” vs “runtime”…
Hmm … config is just … config, so “not much” happens in them. It’s almost as same as json or yaml configuration files except that you can add some extra Elixir code to them. The biggest thing about config files is how they works across environments and that’s almost all. All the basics are covered in Config documentations.
Do you mean code formatting? It’s as same as in any other source code file, see the documentation for mix format task.
Almost everything - it’s easier to say what to put to them. Basically all you need is fetching the OS environment variables and using config_env() - both are well covered in Config documentation.
What “responsibilities” are you referencing to? Simply read the documentation what you are able to configure and use the ones you need. There are no “business logic” inside config or something like that. Simply configure your app and dependencies as you do in json or yaml files, but with a bit more of flexibility for example to fetch some data (like above in OS environment variables) and use them for configuration.
Best practice is rather to follow community standards as it’s easier for others to read your code. However nobody is limiting you what and how you do in your pet projects. If you want name them as you want. The default ones should be clear enough, so most people simply don’t touch configuration file names i.e. there is no need to do that.
Elixir does not have a framework which forces you to do something in specific direction, but gives you a good defaults. As above usually nobody is changing them. They are in config directory in both pet and huge business projects as keeping them in well known default location makes the most sense.
Simply you are passing a file name of file you want to import. There is no magic here except that you cannot import things in runtime.exs (also explained in Config documentation).
The materials are created when needed. If someone is asked to deploy a phoenix app to AWS then having production knowledge the tutorial is shared. Sometimes people have a problem finding something in documentation, but as above changing config naming is not common. Not much people were ever thinking about that and therefore there are not much resources about it. Same is for a database. By default in most projects PostgreSQL is more than enough. You are free to learn about other databases, but again if you don’t need it then you don’t have to learn about it.
Yes it is, but not so detailed. As long as you follow documentation and forum you should be fine. For most cases there are tools to help you like credo.
Thanks for your post and going over each of my points.
I did read the docs and they are quite short, it also implies a lot of understanding already on the part of the reader with respect to what things like config_env do, which is not the case with me.
I did not even know about config environments until I saw config_env and not much is said about what this means, I’ve sort of had to gather it from other peoples troubleshooting.
So import_config takes online string literals? I’ve seen some places where it seems like it accepts atoms…
Not sure about this, but maybe it’s just me. I’m really quickly learning new things. Simply when I saw in example some_function_call() then I look for it in documentation when needed. Personally I never had a problem understanding the config.
So you were looking at wrong documentation. Your mistake is that you were looking at details (here config) without thinking about general things (here application). Look at the top of mix.exs and it suddenly becomes clear (i.e. use Mix.Project).
The config.exs is compile-time and code compilation is part of building your application. The building tool is mix and that’s why there’s more documentation. For example you can see environments section of Mix documentation.
Link to all Elixir’s core applications are here:
Could you please give some link? You may saw some old code for other version of Elixir core. Unfortunately macros are more rarely documented. Because of the quoted expressions there are less pattern-matching simply because for example list literals and the ones created using sigil_w have a different AST (Abstract Syntax Tree) representation and simply is_list/1 check may fail for them and also for example variables.
The current documentation uses Path.expand/1 which only accepts Path.t which points to IO.chardata(), so currently atoms does not work.
iex> Path.expand(:example)
** (FunctionClauseError) no function clause matching in IO.chardata_to_string/1
The following arguments were given to IO.chardata_to_string/1:
# 1
:example
Attempted function clauses (showing 2 out of 2):
def chardata_to_string(string) when is_binary(string)
def chardata_to_string(list) when is_list(list)
(elixir 1.16.0) lib/io.ex:687: IO.chardata_to_string/1
(elixir 1.16.0) lib/path.ex:818: Path.expand_home/1
(elixir 1.16.0) lib/path.ex:180: Path.expand/1
iex:1: (file)
When interpolating (#{} syntax) the atoms are converted to String as same as integers for example. What I was talking about was atom as an argument, so which does not works even with interpolation i.e. :"a#{:b}c" (notice colon at the start) becomes :abc which is not supported, but "a#{:b}c" becomes "abc" which is fine. See Interpolation section of String module.
There are a bunch of mechanisms but the most core problem is configuration in deployment. However, I have found the best approach is to tackle that problem first then work backwards to development. Of course starting at the end isn’t possible when you’re just starting with all of this.
The most common deployment model is containers, but what I’m going to describe works well with any model of deployment and in development, the benefit there being a single mechanism can be used and tested through the entire life cycle.
There are two ways to inject deployment time data into a container, files and the environment. I’ve tried the first and I don’t recommend it. Getting a file injected in a container is a pain. I’ve been using the environment in combination with some assistance from a package (I wrote) Jetenv — Jetenv v0.1.1
which gives full access to the entire application environment and supports types and complex values.
With this approach you can configure any part of your application with predictable names and have full visibility into what’s being configured. It works well with secret managers since they work best with env vars. You can inject multi-line strings (think certificates) as well. And the same approach, though with different values typically, works in development. Source an env file and you’re ready to go. This is also nice because it provides a template for devops in building the deployment environment.