A couple days ago I saw a post from José Valim Mocks and explicit contracts and decided to implement those patterns into my own project wikix.
Those are my config/*.exs files. As you can see (in test.exs and prod.exs) I defined a couple of env vars "http_client" and "user_agent". This way, when I test the module I use a mock, and when the module goes to production I use a real http client.
The problem is: when I try to use wikix as a dependency in my phoenix app, for some reason mix is ignoring my wikix/config/prod.exs file so the http client (in wikix) is never defined.
In my phoenix app instead of have Wikix.HTTPClient I got nil.HTTPClient.
Here is my phoenix/mix.exs file
defmodule GraapyApi.Mixfile do
use Mix.Project
def project do
[app: :graapy_api,
version: "0.0.1",
elixir: "~> 1.0",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [:phoenix, :gettext] ++ Mix.compilers,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
aliases: aliases, deps: deps
]
end
# Configuration for the OTP application.
#
# Type `mix help compile.app` for more information.
def application do
[mod: {GraapyApi, []},
applications: [:phoenix, :cowboy, :logger, :gettext,
:phoenix_ecto, :mongodb_ecto, :wikix]]
end
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
defp elixirc_paths(_), do: ["lib", "web"]
# Specifies your project dependencies.
#
# Type `mix help deps` for examples and options.
defp deps do
[
{:phoenix, "~> 1.1.4"},
{:mongodb_ecto, ">= 0.0.0"},
{:phoenix_ecto, "~> 2.0"},
{:gettext, "~> 0.9"},
{:cowboy, "~> 1.0"},
{:dogma, "~> 0.1", only: :dev},
{:credo, "~> 0.3", only: [:dev, :test]},
{:wikix, github: "graapy/wikix"}
]
end
# Aliases are shortcut or tasks specific to the current project.
# For example, to create, migrate and run the seeds file at once:
#
# $ mix ecto.setup
#
# See the documentation for `Mix` for more info on aliases.
defp aliases do
["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"]
This is expected behavior. Parent project (your phoenix app) is not using configuration from deps. If dependencies require configuration you need to specify it in the parent app, so you would need to copy:
config :wikix, http_client: Wikix.HTTPClient
config :wikix, user_agent: [{"User-agent", "tugorez tugorez@gmail.com"}]
to your Phoenix app.
It is often a good idea to provide a default, so for example in Wikix
app you can have something like this:
defmodule Wikix.SomeModule do
@httpclient Wikix.HTTPClient
call(first_arg, second_arg, httpclient \\ @httclient) do
...
You can now read the configuration and pass the client as last argument just as in the article about mocks.
But also you can make sure that your Phoenix app doesn't have to configure anything and it will use the default client when calling the function without specifying last argument.
Your Phoenix app probably shouldn't even know about Wikix
"in memory client", so it makes sense to make such defaults.