指定模块名称后如何访问与Config相关的键

时间:2018-12-17 13:20:51

标签: elixir phoenix-framework

在phoenix中查看配置文件时,如果我创建这样的配置:

config :myapp,
  http: 4000

我可以这样在我的代码中引用该键:

Application.fetch_env!(:myapp, :http)

有时候配置似乎是特定于某个模块的,例如下面的MyApp.Endpoint。

这只是将配置分为几类吗?如果需要的话,如何在代码中引用下面的http端口?

config :myapp, MyApp.Endpoint,
  http: [port: 4000],
  debug_errors: true

2 个答案:

答案 0 :(得分:4)

  

有时候配置似乎是特定于某个模块的,如下所示   有MyApp.Endpoint。

     

这只是将配置分为几类吗?

tldr;

是的。使用配置文件,您可以创建如下内容:

[
  {: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)$/]
     ]
   ]},
  

我该如何   如果我也需要在我的代码中引用下面的http端口?

config :myapp, MyApp.Endpoint,
  http: [port: 4000],
  debug_errors: true

如果您查看该配置部分并删除换行符,则会得到:

config :myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true

而且,如果添加一些括号,则会得到:

config(:myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true)

这是一个函数调用,其中第一个参数是:myapp,第二个参数是MyApp.Endpoint,然后是这一部分:

http: [port: 4000], debug_errors: true

Keyword list。离题片刻...在长生不老药中,您可以定义如下函数:

  def go(x, y, z) do
    IO.puts x
    IO.puts y
    IO.inspect z
  end

并这样称呼他们:

My.go(10, 20, a: 1, b: 2)

它将输出:

10
20
[a: 1, b: 2]

这表明,如果函数调用中的最后一个参数具有特定的语法,则最后一个参数将全部聚集到一个Keyword list中,并作为一个参数传递给函数。因此,即使看起来您正在用四个参数调用My.go(),您实际上还是在用三个参数调用它。

因此,此函数调用:

config(:myapp, MyApp.Endpoint, http: [port: 4000], debug_errors: true)

正在调用config/3函数定义。

在配置文件中未定义config/3函数时,该怎么调用?这行:

use Mix.Config

config/3函数定义注入文件中。这是长生不老药的问题:发生了很多不可思议的事情,这使得很难弄清函数定义的来源。

这时,您知道在配置文件中正在调用config/3函数:

config :myapp, MyApp.Endpoint,
  http: [port: 4000],
  debug_errors: true

此外,配置文件顶部的行use Mix.Config暗示着config/3可能已在该模块中定义,因此您可以仔细阅读Mix.Config docs。果然,文档中描述了一个config/3函数。文档中还有一个config/2函数,在这里称为:

config :myapp,
  http: 4000

如果您阅读了config/2的文档,似乎很容易理解它是如何工作的:当您使用config/2设置键和值时,可以通过调用来检索与键关联的特定值:

Application.get_env(:myapp, key)  

例如,

Application.get_env(:myapp, :http)
#=> 4000

但是,当您使用config/3设置键和值时,无法检索与键相关联的特定值-而是最终检索了键/值对的整个列表:

keyword_list = Application.get_env(:myapp, module)

config/3创建一个嵌套结构,其中模块是键,其值是由您在配置文件中指定的键/值对组成的关键字列表,例如:

  http: [port: 4000],
  debug_errors: true

另一种解决问题的方法是考虑嵌套地图:

%{
  myapp: %{
    http: 4000,  #<== config/2
    my_app_endpoint: %{http: [port: 4000, debug_errors: true]}  #<== config/3
  }

...和Application.get_env()仅允许您指定两个键,例如:

Application.get_env(:myapp, :http)  #=> 4000

或:

Application.get_env(:myapp, :my_app_endpoint)  #=> %{http: [port: 4000, debug_errors: true]}

一旦检索到keyword_list:

keyword_list = Application.get_env(:myapp, module)

然后,您必须使用不老长寿药的一般知识来从关键字列表中检索与特定键相关联的值:

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

答案 1 :(得分:3)

在这种情况下,您从fetch_env!/2通话中获得的回报是keyword list。您可以按照使用地图的方式来访问它:

Application.fetch_env!(:myapp, :http)[:port]

或将其转换为地图,然后执行以下操作:

Application.fetch_env!(:myapp, :http) |> Map.new |> Map.get(:port)

编辑-基于@BrettBeatty的评论

您也可以使用Keyword.get/2函数:

:myapp |> Application.get_env(:http) |> Keyword.get(:port)