Proposal: Searching for implementations with keys#17
Proposal: Searching for implementations with keys#17NickNeck wants to merge 15 commits intoalexocode:mainfrom
Conversation
| def fetch!(%Options{implementation: {:config, otp_app, [key | keys]}}) do | ||
| otp_app | ||
| |> Application.fetch_env!(key) | ||
| |> get_in(keys) | ||
| end | ||
|
|
There was a problem hiding this comment.
Sadly, this doesn't really play nice with the default changes from #18.
It would also fail quietly when key exists but get_in returns nil for keys.
Instead I'd would expect to receive an error along the lines of:
Unable to fetch implementation for
MyFacadefrom config of app<otp_app>under path<keys>.
The message could be improved upon but I think you catch my drift?
| implementation: :module, | ||
| otp_app: :atom, | ||
| config_key: :atom, | ||
| config_key: :keys, |
There was a problem hiding this comment.
Instead of introducing :keys I'd argue it's a good opportunity to introduce an "or" mechanism:
config_key: [:atom, {:list_of, :atom}]Where valid_value?/2 would look like this:
defp valid_value?({:list_of, type}, value),
do: is_list(value) and Enum.all?(value, &valid_value?(type, &1))
defp valid_value?(list, value) when is_list(list),
do: Enum.any?(list, &valid_value?(&1, value))In addition the transform function could then be extended to map config_key to always be a list (by using List.wrap/1).
Which in turn would simplify the implementation in Implementation.fetch!/1 as there no need for multiple clauses anymore (one for single atom, one for list).
|
Hey @sascha-wolf what do you think is needed to merge this? I'm available to help if needed. Could really use this feature =) |
| def fetch!(%Options{implementation: {:config, otp_app, [key | keys]}, default: default}) do | ||
| with nil <- otp_app |> env!(key, default) |> get(keys) do | ||
| raise ArgumentError, | ||
| message: """ | ||
| could not fetch application environment #{inspect([key | keys])} \ | ||
| for application #{inspect(otp_app)}\ | ||
| """ |
There was a problem hiding this comment.
🎨 If it's okay I'd prefer to write it like this:
| def fetch!(%Options{implementation: {:config, otp_app, [key | keys]}, default: default}) do | |
| with nil <- otp_app |> env!(key, default) |> get(keys) do | |
| raise ArgumentError, | |
| message: """ | |
| could not fetch application environment #{inspect([key | keys])} \ | |
| for application #{inspect(otp_app)}\ | |
| """ | |
| def fetch!(%Options{implementation: {:config, otp_app, [key | keys]}, default: default}) do | |
| otp_app | |
| |> env!(key, default) | |
| |> get(keys) | |
| |> case do | |
| nil -> | |
| raise ArgumentError, | |
| "could not fetch application environment #{inspect([key | keys])} for application #{inspect(otp_app)}" | |
| implementation -> | |
| implementation | |
| end |
| |> validate!() | ||
| |> with_defaults() | ||
| |> transform(with_env: env) | ||
| |> with_defaults() | ||
|
|
There was a problem hiding this comment.
delegate_at_runtime? default, since the "environment" spec gets resolved to a boolean in transform/2.
| defp valid_value?(:keys, value), | ||
| do: is_atom(value) || (is_list(value) && Enum.all?(value, &is_atom/1)) | ||
|
|
There was a problem hiding this comment.
🧹 I think we can drop this now, right?
| defp valid_value?(:keys, value), | |
| do: is_atom(value) || (is_list(value) && Enum.all?(value, &is_atom/1)) |
Co-authored-by: Sascha Wolf <swolf.dev@gmail.com>
2a5cd73 to
402396f
Compare
|
Hi, Sascha I have made some more changes and incorporated your change requests. I have also updated the Because Let me know if the PR is too big and how we want handle the failing tests. Have a nice day. |
|
Hey @NickNeck, I've to say the PR currently intimidates me. I'm also not sure why you updated the If you think the project can derive value from the changes to |
| defmodule_salted WorkingBehaviour do | ||
| use Knigge, | ||
| otp_app: :knigge, | ||
| config_key: [:sys, :module] | ||
| end |
There was a problem hiding this comment.
Reading this test, I think it'd be beneficial for readability to deprecate config_key in favor of config_path. The Options module has a map_deprecated function (link) which we can use to map config_key to config_path while also printing a deprecation warning.
What do you think?
There was a problem hiding this comment.
Good idea. I will add this.
|
I just update the |
|
Hello @NickNeck and @sascha-wolf! Now I fetch an implementation module from nested configuration key and pass it to |
I'll try to find some time this week. Life has not been kind lately. |
Hi, Sascha
my suggestion would be to extend the
config_keyoption so that a list is also accepted.That would be usefull for
behaviours with additional configurations.Example:
What do you think?
Kind regards, Marcus
p.s. Nice work and I love the name of the lib.