-
Notifications
You must be signed in to change notification settings - Fork 37
Description
Hey, so I love Vapor's new API, and I'm super happy you've taken the time to
put all of this together and continue to refine it - thanks for that!
I'm like 90% sure that what I'm proposing is a bad idea, but I was working
yesterday with someone who was new-ish to Elixir (and totally new to
Vapor), and they said this:
I think this particular example is interesting because it's configuration inside a function, not a process and so Vapor.load!/1 wouldn't be called once at boot, but every time we call the function (perhaps a concern for hot code paths). Meaning if the application was configured incorrectly, we wouldn't find out until we called the function.
The code they were talking about was something like this:
def sign(message) do
key = Application.get_env(:my_app, :private_key)
do_sign(message, key)
endAnd they were under the impression that to use Vapor they'd need to do
something like:
def sign(message) do
%{key: key} = Vapor.load!([%Env{bindings: [key: "PRIVATE_KEY"]}])
do_sign(message, key)
endPersonally I love having the API of Vapor be just a function call, but
I think this is leaving some folks with confusion about when and how to use
it. I might be missing something here, but I see this as basically only being
used in an application's start/2 callback, and so maybe the design of the API
could make that more explicit by hiding the underlying function that it calls?
I'm thinking about something like this:
defmodule VaporExample.Application do
use Vapor.Application,
env: %{bindings: [db_url: "DB_URL", db_name: "DB_NAME"]},
file: %{path: "config.toml", bindings: [kafka_brokers: "kafka.brokers"]},
file: %{path: "config.json", bindings: [port: "port"]}
def start(_type, _args, config) do
children = [
{VaporExampleWeb.Endpoint, port: config.port}
{VaporExample.Repo, [db_url: config.db_url, db_name: config.db_name]},
{VaporExample.Kafka, brokers: config.kafka_brokers},
]
opts = [strategy: :one_for_one, name: VaporExample.Supervisor]
Supervisor.start_link(children, opts)
end
endWhich would translate to something like:
defmodule Vapor.Application do
defmacro __using__(providers) do
quote do
use Application
def start(type, args) do
config = Vapor.load!(unquote(providers))
start(type, args, config)
end
end
end
endThis is just a quick and silly idea, and maybe some additional documentation could
help here (or something like a set of "here's how you do these common things with
Vapor" examples, or a wiki or something), but this was just on idea that jumped to
mind, and I thought that seeing this feedback from someone a bit less experienced
with Elixir might be helpful in making an API that even those who don't yet fully
understand the Elixir boot and config process can use successfully.