Reputation: 267220
When looking at the config files in phoenix, if I create a config like this:
config :myapp,
http: 4000
I can reference that key in my code like this:
Application.fetch_env!(:myapp, :http)
Sometimes the config seems to be specific to a module, like the below has MyApp.Endpoint.
Is this just breaking up the config into sections kind of? How do I reference the http port like below in my code if I need too?
config :myapp, MyApp.Endpoint,
http: [port: 4000],
debug_errors: true
Upvotes: 0
Views: 1119
Reputation: 48649
Sometimes the config seems to be specific to a module, like the below has MyApp.Endpoint.
Is this just breaking up the config into sections kind of?
tldr;
Yes. With your config file, you create something like this:
[
{:ecto_repos, [MyApp.Repo]},
{.Repo,
[
username: "postgres",
password: "postgres",
database: "myapp_dev",
hostname: "localhost",
pool_size: 10
]},
{:http, [port: 4000]}, <===**** HERE ****
{:included_applications, []},
{MyAppWeb.Endpoint, <====**** HERE ****
[
url: [host: "localhost"],
secret_key_base: "rchq5yMDPqqEBzFR+wIoqpc+kNquiyNDYUp/K8aF2Yj6POl/gjfj0H0rljE06LI5",
render_errors: [view: MyAppWeb.ErrorView, accepts: ["html", "json"]],
pubsub: [name: Rum.PubSub, adapter: Phoenix.PubSub.PG2],
http: [port: 4000],
debug_errors: true,
code_reloader: true,
check_origin: false,
watchers: [
node: [
"node_modules/webpack/bin/webpack.js",
"--mode",
"development",
"--watch-stdin",
{:cd, "/Users/7stud/phoenix_apps/book/myapp/assets"}
]
],
live_reload: [
patterns: [~r/priv\/static\/.*(js|css|png|jpeg|jpg|gif|svg)$/,
~r/priv\/gettext\/.*(po)$/, ~r/lib\/myapp_web\/views\/.*(ex)$/,
~r/lib\/myapp_web\/templates\/.*(eex)$/]
]
]},
How do I reference the http port like below in my code if I need too?
config :myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true
If you look at that config section and remove the newlines, you get:
config :myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true
And, if you add in some parentheses, you get:
config(:myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true)
That is a function call where the first argument is :myapp
, the second argument is MyApp.Endpoint
, and then this part:
http: [port: 4000], debug_errors: true
is a Keyword list
. Digressing for a moment...in elixir, you can define functions like this:
def go(x, y, z) do
IO.puts x
IO.puts y
IO.inspect z
end
and call them like this:
My.go(10, 20, a: 1, b: 2)
which will output:
10
20
[a: 1, b: 2]
That demonstrates that if the last arguments in a function call have a particular syntax, then the last arguments will all be gathered into something called a Keyword list
and passed to the function as one argument. So, even though it looks like you are calling My.go()
with four arguments, you are really calling it with three arguments.
As a result, this function call:
config(:myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true)
is calling a config/3
function definition.
How come you can call a config/3
function when no such function is defined in the config file? This line:
use Mix.Config
injects the config/3
function definition into the file. That is a problem with elixir: there is a lot of magic stuff that happens, which makes it hard to figure out where function definitions come from.
At this point, you know that a config/3
function is being called in the config file:
config :myapp, MyApp.Endpoint,
http: [port: 4000],
debug_errors: true
Additionally, the line use Mix.Config
at the top of the config file is a hint that config/3
might be defined in that module, so you can go peruse the Mix.Config docs. Sure enough, there is a config/3
function that is described in the docs. There's also a config/2
function described in the docs, which is called here:
config :myapp,
http: 4000
If you read the docs for config/2
it seems pretty easy to understand how things work: when you set keys and values with config/2
, you can retrieve a particular value associated with a key by calling:
Application.get_env(:myapp, key)
For instance,
Application.get_env(:myapp, :http)
#=> 4000
However, when you set keys and values with config/3
, you cannot retrieve a particular value associated with a key--instead you end up retrieving a whole list of key/value pairs:
keyword_list = Application.get_env(:myapp, module)
config/3
creates a nested structure where the module is the key and its value is a Keyword list made up of the key/value pairs you specify in the config file, e.g.:
http: [port: 4000],
debug_errors: true
Another way of getting your head around that is by thinking of a nested map:
%{
myapp: %{
http: 4000, #<== config/2
my_app_endpoint: %{http: [port: 4000, debug_errors: true]} #<== config/3
}
...and Application.get_env()
only allows you to specify two keys, such as:
Application.get_env(:myapp, :http) #=> 4000
or:
Application.get_env(:myapp, :my_app_endpoint) #=> %{http: [port: 4000, debug_errors: true]}
Once you retrieve the keyword_list:
keyword_list = Application.get_env(:myapp, module)
then you have to use your general knowledge of elixir to retrieve a value associated with a particular key from the keyword list:
all = Application.get_all_env(:myapp)
IO.inspect all #=> lots of stuff (which is not a keyword list)
keyword_list = Application.get_env(:myapp, MyApp.Endpoint)
IO.inspect keyword_list #=> a keyword list with lots of key/value pairs
http = Keyword.get(keyword_list, :http)
IO.inspect http #=> [port: 4000]
port = Keyword.get(http_keyword_list, :port)
IO.inspect port #=>4000
Upvotes: 4
Reputation: 2345
What you get back in this case from your fetch_env!/2
call is a keyword list. You can either access it the way you'd do with a Map:
Application.fetch_env!(:myapp, :http)[:port]
OR convert it to a Map, and do it that way:
Application.fetch_env!(:myapp, :http) |> Map.new |> Map.get(:port)
EDIT - based on @BrettBeatty's comment
You can use the Keyword.get/2
function as well:
:myapp |> Application.get_env(:http) |> Keyword.get(:port)
Upvotes: 6