Compare commits

...
12 Commits
Author SHA1 Message Date
José Valim a3d88dc406 Release v1.2.1 2016-01-14 19:25:11 +01:00
Daniel Azuma 58a6291f17 Fix a crash in Macro.to_string if a tree looks like a sigil but the function is not an atom
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-13 11:19:58 +01:00
Aleksei Magusev 81884247d7 Warn when defimp is called for consolidated protocol
Conflicts:
	lib/elixir/lib/protocol.ex
2016-01-12 10:50:29 +01:00
Adrien Moreau c07a34f9a8 Correct the type definition of ExUnit.state for the failed state
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-12 09:20:45 +01:00
José Valim e05bfc78cb Improve docs and error handling for Access
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-10 22:52:08 +01:00
Aleksei Magusev cb430702c9 Preserve variable metadata during collection from assertion pattern
Closes #4174.
2016-01-09 20:47:46 +01:00
Aleksei Magusev 6aaa4bb06d Do not warn in match assertion if variable is reused in pattern
Having the folowing assertion `assert {var, var} = {1, 1}`,
the code produced previously:

  [var] = case(right) do
    {var, var} ->
      #...
      [var]
    _ ->
      #...
  end

And with the patch:

  [var, var] = case(right) do
    {var, var} ->
      #...
      [var, var]
    _ ->
      #...
  end
2016-01-06 23:56:44 +01:00
Thomas Fisher a6b80a19c5 Fixes bug in IEx.Config.configuration
IEx.Config.configuration was raising a FunctionClauseError since
it did not have a default value for width

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-06 18:58:42 +01:00
Aleksei Magusev 8be0ad0499 Correct Macro.to_string/1 formatting for capture operator
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-06 18:58:07 +01:00
José Valim 8da4936ac3 Ensure dependencies are properly skipped when running in another environment
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-06 18:57:54 +01:00
James Fish bcc92ccc40 Support remote pids/ports with IEx helper i/1
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-01-02 18:54:07 +01:00
José Valim d64d4b009c v1.2 branch 2016-01-01 11:51:36 +01:00
23 changed files with 264 additions and 96 deletions
+14
View File
@@ -85,6 +85,20 @@ ask Mix to use Rebar 3 to compile it by passing the `manager: :rebar3` option.
Once configured, Mix will prompt you to install Rebar 3 if it is not yet
available.
## v1.2.1 (2016-01-14)
### 1. Enhancements
* [IEx] Support remote pids/ports with IEx helper `i/1`
* [Protocol] Warn when `defimpl` is called for a consolidated protocol
### 2. Bug fixes
* [ExUnit] Ensure `assert` macros can be used from quoted code
* [ExUnit] Do not warn in match assertion if variable is reused in pattern
* [Macro] Fix a bug in `Macro.to_string/1` where a remote function could be accidentally interpreted as a sigil
* [Mix] Ensure dependencies are properly skipped when `--only` option is given to `mix deps.get`
## v1.2.0 (2016-01-01)
### 1. Enhancements
+1 -1
View File
@@ -1,6 +1,6 @@
REBAR ?= "$(CURDIR)/rebar"
PREFIX ?= /usr/local
DOCS := master
DOCS := v1.2
ELIXIRC := bin/elixirc --verbose --ignore-module-conflict
ERLC := erlc -I lib/elixir/include
ERL := erl -I lib/elixir/include -noshell -pa lib/elixir/ebin
+1 -3
View File
@@ -30,12 +30,10 @@ This document simply outlines the release process:
12. Create a new branch "vMAJOR.MINOR"
13. Move docs generation to `docs/vMAJOR.MINOR` in Makefile and copy them from `docs/stable`
13. Move docs generation to `docs/vMAJOR.MINOR` in Makefile and copy them from `docs/stable` (change index.html accordingly)
14. In master, bump versions, start new CHANGELOG, add `-dev` back and commit "Start vVERSION+1"
15. `make release_docs` and push it to `elixir-lang/docs`
## Places where version is mentioned
* VERSION (make sure there is no newline in this file)
+1 -1
View File
@@ -1 +1 @@
1.2.0
1.2.1
+83 -8
View File
@@ -1,15 +1,16 @@
defmodule Access do
@moduledoc """
Dictionary-like access to data structures via the `foo[bar]` syntax.
Key-based access to data structures via the `foo[bar]` syntax.
This module also empowers `Kernel`s nested update functions
`Kernel.get_in/2`, `Kernel.put_in/3`, `Kernel.update_in/3` and
`Kernel.get_and_update_in/3`.
Elixir provides two syntaxes for accessing values. `user[:name]`
is used by dynamic structures, like maps and keywords, while
`user.name` is used by structs. The main difference is that
`user[:name]` won't raise if the key `:name` is missing but
`user.name` will raise if there is no `:name` key.
## Examples
## Key-based lookups
Out of the box, Access works with built-in dictionaries: `Keyword`
and `Map`:
Out of the box, Access works with `Keyword` and `Map`:
iex> keywords = [a: 1, b: 2]
iex> keywords[:a]
@@ -23,13 +24,67 @@ defmodule Access do
iex> star_ratings[1.5]
"★☆"
Access can be combined with `Kernel.put_in/3` to put a value
in a given key:
iex> map = %{a: 1, b: 2}
iex> put_in map[:a], 3
%{a: 3, b: 2}
This syntax is very convenient as it can be nested arbitrarily:
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in users["john"][:age], 28
%{"john" => %{age: 28}, "meg" => %{age: 23}}
Furthermore, Access transparently ignores `nil` values:
iex> keywords = [a: 1, b: 2]
iex> keywords[:c][:unknown]
nil
The key comparison must be implemented using the `===` operator.
Since Access is a behaviour, it can be implemented to key-value
data structures. Access requires the key comparison to be
implemented using the `===` operator.
## Field-based lookups
The Access syntax (`foo[bar]`) cannot be used to access fields in
structs. That's by design, as Access is meant to be used for
dynamic key-value structures, like maps and keywords, and not
by static ones like structs.
However Elixir already provides a field-based lookup for structs.
Imagine a struct named `User` with name and age fields. The
following would raise:
user = %User{name: "john"}
user[:name]
** (UndefinedFunctionError) undefined function User.fetch/2
(User does not implement the Access behaviour)
Structs instead use the `user.name` syntax:
user.name
#=> "john"
The same `user.name` syntax can also be used by `Kernel.put_in/2`
to for updating structs fields:
put_in user.name, "mary"
%User{name: "mary"}
Differently from `user[:name]`, `user.name` cannot be extended by
the developers, and will be always restricted to only maps and
structs.
Summing up:
* `user[:name]` is used by dynamic structures, is extensible and
does not raise on missing keys
* `user.name` is used by static structures, it is not extensible
and it will raise on missing keys
"""
@type t :: list | map | nil
@@ -39,6 +94,20 @@ defmodule Access do
@callback fetch(t, key) :: {:ok, value} | :error
@callback get_and_update(t, key, (value -> {value, value})) :: {value, t}
defmacrop raise_undefined_behaviour(e, struct, top) do
quote do
stacktrace = System.stacktrace
e =
case stacktrace do
[unquote(top)|_] ->
%{unquote(e) | reason: "#{inspect unquote(struct)} does not implement the Access behaviour"}
_ ->
unquote(e)
end
reraise e, stacktrace
end
end
@doc """
Fetches the container's value for the given key.
"""
@@ -47,6 +116,9 @@ defmodule Access do
def fetch(%{__struct__: struct} = container, key) do
struct.fetch(container, key)
rescue
e in UndefinedFunctionError ->
raise_undefined_behaviour e, struct, {^struct, :fetch, [^container, ^key], _}
end
def fetch(%{} = map, key) do
@@ -96,6 +168,9 @@ defmodule Access do
def get_and_update(%{__struct__: struct} = container, key, fun) do
struct.get_and_update(container, key, fun)
rescue
e in UndefinedFunctionError ->
raise_undefined_behaviour e, struct, {^struct, :get_and_update, [^container, ^key, ^fun], _}
end
def get_and_update(%{} = map, key, fun) do
+4
View File
@@ -640,6 +640,10 @@ defmodule UndefinedFunctionError do
"undefined function " <> Exception.format_mfa(module, function, arity) <>
" (function #{fa} is not available)"
end
def message(%{reason: reason, module: module, function: function, arity: arity}) do
"undefined function " <> Exception.format_mfa(module, function, arity) <> " (#{reason})"
end
end
defmodule FunctionClauseError do
+5 -1
View File
@@ -571,6 +571,10 @@ defmodule Macro do
fun.(ast, "&" <> to_string(mod, fun) <> "." <> Atom.to_string(name) <> "/" <> to_string(arity, fun))
end
def to_string({:&, _, [arg]} = ast, fun) when not is_integer(arg) do
fun.(ast, "&(" <> to_string(arg, fun) <> ")")
end
# Unary ops
def to_string({unary, _, [{binary, _, [_, _]} = arg]} = ast, fun)
when unary in unquote(@unary_ops) and binary in unquote(@binary_ops) do
@@ -691,7 +695,7 @@ defmodule Macro do
defp module_to_string(atom, _fun) when is_atom(atom), do: inspect(atom, [])
defp module_to_string(other, fun), do: call_to_string(other, fun)
defp sigil_call({func, _, [{:<<>>, _, _} = bin, args]} = ast, fun) when is_list(args) do
defp sigil_call({func, _, [{:<<>>, _, _} = bin, args]} = ast, fun) when is_atom(func) and is_list(args) do
sigil =
case Atom.to_string(func) do
<<"sigil_", name>> ->
+13
View File
@@ -540,6 +540,7 @@ defmodule Protocol do
"the dict[key] syntax, please implement the Dict behaviour instead"
else
Protocol.assert_protocol!(protocol)
Protocol.__ensure_defimpl__(protocol, for, __ENV__)
end
defmodule name do
@@ -580,6 +581,7 @@ defmodule Protocol do
defp derive(protocol, for, struct, opts, env) do
extra = ", cannot derive #{inspect protocol} for #{inspect for}"
assert_protocol!(protocol, extra)
__ensure_defimpl__(protocol, for, env)
assert_impl!(protocol, Any, extra)
# Clean up variables from eval context
@@ -608,6 +610,17 @@ defmodule Protocol do
end)
end
@doc false
def __ensure_defimpl__(protocol, for, env) do
if Protocol.consolidated?(protocol) do
message =
"the #{inspect protocol} protocol has already been consolidated" <>
", an implementation for #{inspect for} has no effect"
:elixir_errors.warn(env.line, env.file, message)
end
:ok
end
@doc false
def __spec__?(module, name, arity) do
signature = {name, arity}
+16
View File
@@ -59,4 +59,20 @@ defmodule AccessTest do
assert Access.get_and_update(%{}, :foo, fn nil -> {:ok, :baz} end) == {:ok, %{foo: :baz}}
assert Access.get_and_update(%{foo: :bar}, :foo, fn :bar -> {:ok, :baz} end) == {:ok, %{foo: :baz}}
end
test "for struct" do
defmodule Sample do
defstruct [:name]
end
assert_raise UndefinedFunctionError,
"undefined function AccessTest.Sample.fetch/2 (AccessTest.Sample does not implement the Access behaviour)", fn ->
Access.fetch(struct(Sample, []), :name)
end
assert_raise UndefinedFunctionError,
"undefined function AccessTest.Sample.get_and_update/3 (AccessTest.Sample does not implement the Access behaviour)", fn ->
Access.get_and_update(struct(Sample, []), :name, fn nil -> {:ok, :baz} end)
end
end
end
+3 -1
View File
@@ -290,6 +290,8 @@ defmodule MacroTest do
assert Macro.to_string(quote do: ~R"123") == ~s/~R"123"/
assert Macro.to_string(quote do: ~R"123"u) == ~s/~R"123"u/
assert Macro.to_string(quote do: ~R"\n123") == ~s/~R"\\\\n123"/
assert Macro.to_string(quote do: Foo.bar(<<>>, [])) == "Foo.bar(<<>>, [])"
end
test "arrow to string" do
@@ -387,6 +389,7 @@ defmodule MacroTest do
assert Macro.to_string(quote do: &foo/0) == "&foo/0"
assert Macro.to_string(quote do: &Foo.foo/0) == "&Foo.foo/0"
assert Macro.to_string(quote do: & &1 + &2) == "&(&1 + &2)"
assert Macro.to_string(quote do: & &1) == "&(&1)"
end
test "containers to string" do
@@ -422,7 +425,6 @@ defmodule MacroTest do
assert Macro.to_string(quote do: !(foo > bar)) == "!(foo > bar)"
assert Macro.to_string(quote do: @foo(bar)) == "@foo(bar)"
assert Macro.to_string(quote do: identity(&1)) == "identity(&1)"
assert Macro.to_string(quote do: identity(&foo)) == "identity(&foo)"
end
test "access to string" do
+11
View File
@@ -319,6 +319,17 @@ defmodule Protocol.ConsolidationTest do
refute Protocol.consolidated?(Enumerable)
end
test "consolidation prevents new implementations" do
assert ExUnit.CaptureIO.capture_io(:stderr, fn ->
defimpl WithAny, for: Integer do
def ok(_any), do: :ok
end
end) =~ ~r"warning: the .+WithAny protocol has already been consolidated"
after
:code.purge(WithAny.Atom)
:code.delete(WithAny.Atom)
end
test "consolidated implementations without any" do
assert is_nil Sample.impl_for(:foo)
assert is_nil Sample.impl_for(fn(x) -> x end)
+1 -1
View File
@@ -58,7 +58,7 @@ defmodule ExUnit do
@typedoc "The error state returned by ExUnit.Test and ExUnit.TestCase"
@type state :: nil | {:failed, failed} | {:skip, binary} | {:invalid, module}
@type failed :: {Exception.kind, reason :: term, stacktrace :: [tuple]}
@type failed :: [{Exception.kind, reason :: term, stacktrace :: [tuple]}]
defmodule Test do
@moduledoc """
+22 -23
View File
@@ -102,15 +102,15 @@ defmodule ExUnit.Assertions do
# is not nil nor false. We need to rewrite the if
# to avoid silly warnings though.
return =
no_warning(quote do
if right do
right
else
raise ExUnit.AssertionError,
expr: expr,
message: "Expected truthy, got #{inspect right}"
end
end)
no_warning(quote do
if right do
right
else
raise ExUnit.AssertionError,
expr: expr,
message: "Expected truthy, got #{inspect right}"
end
end)
quote do
right = unquote(right)
@@ -401,20 +401,19 @@ defmodule ExUnit.Assertions do
end
defp collect_vars_from_pattern(expr) do
{_, vars} =
Macro.prewalk(expr, [], fn
{:::, _, [left, _]}, acc ->
{[left], acc}
{skip, _, [_]}, acc when skip in [:^, :@] ->
{:ok, acc}
{:_, _, context}, acc when is_atom(context) ->
{:ok, acc}
{name, _, context}, acc when is_atom(name) and is_atom(context) ->
{:ok, [{name, [generated: true], context}|acc]}
node, acc ->
{node, acc}
end)
Enum.uniq(vars)
Macro.prewalk(expr, [], fn
{:::, _, [left, _]}, acc ->
{[left], acc}
{skip, _, [_]}, acc when skip in [:^, :@] ->
{:ok, acc}
{:_, _, context}, acc when is_atom(context) ->
{:ok, acc}
{name, meta, context}, acc when is_atom(name) and is_atom(context) ->
{:ok, [{name, [generated: true] ++ meta, context} | acc]}
node, acc ->
{node, acc}
end)
|> elem(1)
end
defp no_warning({name, meta, args}) do
@@ -11,6 +11,16 @@ alias ExUnit.AssertionsTest.Value
defmodule ExUnit.AssertionsTest do
use ExUnit.Case, async: true
defmacrop assert_ok(arg) do
quote do
assert {:ok, val} = ok(unquote(arg))
end
end
test "assert inside macro" do
assert_ok 42
end
test "assert with true value" do
true = assert Value.truthy
end
+1
View File
@@ -63,6 +63,7 @@ defmodule IEx.Config do
defp default_option(:colors), do: [{:enabled, IO.ANSI.enabled?} | default_colors()]
defp default_option(:inspect), do: []
defp default_option(:history_size), do: 20
defp default_option(:width), do: width()
defp default_option(prompt) when prompt in [:default_prompt, :alive_prompt] do
"%prefix(%counter)>"
+11 -9
View File
@@ -227,13 +227,14 @@ defimpl IEx.Info, for: PID do
def info(pid) do
extra =
if info = Process.info(pid, @keys) do
["Alive": true,
"Name": process_name(info[:registered_name]),
"Links": links(info[:links]),
"Message queue length": info[:message_queue_len]]
else
["Alive": false]
case :rpc.pinfo(pid, @keys) do
[_|_] = info ->
["Alive": true,
"Name": process_name(info[:registered_name]),
"Links": links(info[:links]),
"Message queue length": info[:message_queue_len]]
_ ->
["Alive": false]
end
["Data type": "PID"] ++ extra ++
@@ -257,9 +258,10 @@ end
defimpl IEx.Info, for: Port do
def info(port) do
port_info = Port.info(port)
connected = :rpc.call(node(port), :erlang, :port_info, [port, :connected])
["Data type": "Port",
"Open": port_info != nil,
"Open": match?({:connected, _}, connected),
"Reference modules": "Port"]
end
end
+9
View File
@@ -0,0 +1,9 @@
Code.require_file "../test_helper.exs", __DIR__
defmodule IEx.ConfigTest do
use ExUnit.Case, async: true
test "configuration sets a default for width" do
assert IEx.Config.configuration[:width] == 80
end
end
+31 -24
View File
@@ -49,16 +49,12 @@ defmodule Mix.Dep.Converger do
end
defp all(acc, lock, opts, callback) do
deps = Mix.Dep.Loader.children()
deps = Enum.map(deps, &(%{&1 | top_level: true}))
lock_given? = !!lock
main = Mix.Dep.Loader.children()
main = Enum.map(main, &(%{&1 | top_level: true}))
apps = Enum.map(main, &(&1.app))
# Filter the dependencies per environment. We pass the ones
# left out as accumulator and upper breadth to help catch
# inconsistencies across environment, specially regarding
# the :only option. They are filtered again later.
current = Enum.map(deps, &(&1.app))
{main, only} = Mix.Dep.Loader.partition_by_env(deps, opts)
lock_given? = !!lock
env = opts[:env]
# If no lock was given, let's read one to fill in the deps
lock = lock || Mix.Dep.Lock.read
@@ -68,7 +64,7 @@ defmodule Mix.Dep.Converger do
# lazily loaded, we need to check for it on every
# iteration.
{deps, rest, lock} =
all(main, only, [], current, callback, acc, lock, fn dep ->
all(main, apps, callback, acc, lock, env, fn dep ->
if (remote = Mix.RemoteConverger.get) && remote.remote?(dep) do
{:loaded, dep}
else
@@ -76,9 +72,6 @@ defmodule Mix.Dep.Converger do
end
end)
# Filter deps per environment once more. If the filtered
# dependencies had no conflicts, they are removed now.
{deps, _} = Mix.Dep.Loader.partition_by_env(deps, opts)
diverged? = Enum.any?(deps, &Mix.Dep.diverged?/1)
# Run remote converger if one is available and rerun Mix's
@@ -99,12 +92,11 @@ defmodule Mix.Dep.Converger do
# In case no lock was given, we will use the local lock
# which is potentially stale. So remote.deps/2 needs to always
# check if the data it finds in the lock is actually valid.
all(main, [], [], Enum.map(main, &(&1.app)), callback, rest, lock, fn dep ->
cond do
cached = deps[dep.app] ->
{:loaded, cached}
true ->
{:unloaded, dep, remote.deps(dep, lock)}
all(main, apps, callback, rest, lock, env, fn dep ->
if cached = deps[dep.app] do
{:loaded, cached}
else
{:unloaded, dep, remote.deps(dep, lock)}
end
end)
else
@@ -112,6 +104,14 @@ defmodule Mix.Dep.Converger do
end
end
defp all(main, apps, callback, rest, lock, env, cache) do
{deps, rest, lock} = all(main, [], [], apps, callback, rest, lock, env, cache)
# When traversing dependencies, we keep skipped ones to
# find conflicts. We remove them now after traversal.
{deps, _} = Mix.Dep.Loader.partition_by_env(deps, env)
{deps, rest, lock}
end
# We traverse the tree of dependencies in a breadth-first
# fashion. The reason for this is that we converge
# dependencies, but allow the parent to override any
@@ -151,10 +151,14 @@ defmodule Mix.Dep.Converger do
# Now, since "d" was specified in a parent project, no
# exception is going to be raised since d is considered
# to be the authoritative source.
defp all([dep|t], acc, upper_breadths, current_breadths, callback, rest, lock, cache) do
defp all([dep|t], acc, upper_breadths, current_breadths, callback, rest, lock, env, cache) do
cond do
new_acc = diverged_deps(acc, upper_breadths, dep) ->
all(t, new_acc, upper_breadths, current_breadths, callback, rest, lock, cache)
all(t, new_acc, upper_breadths, current_breadths, callback, rest, lock, env, cache)
Mix.Dep.Loader.skip?(dep, env) ->
# We still keep skipped dependencies around to detect conflicts.
# They must be rejected after every all iteration.
all(t, [dep|acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)
true ->
dep =
case cache.(dep) do
@@ -170,12 +174,15 @@ defmodule Mix.Dep.Converger do
end
dep = %{dep | deps: reject_non_fullfilled_optional(dep.deps, current_breadths)}
{acc, rest, lock} = all(t, [dep|acc], upper_breadths, current_breadths, callback, rest, lock, cache)
all(dep.deps, acc, current_breadths, Enum.map(dep.deps, &(&1.app)) ++ current_breadths, callback, rest, lock, cache)
{acc, rest, lock} =
all(t, [dep|acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)
new_breadths = Enum.map(dep.deps, &(&1.app)) ++ current_breadths
all(dep.deps, acc, current_breadths, new_breadths, callback, rest, lock, env, cache)
end
end
defp all([], acc, _upper, _current, _callback, rest, lock, _cache) do
defp all([], acc, _upper, _current, _callback, rest, lock, _env, _cache) do
{acc, rest, lock}
end
+14 -14
View File
@@ -21,18 +21,18 @@ defmodule Mix.Dep.Loader do
@doc """
Partitions loaded dependencies by environment.
"""
def partition_by_env(deps, opts) do
if env = opts[:env] do
Enum.partition(deps, fn
%Mix.Dep{status: {:divergedonly, _}} ->
true
%Mix.Dep{opts: opts} ->
only = opts[:only] |> List.wrap |> validate_only!
only == [] or env in List.wrap(only)
end)
else
{deps, []}
end
def partition_by_env(deps, nil), do: {deps, []}
def partition_by_env(deps, env), do: Enum.partition(deps, &not skip?(&1, env))
@doc """
Check if a dependency must be skipped according to the environment.
"""
def skip?(_dep, nil), do: false
def skip?(%Mix.Dep{status: {:divergedonly, _}}, _), do: false
def skip?(%Mix.Dep{opts: opts}, env) do
only = opts[:only]
validate_only!(only)
only != nil and not env in List.wrap(only)
end
@doc """
@@ -283,7 +283,7 @@ defmodule Mix.Dep.Loader do
end
defp validate_only!(only) do
for entry <- only, not is_atom(entry) do
for entry <- List.wrap(only), not is_atom(entry) do
Mix.raise "Expected :only in dependency to be an atom or a list of atoms, got: #{inspect only}"
end
only
@@ -293,7 +293,7 @@ defmodule Mix.Dep.Loader do
from = Path.absname("mix.exs")
(Mix.Project.config[:deps] || [])
|> Enum.map(&to_dep(&1, from))
|> partition_by_env(opts)
|> partition_by_env(opts[:env])
|> elem(0)
end
+2 -3
View File
@@ -255,9 +255,8 @@ defmodule Mix.DepTest do
deps = Mix.Dep.loaded([])
assert length(deps) == 2
deps = Mix.Dep.loaded([env: :prod])
assert length(deps) == 1
assert Enum.find deps, &match?(%Mix.Dep{app: :foo}, &1)
assert [dep] = Mix.Dep.loaded([env: :prod])
assert dep.app == :foo
end
end
end
+8 -4
View File
@@ -113,15 +113,19 @@ defmodule Mix.UmbrellaTest do
end
"""
# Should work across all environments
# Does not fetch when filtered
Mix.Tasks.Deps.Get.run ["--only", "dev"]
refute_received {:mix_shell, :info, ["* Getting git_repo" <> _]}
# But works across all environments
Mix.Tasks.Deps.Get.run []
assert_received {:mix_shell, :info, ["* Getting git_repo" <> _]}
# Works on the current environment only
# Does not show by default
Mix.Tasks.Deps.run []
refute_received {:mix_shell, :info, ["* git_repo " <> _]}
refute_received {:mix_shell, :info, ["* git_repo" <> _]}
# Works on the other environment only
# But shows on proper environment
Mix.env(:other)
Mix.Tasks.Deps.run []
assert_received {:mix_shell, :info, ["* git_repo " <> _]}
+1 -1
View File
@@ -1,6 +1,6 @@
{application, elixir,
[{description, "elixir"},
{vsn, "1.2.0"},
{vsn, "1.2.1"},
{modules, [
elixir
]},