elixir-toniq / vapor

Runtime configuration system for Elixir
MIT License
596 stars 37 forks source link

Convert function based api to data driven api #51

Closed keathley closed 4 years ago

keathley commented 4 years ago

This PR has been a long time coming. It brings with it a substantial API change but I think it's for the better. I'll try to explain the major changes

No more Plan module

I've removed the entire Plan module in favor of just using structs and lists to define the order the providers should be loaded in:

# Before
Plan.default()
|> Plan.merge(Dotenv.default())
|> Plan.merge(Env.with_bindings([])

# After
plan = [
  %Dotenv{},
  %Env{bindings: []},
]

This approach allows us to remove a lot of functions and complexity while also making it more obvious to the user what it is we're doing.

No more type conversions

I've removed the type conversions from get. Now get will fetch whatever has been stored in ets. I've always disliked doing type conversions at the call-site and felt that they were much more error-prone. Instead of using explicit type casts I've extended the vapor store to accept a map of "translation" functions. These functions are user defined and can perform any manipulations the user needs:

# Before
Config.get("key", as: :string)

# After
plan = [
  %Env{bindings: [port: "PORT"]}
]

translations = [
  port: fn str -> String.to_integer(str),
]

Config.start_link(%{plan: plan, translations: translations})

No more weird path thing to get nested data

Everything we're going to store is elixir data structures so we don't need to use string keys.

Finally implement init/1

I mean...its about time.

What's done so far

So far I've only implemented environment variable and dotenv support and the init callbacks. I still believe supporting watches is an important piece of functionality but that can be done on a per provider basis. There's also several doc changes that will need to be done. If anyone has any feedback please let me know.