简而言之,我有一个脚本,它读取.yaml文件以在运行时获取一些配置信息,例如要联系的URL,要使用的共享密钥,是否使用调试模式等。
使用该配置的模块具有启动功能,该功能稍后调用循环并且还调用写入诊断的logdebug函数,但仅在设置了调试模式时。令我恼火的是,每当我打电话给我时,我必须将配置传递给每个功能。如果我可以调用start函数并设置一些可用于模块中所有其他函数的变量,那将会容易得多。可以这样做吗?我似乎无法找到有关如何做到的事情。
是否有一种设置运行时配置的首选方式,就像我在这里做的一样?也许我过度复杂了?
编辑:更多细节,我将此分发为使用Escript.Build
创建的可执行文件,并且我不想让最终用户编辑文件然后重建文件。这就是为什么我希望最终用户(可能不是超级技术人员)能够编辑.yaml文件。
答案 0 :(得分:5)
免责声明:我将'运行时配置'更多地解释为参数;如果不是这样的话,这个答案可能不是很有用。
Module
类似于Class
。
不幸的是,这种常见的O-O工作方式不够相似; Elixir / Erlang模块没有“生命”,只是扁平的逻辑。你有效地尝试做的是在模块本身存储状态;在函数式语言中,状态必须保存在变量中,因为模块在所有进程的所有调用者之间共享 - 另一个进程可能需要存储不同的状态!
但是,这是一个常见的编程问题,在Elixir中有一种惯用的解决方法:GenServer
。
如果您不熟悉OTP,那么您应该自己学习它:它会改变您对编程的看法,它会帮助您更好地编写(读取:更可靠)软件,它会让你开心。真。
我会将配置存储在GenServer的状态中;如果你创建一个内部结构来表示它,你可以轻松传递它并设置默认值;我们想要的所有东西都在令人愉悦的API中。
示例实施:
defmodule WebRequestor do
use GenServer
### API ###
# these functions execute in the CALLER's process
def start_link() do
GenServer.start_link(__MODULE__, [], [name: __MODULE__])
end
def start do
# could use .call if you need synch, but then you'd want to look at
# delayed reply genserver calls, which is an advanced usage
GenServer.cast(__MODULE__, :start)
end
#could add other methods for enabling debug, setting secrets, etc.
def update_url(new_url) do
GenServer.call(__MODULE__, {:update_url, new_url})
end
defmodule State do
@doc false
defstruct [
url: "http://api.my.default",
secret: "mybadpassword",
debug: false,
status: :ready, # or whatever else you might need
]
end
### GenServer Callbacks ###
# These functions execute in the SERVER's process
def init([]) do
config = read_my_config_file
{:ok, config}
end
def handle_cast(:start, %{status: :ready} = state) do
if config.debug, do: IO.puts "Starting"
make_request(state.url)
{:noreply, %{state|status :running}}
end
def handle_cast(:state, state) do
#already running, so don't start again.
{:noreply, state}
end
def handle_call({:update_url, new_url}, _from, state) do
{:reply, :ok, %{state|url: new_url}}
end
### Internal Calls ###
defp make_request(config) do
# whatever you do here...
end
defp read_my_config_file do
# config reading...
%State{}
end
end
答案 1 :(得分:3)
答案取决于你的约束。你需要使用.yaml吗?我们不鼓励使用YAML,除非你真的让非Elixir程序员需要触摸它们。如果他们都被程序员触及,那么你可以使用Elixir配置:
# config/config.exs
config :my_app,
url: "...",
this: "...",
that: "..."
这将允许您使用Application.get_env(:my_app, :url)
和Application.put_env(:my_app, :foo, :bar)
等功能访问和更改配置。将来,如果您想构建版本(将整个应用程序与VM一起发布到一个目录中),提供升级等,使用Elixir配置将证明是最佳工作流程。
答案 2 :(得分:1)
您可以使用exs文件吗?如有必要,可能会在那里加载yaml文件?这篇博文似乎非常相似:http://www.schmitty.me/taking-advantage-of-mix-config/
答案 3 :(得分:0)
我最后在我的主模块中使用yamerl
来读取.yaml文件,然后我使用Application.put_env/2
将值放入可用于我所有模块的位置。
[ config | _ ] = :yamerl_constr.file("config.yaml")
Application.put_env(:osq_simulator, :base_url, :proplists.get_value('base_url', config))
虽然基于我得到的其他一些反馈,但看起来Chris Meyer的回答可能就是"对"做我想做的事情。