Skip to content

Commit a1668a6

Browse files
author
José Valim
committed
Ensure Mix.Config is deep merged
Signed-off-by: José Valim <[email protected]>
1 parent a4cb74a commit a1668a6

File tree

3 files changed

+36
-33
lines changed

3 files changed

+36
-33
lines changed

lib/mix/lib/mix/config.ex

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ defmodule Mix.Config do
4040
@doc """
4141
Configures the given application.
4242
43+
Keyword lists are always deep merged.
44+
4345
## Examples
4446
4547
The given `opts` are merged into the existing configuration
@@ -69,6 +71,8 @@ defmodule Mix.Config do
6971
@doc """
7072
Configures the given key for the given application.
7173
74+
Keyword lists are always deep merged.
75+
7276
## Examples
7377
7478
The given `opts` are merged into the existing values for `key`
@@ -91,8 +95,7 @@ defmodule Mix.Config do
9195
quote do
9296
var!(config, Mix.Config) =
9397
Mix.Config.merge(var!(config, Mix.Config),
94-
[{unquote(app), [{unquote(key), unquote(opts)}]}],
95-
fn _app, _key, v1, v2 -> Keyword.merge(v1, v2) end)
98+
[{unquote(app), [{unquote(key), unquote(opts)}]}])
9699
end
97100
end
98101

@@ -116,7 +119,7 @@ defmodule Mix.Config do
116119
defmacro import_config(file) do
117120
quote do
118121
var!(config, Mix.Config) =
119-
Mix.Config.read_wildcard!(Path.expand(unquote(file), __DIR__), var!(config, Mix.Config))
122+
Mix.Config.read_wildcard!(var!(config, Mix.Config), Path.expand(unquote(file), __DIR__))
120123
end
121124
end
122125

@@ -142,7 +145,7 @@ defmodule Mix.Config do
142145
@doc """
143146
Reads many configuration files given by wildcard into a single config.
144147
"""
145-
def read_wildcard!(path, config) do
148+
def read_wildcard!(config, path) do
146149
paths = case Path.wildcard(path) do
147150
[] -> [path]
148151
o -> o
@@ -201,34 +204,15 @@ defmodule Mix.Config do
201204
"""
202205
def merge(config1, config2) do
203206
Keyword.merge(config1, config2, fn _, app1, app2 ->
204-
Keyword.merge(app1, app2)
207+
Keyword.merge(app1, app2, &deep_merge/3)
205208
end)
206209
end
207210

208-
@doc """
209-
Merges two configurations.
210-
211-
The configuration of each application is merged together
212-
and a callback is invoked in case of conflicts receiving
213-
the app, the conflicting key and both values. It must return
214-
a value that will be used as part of the conflict resolution.
215-
216-
## Examples
217-
218-
iex> Mix.Config.merge([app: [k: :v1]], [app: [k: :v2]],
219-
...> fn app, k, v1, v2 -> {app, k, v1, v2} end)
220-
[app: [k: {:app, :k, :v1, :v2}]]
221-
222-
"""
223-
def merge(config1, config2, callback) do
224-
Keyword.merge(config1, config2, fn app, app1, app2 ->
225-
Keyword.merge(app1, app2, fn k, v1, v2 ->
226-
if v1 == v2 do
227-
v1
228-
else
229-
callback.(app, k, v1, v2)
230-
end
231-
end)
232-
end)
211+
defp deep_merge(_key, value1, value2) do
212+
if Keyword.keyword?(value1) and Keyword.keyword?(value2) do
213+
Keyword.merge(value1, value2, &deep_merge/3)
214+
else
215+
value2
216+
end
233217
end
234218
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
use Mix.Config
2+
config :app, Repo, key: [nested: true]

lib/mix/test/mix/config_test.exs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ defmodule Mix.ConfigTest do
1313
assert var!(config, Mix.Config) == [lager: [key: :value]]
1414

1515
config :lager, other: :value
16-
assert var!(config, Mix.Config) == [lager: [other: :value, key: :value]]
16+
assert var!(config, Mix.Config) == [lager: [key: :value, other: :value]]
1717

1818
config :lager, key: :other
1919
assert var!(config, Mix.Config) == [lager: [key: :other, other: :value]]
@@ -26,10 +26,19 @@ defmodule Mix.ConfigTest do
2626
assert var!(config, Mix.Config) == [app: [{Repo, key: :value}]]
2727

2828
config :app, Repo, other: :value
29-
assert var!(config, Mix.Config) == [app: [{Repo, other: :value, key: :value}]]
29+
assert var!(config, Mix.Config) == [app: [{Repo, key: :value, other: :value}]]
3030

3131
config :app, Repo, key: :other
32-
assert var!(config, Mix.Config) == [app: [{Repo, [key: :other, other: :value]}]]
32+
assert var!(config, Mix.Config) == [app: [{Repo, key: :other, other: :value}]]
33+
34+
config :app, Repo, key: [nested: false]
35+
assert var!(config, Mix.Config) == [app: [{Repo, key: [nested: false], other: :value}]]
36+
37+
config :app, Repo, key: [nested: true]
38+
assert var!(config, Mix.Config) == [app: [{Repo, key: [nested: true], other: :value}]]
39+
40+
config :app, Repo, key: :other
41+
assert var!(config, Mix.Config) == [app: [{Repo, key: :other, other: :value}]]
3342
end
3443

3544
test "import_config/1" do
@@ -44,6 +53,14 @@ defmodule Mix.ConfigTest do
4453
assert var!(config, Mix.Config) == [my_app: [key: :value]]
4554
end
4655

56+
test "import_config/1 with nested" do
57+
use Mix.Config
58+
config :app, Repo, key: [nested: false, other: true]
59+
60+
import_config fixture_path("configs/nested.exs")
61+
assert var!(config, Mix.Config) == [app: [{Repo, key: [nested: true, other: true]}]]
62+
end
63+
4764
test "import_config/1 with bad path" do
4865
use Mix.Config
4966

0 commit comments

Comments
 (0)