Writing and publishing a package: How to deal with configurations?

Hi all!

I’m writing my own package, MyPackage, as a test exercise. While I wrote it, I used some options in its config/config.exs file.

Now that I’m using the package (after being published) in another Elixir app, I’m not able to supply the same keys in the new app’s config/config.exs. Some code for context

#app_using_my_package/config/config.exs
config :my_package, index: "some value"

The MyPackage code needs this config value in order to work correctly, but all it gets is nil.

#app_using_my_package/deps/my_package/lib/code.ex
defmodule MyPackage.Code do
  def some_function do
    Application.get_env(:my_package, :index) # this is nil instead of "some value"
  end

end

I know I wrote something wrong in my_package but I haven’t found a way to solve this.

Thanks for any insight!

UPDATE Here is the code: https://github.com/sebastialonso/graveyard

Can you provide a link to your package? What you’ve presented so far is the normal way to go about stuff and ought to work fine.

Sure thing. Here it is.

I can’t find a lib/code.ex there, can you point to the place where you are trying to use the config?

You should be able to use config the same way. Your just not going to be putting it in the libraries config(they are not included with deps.get). So the configs you would normally put there will now be put in a higher level config that is using the package. So example if your using phoenix you would put your graveyard configs inside the phoenix config files.

The code.ex file was just an example. The file your looking for is in lib/graveyard/support.ex. Try Graveyard.Support.index() in iex.

Okay, GitHub search found this

Those module attributes are only populated during compilation of the dependency.

If you change the setting you need to recompile the dependency. Or replace the attributes by direct calls.

Ps: the code in the repo is fundamentally different from the example you have shown. The example from above would actually work.

1 Like

So should I skip the module attribute approach, and just call the Application.get_env inside the functions?

Yes that would be one way of resolving your issue.

From what I know when resolving this same issue you should never set a module attribute to something from your config. If someone knows more they may correct me but as far as I know you are not suppose to do that because its compile time set.

1 Like

You are correct. I was way off thinking it would not mind. Thanks!

Sometimes I forget Elixir is compiled. I’m still thinking in Ruby…

1 Like

Yeah, it is fairly easy to shoot yourself in the foot with configuration. In general these are the rules that I try to follow:

  • Reduce configuration as much as possible (i.e. prefer passing in arguments to creating configuration for a library)
  • Prefer run-time configuration over compile-time
  • Only use compile-time configuration when it is needed for performance (or some other strong reason) and document it throughly

Yes that would be my recommendation (when you can’t or it isn’t reasonable to move to passing arguments directly)

So this code:

@elastic_index Application.get_env(:graveyard, :index)
@elastic_type Application.get_env(:graveyard, :type)
@mappings Application.get_env(:graveyard, :mappings)

Could become:

def elastic_index, do: Application.get_env(:graveyard, :index)
def elastic_type, do: Application.get_env(:graveyard, :type)
def mappings, do: Application.get_env(:graveyard, :mappings)

Or your could just embed the Application.get_env/2 call inside the function where it is used. Also, I’ve switched to using Application.fetch_env!/2 in most places in my code because I always expect the System Environment variables to be set (the code in question runs on heroku so environment variables are used for lots of configuration).

Some good reading:

5 Likes