Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7340ca2d92 | ||
|
|
3beb72e6f4 | ||
|
|
45f9c9c1ce | ||
|
|
cde12f75fc | ||
|
|
eb7e0984bd | ||
|
|
57a52b9a05 | ||
|
|
c352766f43 | ||
|
|
15222304eb | ||
|
|
f6aced3072 | ||
|
|
5e5eaca78f | ||
|
|
ab61c09b3d | ||
|
|
ccf939e9e7 | ||
|
|
331a70ade9 | ||
|
|
4329429823 | ||
|
|
05418eaa4b | ||
|
|
d1efe7c012 | ||
|
|
0494629c4c | ||
|
|
941ce758ed | ||
|
|
759457ecfa | ||
|
|
bfa894a15f | ||
|
|
407d2e1835 | ||
|
|
94e5c72793 | ||
|
|
d90c7bbb79 | ||
|
|
5b3e13c90b | ||
|
|
2021fed11a | ||
|
|
e74701ef07 | ||
|
|
284a7d00d6 | ||
|
|
a31549cc7d | ||
|
|
be6e54de16 | ||
|
|
6fd203ba7b | ||
|
|
0a677fe403 | ||
|
|
5b25761d4c | ||
|
|
e1f10a9386 | ||
|
|
37321cd529 | ||
|
|
8023b5e0c6 | ||
|
|
19d41f1fdb | ||
|
|
907149c8c7 | ||
|
|
c99e1cdc9b | ||
|
|
402a33867b |
+60
-2
@@ -14,7 +14,17 @@ Or:
|
||||
|
||||
saudação = "Bom dia!"
|
||||
|
||||
Elixir follows the recommendations in [Unicode Annex #31](http://unicode.org/reports/tr31/) to make the language more accessible to other languages and communities. Identifiers must still be a sequence of letters, followed by digits and combining marks. This means symbols, such as mathematical notations and emoji, are not allowed identifiers.
|
||||
Elixir follows the recommendations in [Unicode Annex #31](http://unicode.org/reports/tr31/) to make the language more accessible to other languages and communities. Identifiers must start with a letter or underscore, optionally followed by letters, digits, and underscores. Here letter means any UTF-8 letter-character (optionally with a combining mark) and digit means a UTF-8 decimal-digit-character. If you're using ASCII, this does what you'd expect.
|
||||
|
||||
Examples of valid variables are:
|
||||
|
||||
name josé _age まつもと _42 адрес
|
||||
|
||||
Examples of invalid variables include:
|
||||
|
||||
name• a±2 42
|
||||
|
||||
Symbols, such as mathematical notations and emoji, are not allowed identifiers.
|
||||
|
||||
For a complete reference on Elixir syntax, see the [Syntax Reference](https://hexdocs.pm/elixir/syntax-reference.html). For technical details on Unicode support, see [Unicode Syntax](https://hexdocs.pm/elixir/unicode-syntax.html).
|
||||
|
||||
@@ -156,6 +166,53 @@ Overall, using `@impl` has the following advantages:
|
||||
|
||||
This release brings further improvements to Calendar types. It adds arithmetic and others functions to `Time`, `Date`, `NaiveDateTime` and `Datetime` as well as conversion between different calendars.
|
||||
|
||||
## v1.5.3 (2017-12-19)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Calendar] Consider microseconds in maximum possible Calendar.ISO datetime
|
||||
* [Enum] Fix `chunk_every/4` when `step > count`
|
||||
* [Kernel] Warn duplicate definitions in macros
|
||||
* [Kernel] Remove dialyzer warnings from else in with clauses
|
||||
* [Kernel] Do not warning on upcoming `@deprecated` and `@since` attributes for v1.6
|
||||
* [MapSet] Return valid MapSet when union-ing a legacy MapSet
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit] Fix compiler warnings in `assert_receive/3`
|
||||
|
||||
## v1.5.2 (2017-09-29)
|
||||
|
||||
### 1. Enhacements
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Optimize function definition with multiple clauses by not traversing the internal clauses table
|
||||
* [Kernel] Warn if unary operators are followed by new lines
|
||||
* [Registry] Use the name of the Registry as its `:id` in the `child_spec/1` function
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [DateTime] Fix negative microsecond result when passing negative Unix epochs to `from_unix/2`
|
||||
* [Kernel] Improve error message for oversized atoms
|
||||
* [Kernel] Ensure `@impl` attribute also propagates to clauses from default arguments
|
||||
* [Kernel] Emit proper error for unknown vars inside binary pattern in match
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx] Do not crash IEx unexpectedly on `System.stop/0`
|
||||
* [IEx.Helpers] Ensure exiting a breakpoint set inside a breakpoint does not terminate the shell unexpectedly
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix local.hex] Ensure `--if-missing` flag works as advertised
|
||||
* [mix test] Do not trigger additional error reports when there is a failure when loading test files
|
||||
* [Mix.SCM.Git] Ensure errors when invoking `git` propagate correctly
|
||||
|
||||
## v1.5.1 (2017-08-01)
|
||||
|
||||
### 1. Enhancements
|
||||
@@ -171,7 +228,7 @@ This release brings further improvements to Calendar types. It adds arithmetic a
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix profile.*] Allow profile tasks to run without a project
|
||||
* [mix profile] Allow profile tasks to run without a project
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
@@ -296,6 +353,7 @@ This release brings further improvements to Calendar types. It adds arithmetic a
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit] Properly account failed tests when `setup_all` fails
|
||||
* [ExUnit] Having two or more `describe` blocks with the same name will now raise an error.
|
||||
|
||||
#### IEx
|
||||
|
||||
|
||||
@@ -9,17 +9,57 @@ defmodule Application do
|
||||
Applications are defined with an application file named `APP.app` where
|
||||
`APP` is the application name, usually in `underscore_case`. The application
|
||||
file must reside in the same `ebin` directory as the compiled modules of the
|
||||
application.
|
||||
application. In Elixir, the Mix build tool is responsible for compiling your
|
||||
source code and generating your application `.app` file. You can learn more
|
||||
about the generation of `.app` files by typing `mix help compile.app`.
|
||||
|
||||
In Elixir, Mix is responsible for compiling your source code and
|
||||
generating your application `.app` file. Furthermore, Mix is also
|
||||
responsible for configuring, starting and stopping your application
|
||||
and its dependencies. For this reason, this documentation will focus
|
||||
on the remaining aspects of your application: the application environment
|
||||
and the application callback module.
|
||||
Once your application is compiled, running your system is a matter of starting
|
||||
your current application and its dependencies. Differently from other languages,
|
||||
Elixir does not have a `main` procedure that is responsible for starting your
|
||||
system. Instead, you start one or more applications, each with their own
|
||||
initialization and termination logic.
|
||||
|
||||
You can learn more about Mix generation of `.app` files by typing
|
||||
`mix help compile.app`.
|
||||
Starting an application is done via the "application module callback", which
|
||||
is a module that defines the `start/2` function. The `start/2` function should
|
||||
then start a supervisor, which is often called as the top-level supervisor, since
|
||||
it sits at the root of a potentially long supervision tree. When the system is
|
||||
shutting down, all applications shut down their top-level supervisor, which
|
||||
terminates children in the opposite order they are started.
|
||||
|
||||
We have mentioned the Mix build tool is responsible for compiling applications,
|
||||
but it is also capable of running applications. For example, `mix test`
|
||||
automatically starts your application dependencies and your application itself
|
||||
before your test runs. `mix run --no-halt` also boots your current project and
|
||||
can be used to start a long running system. See `mix help run`.
|
||||
|
||||
Developers can also use tools like [Distillery](https://github.com/bitwalker/distillery)
|
||||
that build **releases**. Releases are able to package all of your source code
|
||||
as well as the Erlang VM into a single directory. Releases also give you explicit
|
||||
control over how each application is started and in which order. They also provide
|
||||
a more streamlined mechanism for starting and stopping systems, debugging, logging,
|
||||
as well as system monitoring.
|
||||
|
||||
Finally, Elixir provides tools such as escripts and archives, which are
|
||||
different mechanisms for packaging your application. Those are typically used
|
||||
when tools must be shared between developers and not as deployment options.
|
||||
See `mix help archive.build` and `mix help escript.build` for more detail.
|
||||
|
||||
Shutting down a live system cleanly can be done by calling `System.stop/1`.
|
||||
It will shut down all applications in the opposite order they are started.
|
||||
Each application will then shutdown its top-level supervisor, if one is
|
||||
available, which then shuts down its children.
|
||||
|
||||
From Erlang/OTP 19.1, a SIGTERM from the operating system will automatically
|
||||
translate to `System.stop/0`. Erlang/OTP 20 gives user more explicit control
|
||||
over OS signals via the `:os.set_signal/2` function.
|
||||
|
||||
Applications also provide an "application environment", which is how
|
||||
applications are configured. The application environment can either be set
|
||||
statically, via a configuration file, or dynamically via `put_env/3` and
|
||||
friends.
|
||||
|
||||
Over the next sections, we will cover the "application environment" and
|
||||
the "application module callback" in more detail.
|
||||
|
||||
## Application environment
|
||||
|
||||
@@ -40,9 +80,13 @@ defmodule Application do
|
||||
Application.get_env(:APP_NAME, :hello)
|
||||
#=> :world
|
||||
|
||||
It is also possible to put and delete values from the application value,
|
||||
including new values that are not defined in the environment file (although
|
||||
this should be avoided).
|
||||
Applications and dependencies in Mix projects are typically configured
|
||||
via the `config/config.exs` file. For example, someone using your
|
||||
application can configure the `:hello` key as follows:
|
||||
|
||||
config :APP_NAME, hello: :brand_new_world
|
||||
|
||||
It is also possible to configure applications dynamically via `put_env/3`.
|
||||
|
||||
Keep in mind that each application is responsible for its environment.
|
||||
Do not use the functions in this module for directly accessing or modifying
|
||||
@@ -78,8 +122,8 @@ defmodule Application do
|
||||
|
||||
The `type` argument passed to `start/2` is usually `:normal` unless in a
|
||||
distributed setup where application takeovers and failovers are configured.
|
||||
This particular aspect of applications is explained in more detail in the
|
||||
OTP documentation:
|
||||
Distributed applications is beyond the scope of this documentation. For those
|
||||
interested on the topic, please access the OTP documentation:
|
||||
|
||||
* [`:application` module](http://www.erlang.org/doc/man/application.html)
|
||||
* [Applications – OTP Design Principles](http://www.erlang.org/doc/design_principles/applications.html)
|
||||
|
||||
@@ -326,25 +326,25 @@ defmodule DateTime do
|
||||
@spec to_iso8601(Calendar.datetime, :extended | :basic ) :: String.t
|
||||
def to_iso8601(datetime, format \\ :extended)
|
||||
|
||||
def to_iso8601(_, format) when format not in [:extended, :basic] do
|
||||
raise ArgumentError, "DateTime.to_iso8601/2 expects format to be :extended or :basic, got: #{inspect format}"
|
||||
end
|
||||
|
||||
def to_iso8601(%{calendar: Calendar.ISO, year: year, month: month, day: day,
|
||||
hour: hour, minute: minute, second: second, microsecond: microsecond,
|
||||
time_zone: time_zone, zone_abbr: zone_abbr, utc_offset: utc_offset, std_offset: std_offset}, format) when format in [:extended, :basic] do
|
||||
time_zone: time_zone, zone_abbr: zone_abbr, utc_offset: utc_offset, std_offset: std_offset}, format) do
|
||||
Calendar.ISO.datetime_to_iso8601(year, month, day, hour, minute, second, microsecond,
|
||||
time_zone, zone_abbr, utc_offset, std_offset, format)
|
||||
end
|
||||
|
||||
def to_iso8601(%{calendar: _, year: _, month: _, day: _,
|
||||
hour: _, minute: _, second: _, microsecond: _,
|
||||
time_zone: _, zone_abbr: _, utc_offset: _, std_offset: _} = datetime, format) when format in [:extended, :basic] do
|
||||
time_zone: _, zone_abbr: _, utc_offset: _, std_offset: _} = datetime, format) do
|
||||
datetime
|
||||
|> convert!(Calendar.ISO)
|
||||
|> to_iso8601(format)
|
||||
end
|
||||
|
||||
def to_iso8601(_, format) do
|
||||
raise ArgumentError, "DateTime.to_iso8601/2 expects format to be :extended or :basic, got: #{inspect format}"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses the extended "Date and time of day" format described by
|
||||
[ISO 8601:2004](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
|
||||
@@ -17,7 +17,7 @@ defmodule Calendar.ISO do
|
||||
|
||||
@unix_epoch 62167219200
|
||||
@unix_start 1_000_000 * -@unix_epoch
|
||||
@unix_end 1_000_000 * (315569519999 - @unix_epoch)
|
||||
@unix_end 315569519999999999 - @unix_epoch * 1_000_000
|
||||
@unix_range_microseconds @unix_start..@unix_end
|
||||
|
||||
@type year :: 0..9999
|
||||
@@ -122,13 +122,13 @@ defmodule Calendar.ISO do
|
||||
def date_to_iso_days_days(1970, 1, 1) do
|
||||
719528
|
||||
end
|
||||
def date_to_iso_days_days(year, month, day) do
|
||||
def date_to_iso_days_days(year, month, day) when year <= 9999 do
|
||||
:calendar.date_to_gregorian_days(year, month, day)
|
||||
end
|
||||
|
||||
# Converts count of days since 0000-01-01 to {year, month, day} tuple.
|
||||
@doc false
|
||||
def date_from_iso_days_days(days) do
|
||||
def date_from_iso_days_days(days) when days <= 3652424 do
|
||||
:calendar.gregorian_days_to_date(days)
|
||||
end
|
||||
|
||||
@@ -314,10 +314,11 @@ defmodule Calendar.ISO do
|
||||
def from_unix(integer, unit) when is_integer(integer) do
|
||||
total = System.convert_time_unit(integer, unit, :microsecond)
|
||||
if total in @unix_range_microseconds do
|
||||
microsecond = rem(total, 1_000_000)
|
||||
microseconds = Integer.mod(total, @microseconds_per_second)
|
||||
seconds = @unix_epoch + Integer.floor_div(total, @microseconds_per_second)
|
||||
precision = precision_for_unit(unit)
|
||||
{date, time} = :calendar.gregorian_seconds_to_datetime(@unix_epoch + div(total, 1_000_000))
|
||||
{:ok, date, time, {microsecond, precision}}
|
||||
{date, time} = :calendar.gregorian_seconds_to_datetime(seconds)
|
||||
{:ok, date, time, {microseconds, precision}}
|
||||
else
|
||||
{:error, :invalid_unix_time}
|
||||
end
|
||||
|
||||
+19
-1
@@ -362,6 +362,9 @@ defmodule Enum do
|
||||
iex> Enum.chunk_every([1, 2, 3, 4], 10)
|
||||
[[1, 2, 3, 4]]
|
||||
|
||||
iex> Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, [])
|
||||
[[1, 2], [4, 5]]
|
||||
|
||||
"""
|
||||
@spec chunk_every(t, pos_integer, pos_integer, t | :discard) :: [list]
|
||||
def chunk_every(enumerable, count, step, leftover \\ [])
|
||||
@@ -832,13 +835,28 @@ defmodule Enum do
|
||||
Filters the enumerable, i.e. returns only those elements
|
||||
for which `fun` returns a truthy value.
|
||||
|
||||
See also `reject/2`.
|
||||
See also `reject/2` which discards all elements where the
|
||||
function returns true.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.filter([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
|
||||
[2]
|
||||
|
||||
Keep in mind that `filter` is not capable of filtering and
|
||||
transforming an element at the same time. If you would like
|
||||
to do so, consider using `flat_map/2`. For example, if you
|
||||
want to convert all strings that represent an integer and
|
||||
discard the invalid one in one pass:
|
||||
|
||||
strings = ["1234", "abc", "12ab"]
|
||||
Enum.flat_map(strings, fn string ->
|
||||
case Integer.parse(string) do
|
||||
{int, _rest} -> [int] # transform to integer
|
||||
:error -> [] # skip the value
|
||||
end
|
||||
end)
|
||||
|
||||
"""
|
||||
@spec filter(t, (element -> as_boolean(term))) :: list
|
||||
def filter(enumerable, fun) when is_list(enumerable) do
|
||||
|
||||
@@ -131,7 +131,7 @@ defmodule Macro do
|
||||
valid_alias?(charlist) ->
|
||||
:alias
|
||||
true ->
|
||||
case :elixir_config.get(:identifier_tokenizer).tokenize(charlist) do
|
||||
case :elixir_config.safe_get(:identifier_tokenizer, String.Tokenizer).tokenize(charlist) do
|
||||
{kind, _acc, [], _, _, special} ->
|
||||
if kind == :identifier and not :lists.member(?@, special) do
|
||||
:callable
|
||||
|
||||
@@ -353,7 +353,8 @@ defmodule MapSet do
|
||||
%{map_set | map: Map.merge(map1, map2)}
|
||||
end
|
||||
def union(%MapSet{map: map1}, %MapSet{map: map2}) do
|
||||
new_from_list(Map.keys(map1) ++ Map.keys(map2), [])
|
||||
map = new_from_list(Map.keys(map1) ++ Map.keys(map2), [])
|
||||
%MapSet{map: map}
|
||||
end
|
||||
|
||||
defp order_by_size(map1, map2) when map_size(map1) > map_size(map2), do: {map2, map1}
|
||||
|
||||
@@ -1192,8 +1192,13 @@ defmodule Module do
|
||||
case :ets.take(table, :impl) do
|
||||
[{:impl, value, _, _}] ->
|
||||
impls = :ets.lookup_element(table, {:elixir, :impls}, 2)
|
||||
impl = {{name, length(args)}, kind, line, file, value}
|
||||
:ets.insert(table, {{:elixir, :impls}, [impl | impls]})
|
||||
{total, defaults} = args_count(args, 0, 0)
|
||||
|
||||
impl = for arity <- total..(total - defaults), into: impls do
|
||||
{{name, arity}, kind, line, file, value}
|
||||
end
|
||||
|
||||
:ets.insert(table, {{:elixir, :impls}, impl})
|
||||
[] ->
|
||||
:ok
|
||||
end
|
||||
@@ -1201,6 +1206,14 @@ defmodule Module do
|
||||
:ok
|
||||
end
|
||||
|
||||
defp args_count([{:\\, _, _} | tail], total, defaults) do
|
||||
args_count(tail, total + 1, defaults + 1)
|
||||
end
|
||||
defp args_count([_head | tail], total, defaults) do
|
||||
args_count(tail, total + 1, defaults)
|
||||
end
|
||||
defp args_count([], total, defaults), do: {total, defaults}
|
||||
|
||||
@doc false
|
||||
def check_behaviours_and_impls(env, table, all_definitions, overridable_pairs) do
|
||||
behaviours = :ets.lookup_element(table, :behaviour, 2)
|
||||
|
||||
@@ -304,7 +304,7 @@ defmodule Registry do
|
||||
@doc false
|
||||
def child_spec(opts) do
|
||||
%{
|
||||
id: Registry,
|
||||
id: Keyword.get(opts, :name, Registry),
|
||||
start: {Registry, :start_link, [opts]},
|
||||
type: :supervisor
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ defmodule Stream.Reducers do
|
||||
{:cont, new_state}
|
||||
end
|
||||
end, fn {acc_buffer, acc_count} ->
|
||||
if leftover == :discard or acc_count == 0 do
|
||||
if leftover == :discard or acc_count == 0 or (step > count and acc_count >= count) do
|
||||
{:cont, []}
|
||||
else
|
||||
{:cont, :lists.reverse(acc_buffer, Enum.take(leftover, count - acc_count)), []}
|
||||
|
||||
@@ -82,7 +82,7 @@ defmodule Supervisor do
|
||||
supervisor will automatically start a new one, with the initial stack
|
||||
of `[:hello]`:
|
||||
|
||||
GenServer.call(MyStack, :pop)
|
||||
GenServer.call(Stack, :pop)
|
||||
#=> :hello
|
||||
|
||||
Supervisors support different strategies; in the example above, we
|
||||
@@ -187,7 +187,7 @@ defmodule Supervisor do
|
||||
`GenServer` with a shutdown limit of 10 seconds (10_000 miliseconds),
|
||||
one might do:
|
||||
|
||||
use GenServer, shutdown: 10000
|
||||
use GenServer, shutdown: 10_000
|
||||
|
||||
Let's understand what the `:shutdown` and `:restart` options control.
|
||||
|
||||
@@ -333,14 +333,14 @@ defmodule Supervisor do
|
||||
The first argument given to `start_link/2` is a list of children which may
|
||||
be either:
|
||||
|
||||
* a module - such as `Stack`. In this case, it is equivalent to passing
|
||||
`{Stack, []}` (which means `Stack.child_spec/1` is invoked with an empty
|
||||
keywords list)
|
||||
* a tuple with a module as first element and the start argument as second -
|
||||
such as `{Stack, [:hello]}`. When such format is used, the supervisor
|
||||
will retrieve the child specification from the given module.
|
||||
* a map representing the child specification itself - such as the child
|
||||
specification map outlined in the previous section.
|
||||
* a module - such as `Stack`. In this case, it is equivalent to passing
|
||||
`{Stack, []}` (which means `Stack.child_spec/1` is invoked with an empty
|
||||
keywords list)
|
||||
* a tuple with a module as first element and the start argument as second -
|
||||
such as `{Stack, [:hello]}`. When such format is used, the supervisor
|
||||
will retrieve the child specification from the given module.
|
||||
* a map representing the child specification itself - such as the child
|
||||
specification map outlined in the previous section.
|
||||
|
||||
The second argument is a keyword list of options:
|
||||
|
||||
@@ -591,38 +591,7 @@ defmodule Supervisor do
|
||||
e in UndefinedFunctionError ->
|
||||
case System.stacktrace do
|
||||
[{^module, :child_spec, [^arg], _} | _] ->
|
||||
raise ArgumentError, """
|
||||
The module #{inspect module} was given as a child to a supervisor
|
||||
but it does not implement child_spec/1.
|
||||
|
||||
If you own the given module, please define a child_spec/1 function
|
||||
that receives an argument and returns a child specification as a map.
|
||||
For example:
|
||||
|
||||
def child_spec(opts) do
|
||||
%{
|
||||
id: __MODULE__,
|
||||
start: {__MODULE__, :start_link, [opts]},
|
||||
type: :worker,
|
||||
restart: :permanent,
|
||||
shutdown: 500
|
||||
}
|
||||
end
|
||||
|
||||
Note that "use Agent", "use GenServer" and so on automatically define
|
||||
this function for you.
|
||||
|
||||
However, if you don't own the given module and it doesn't implement
|
||||
child_spec/1, instead of passing the module name directly as a supervisor
|
||||
child, you will have to pass a child specification as a map:
|
||||
|
||||
%{
|
||||
id: #{inspect module},
|
||||
start: {#{inspect module}, :start_link, [arg1, arg2]}
|
||||
}
|
||||
|
||||
See the Supervisor documentation for more information.
|
||||
"""
|
||||
raise ArgumentError, child_spec_error(module)
|
||||
stack ->
|
||||
reraise e, stack
|
||||
end
|
||||
@@ -647,6 +616,45 @@ defmodule Supervisor do
|
||||
"""
|
||||
end
|
||||
|
||||
defp child_spec_error(module) do
|
||||
if Code.ensure_loaded?(module) do
|
||||
"""
|
||||
The module #{inspect module} was given as a child to a supervisor
|
||||
but it does not implement child_spec/1.
|
||||
|
||||
If you own the given module, please define a child_spec/1 function
|
||||
that receives an argument and returns a child specification as a map.
|
||||
For example:
|
||||
|
||||
def child_spec(opts) do
|
||||
%{
|
||||
id: __MODULE__,
|
||||
start: {__MODULE__, :start_link, [opts]},
|
||||
type: :worker,
|
||||
restart: :permanent,
|
||||
shutdown: 500
|
||||
}
|
||||
end
|
||||
|
||||
Note that "use Agent", "use GenServer" and so on automatically define
|
||||
this function for you.
|
||||
|
||||
However, if you don't own the given module and it doesn't implement
|
||||
child_spec/1, instead of passing the module name directly as a supervisor
|
||||
child, you will have to pass a child specification as a map:
|
||||
|
||||
%{
|
||||
id: #{inspect module},
|
||||
start: {#{inspect module}, :start_link, [arg1, arg2]}
|
||||
}
|
||||
|
||||
See the Supervisor documentation for more information.
|
||||
"""
|
||||
else
|
||||
"The module #{inspect module} was given as a child to a supervisor but it does not exist."
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Builds and overrides a child specification.
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ defmodule URI do
|
||||
"""
|
||||
@spec default_port(binary) :: nil | non_neg_integer
|
||||
def default_port(scheme) when is_binary(scheme) do
|
||||
:elixir_config.get({:uri, scheme})
|
||||
:elixir_config.safe_get({:uri, scheme}, nil)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -22,8 +22,8 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, RequireSize)
|
||||
%% on subparts, however we can't assign new variables.
|
||||
{ER, MatchSize} =
|
||||
case E of
|
||||
{EExtracted, _} -> {EExtracted, false}; %% expand_arg, no assigns
|
||||
_ -> {E#{context := nil}, T /= []} %% expand, revert assigns
|
||||
{EExtracted, _} -> {EExtracted, false}; %% expand_arg, no assigns
|
||||
_ -> {E#{context := nil, match_vars := warn}, T /= []} %% expand, revert assigns
|
||||
end,
|
||||
|
||||
ERight = expand_specs(expr_type(ELeft), Meta, Right, ER, RequireSize or MatchSize),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-module(elixir_config).
|
||||
-compile({no_auto_import, [get/1]}).
|
||||
-export([new/1, delete/1, put/2, get/1, update/2, get_and_put/2]).
|
||||
-export([new/1, delete/1, put/2, get/1, safe_get/2, update/2, get_and_put/2]).
|
||||
-export([start_link/0, init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, code_change/3, terminate/2]).
|
||||
-behaviour(gen_server).
|
||||
@@ -19,9 +19,15 @@ put(Key, Value) ->
|
||||
gen_server:call(?MODULE, {put, Key, Value}).
|
||||
|
||||
get(Key) ->
|
||||
case ets:lookup(?MODULE, Key) of
|
||||
[{_, Value}] = ets:lookup(?MODULE, Key),
|
||||
Value.
|
||||
|
||||
safe_get(Key, Default) ->
|
||||
try ets:lookup(?MODULE, Key) of
|
||||
[{_, Value}] -> Value;
|
||||
[] -> nil
|
||||
[] -> Default
|
||||
catch
|
||||
_:_ -> Default
|
||||
end.
|
||||
|
||||
update(Key, Fun) ->
|
||||
|
||||
@@ -34,10 +34,11 @@ local_for(Module, Name, Arity, Kinds) ->
|
||||
|
||||
%% Take a definition out of the table
|
||||
|
||||
take_definition(Module, Tuple) ->
|
||||
take_definition(Module, {Name, Arity} = Tuple) ->
|
||||
Table = elixir_module:defs_table(Module),
|
||||
case ets:take(Table, {def, Tuple}) of
|
||||
[Result] ->
|
||||
[{{def, Tuple}, _, _, _, _, {Defaults, _, _}} = Result] ->
|
||||
ets:delete_object(Table, {{default, Name}, Arity, Defaults}),
|
||||
{Result, [Clause || {_, Clause} <- ets:take(Table, {clauses, Tuple})]};
|
||||
[] ->
|
||||
false
|
||||
@@ -95,6 +96,10 @@ store_definition(Kind, CheckClauses, Call, Body, Pos) ->
|
||||
%% extract meta information like file and context.
|
||||
{_, Meta, _} = Call,
|
||||
DoCheckClauses = (not lists:keymember(context, 1, Meta)) andalso (CheckClauses),
|
||||
Generated = case lists:keyfind(generated, 1, Meta) of
|
||||
{generated, true} -> ?generated([]);
|
||||
_ -> []
|
||||
end,
|
||||
|
||||
%% Check if there is a file information in the definition.
|
||||
%% If so, we assume this come from another source and
|
||||
@@ -113,7 +118,6 @@ store_definition(Kind, CheckClauses, Call, Body, Pos) ->
|
||||
LinifyArgs = elixir_quote:linify(Line, Key, Args),
|
||||
LinifyGuards = elixir_quote:linify(Line, Key, Guards),
|
||||
LinifyBody = elixir_quote:linify(Line, Key, Body),
|
||||
Generated = case DoCheckClauses of true -> []; false -> ?generated([]) end,
|
||||
|
||||
{EL, MetaLocation} =
|
||||
case retrieve_location(Location, ?key(E, module)) of
|
||||
@@ -186,6 +190,13 @@ store_definition(Check, Kind, Meta, Name, Arity, File, Module, Defaults, Clauses
|
||||
Tuple = {Name, Arity},
|
||||
HasBody = Clauses =/= [],
|
||||
|
||||
if
|
||||
Defaults > 0 ->
|
||||
ets:insert(Defs, {{default, Name}, Arity, Defaults});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
|
||||
MaxDefaults =
|
||||
case ets:take(Defs, {def, Tuple}) of
|
||||
[{_, StoredKind, StoredMeta, StoredFile, StoredCheck,
|
||||
@@ -194,14 +205,14 @@ store_definition(Check, Kind, Meta, Name, Arity, File, Module, Defaults, Clauses
|
||||
(Check and StoredCheck) andalso
|
||||
check_valid_clause(Meta, File, Name, Arity, Kind, Data, StoredMeta, StoredFile),
|
||||
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredDefaults, LastDefaults, LastHasBody),
|
||||
{max(Defaults, StoredDefaults), HasBody, Defaults};
|
||||
max(Defaults, StoredDefaults);
|
||||
[] ->
|
||||
{Defaults, HasBody, Defaults}
|
||||
Defaults
|
||||
end,
|
||||
|
||||
Check andalso ets:insert(Data, {?last_def, Tuple}),
|
||||
ets:insert(Defs, [{{clauses, Tuple}, Clause} || Clause <- Clauses]),
|
||||
ets:insert(Defs, {{def, Tuple}, Kind, Meta, File, Check, MaxDefaults}).
|
||||
ets:insert(Defs, {{def, Tuple}, Kind, Meta, File, Check, {MaxDefaults, HasBody, Defaults}}).
|
||||
|
||||
%% Handling of defaults
|
||||
|
||||
@@ -271,7 +282,7 @@ check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, 0, 0, _) when Defa
|
||||
check_valid_defaults(Meta, File, Name, Arity, Kind, 0, _, LastDefaults, true) when LastDefaults > 0 ->
|
||||
elixir_errors:form_warn(Meta, File, ?MODULE, {clauses_with_defaults, {Kind, Name, Arity}});
|
||||
% Clause without defaults
|
||||
check_valid_defaults(_Meta, _File, _Name, _Arity, _Kind, 0, _, _, _) -> [].
|
||||
check_valid_defaults(_Meta, _File, _Name, _Arity, _Kind, 0, _, _, _) -> ok.
|
||||
|
||||
warn_bodyless_function(Check, _Meta, _File, Module, _Kind, _Tuple)
|
||||
when Check == false; Module == 'Elixir.Module' ->
|
||||
@@ -289,12 +300,11 @@ invalid_arg({Name, _, Kind}) when is_atom(Name), is_atom(Kind) -> false;
|
||||
invalid_arg(_) -> true.
|
||||
|
||||
check_previous_defaults(Meta, Module, Name, Arity, Kind, Defaults, E) ->
|
||||
Matches = ets:match(elixir_module:defs_table(Module),
|
||||
{{def, {Name, '$2'}}, '$1', '_', '_', '_', {'$3', '_', '_'}}),
|
||||
Matches = ets:lookup(elixir_module:defs_table(Module), {default, Name}),
|
||||
[begin
|
||||
elixir_errors:form_error(Meta, ?key(E, file), ?MODULE,
|
||||
{defs_with_defaults, Name, {Kind, Arity}, {K, A}})
|
||||
end || [K, A, D] <- Matches, A /= Arity, D /= 0, defaults_conflict(A, D, Arity, Defaults)].
|
||||
{defs_with_defaults, Kind, Name, Arity, A})
|
||||
end || {_, A, D} <- Matches, A /= Arity, D /= 0, defaults_conflict(A, D, Arity, Defaults)].
|
||||
|
||||
defaults_conflict(A, D, Arity, Defaults) ->
|
||||
((Arity >= (A - D)) andalso (Arity < A)) orelse
|
||||
@@ -324,13 +334,13 @@ format_error({bodyless_clause, Kind, {Name, Arity}}) ->
|
||||
format_error({no_module, {Kind, Name, Arity}}) ->
|
||||
io_lib:format("cannot define function outside module, invalid scope for ~ts ~ts/~B", [Kind, Name, Arity]);
|
||||
|
||||
format_error({defs_with_defaults, Name, {Kind, Arity}, {K, A}}) when Arity > A ->
|
||||
io_lib:format("~ts ~ts/~B defaults conflicts with ~ts ~ts/~B",
|
||||
[Kind, Name, Arity, K, Name, A]);
|
||||
format_error({defs_with_defaults, Kind, Name, Arity, A}) when Arity > A ->
|
||||
io_lib:format("~ts ~ts/~B defaults conflicts with ~ts/~B",
|
||||
[Kind, Name, Arity, Name, A]);
|
||||
|
||||
format_error({defs_with_defaults, Name, {Kind, Arity}, {K, A}}) when Arity < A ->
|
||||
io_lib:format("~ts ~ts/~B conflicts with defaults from ~ts ~ts/~B",
|
||||
[Kind, Name, Arity, K, Name, A]);
|
||||
format_error({defs_with_defaults, Kind, Name, Arity, A}) when Arity < A ->
|
||||
io_lib:format("~ts ~ts/~B conflicts with defaults from ~ts/~B",
|
||||
[Kind, Name, Arity, Name, A]);
|
||||
|
||||
format_error({clauses_with_defaults, {Kind, Name, Arity}}) ->
|
||||
io_lib:format(""
|
||||
|
||||
@@ -371,11 +371,31 @@ translate_with_else(Meta, [{else, Else}], S) ->
|
||||
RaiseExpr = {{'.', Generated, [erlang, error]}, Generated, [{with_clause, RaiseVar}]},
|
||||
RaiseClause = {'->', Generated, [[RaiseVar], RaiseExpr]},
|
||||
|
||||
GeneratedElse = [{'->', ?generated(ElseMeta), ElseArgs} || {'->', ElseMeta, ElseArgs} <- Else],
|
||||
GeneratedElse = [build_generated_clause(Generated, ElseClause) || ElseClause <- Else],
|
||||
|
||||
Case = {'case', [{export_vars, false} | Generated], [ElseVarEx, [{do, GeneratedElse ++ [RaiseClause]}]]},
|
||||
{TranslatedCase, SC} = elixir_erl_pass:translate(Case, SV),
|
||||
{{clause, ?ann(Generated), [ElseVarErl], [], [TranslatedCase]}, SC}.
|
||||
|
||||
build_generated_clause(Generated, {'->', _, [Args, Clause]}) ->
|
||||
NewArgs = [build_generated_clause_arg(Generated, Arg) || Arg <- Args],
|
||||
{'->', Generated, [NewArgs, Clause]}.
|
||||
|
||||
build_generated_clause_arg(Generated, Arg) ->
|
||||
{Expr, Guards} = elixir_utils:extract_guards(Arg),
|
||||
NewGuards = [build_generated_guard(Generated, Guard) || Guard <- Guards],
|
||||
concat_guards(Generated, Expr, NewGuards).
|
||||
|
||||
build_generated_guard(Generated, {{'.', _, _} = Call, _, Args}) ->
|
||||
{Call, Generated, [build_generated_guard(Generated, Arg) || Arg <- Args]};
|
||||
build_generated_guard(_, Expr) ->
|
||||
Expr.
|
||||
|
||||
concat_guards(_Meta, Expr, []) ->
|
||||
Expr;
|
||||
concat_guards(Meta, Expr, [Guard | Tail]) ->
|
||||
{'when', Meta, [Expr, concat_guards(Meta, Guard, Tail)]}.
|
||||
|
||||
translate_with_do([{'<-', Meta, [Left, Expr]} | Rest], Do, Else, S) ->
|
||||
{Args, Guards} = elixir_utils:extract_guards(Left),
|
||||
{TExpr, SR} = elixir_erl_pass:translate(Expr, S),
|
||||
|
||||
@@ -318,7 +318,7 @@ beam_location(#{lexical_tracker := Pid, module := Module}) ->
|
||||
%% Handle unused attributes warnings and special cases.
|
||||
|
||||
warn_unused_attributes(File, Data, PersistedAttrs) ->
|
||||
ReservedAttrs = [after_compile, before_compile, moduledoc, on_definition | PersistedAttrs],
|
||||
ReservedAttrs = [after_compile, before_compile, deprecated, moduledoc, on_definition, since | PersistedAttrs],
|
||||
Keys = ets:select(Data, [{{'$1', '_', '_', '$2'}, [{is_atom, '$1'}, {is_integer, '$2'}], [['$1', '$2']]}]),
|
||||
[elixir_errors:form_warn([{line, Line}], File, ?MODULE, {unused_attribute, Key}) ||
|
||||
[Key, Line] <- Keys, not lists:member(Key, ReservedAttrs)].
|
||||
|
||||
@@ -378,9 +378,9 @@ capture_op_eol -> capture_op : '$1'.
|
||||
capture_op_eol -> capture_op eol : '$1'.
|
||||
|
||||
unary_op_eol -> unary_op : '$1'.
|
||||
unary_op_eol -> unary_op eol : '$1'.
|
||||
unary_op_eol -> unary_op eol : warn_unary_operator_eol('$1'), '$1'.
|
||||
unary_op_eol -> dual_op : '$1'.
|
||||
unary_op_eol -> dual_op eol : '$1'.
|
||||
unary_op_eol -> dual_op eol : warn_unary_operator_eol('$1'), '$1'.
|
||||
|
||||
match_op_eol -> match_op : '$1'.
|
||||
match_op_eol -> match_op eol : '$1'.
|
||||
@@ -857,6 +857,25 @@ warn_empty_stab_clause({stab_op, {Line, _Begin, _End}, '->'}) ->
|
||||
"an expression is always required on the right side of ->. "
|
||||
"Please provide a value after ->").
|
||||
|
||||
warn_unary_operator_eol({dual_op, {Line, _Begin, _End}, Op}) ->
|
||||
elixir_errors:warn(Line, ?file(),
|
||||
io_lib:format(
|
||||
"unary operator ~ts followed by new line. "
|
||||
"This may happen when you try to use a binary operator over multiple lines. "
|
||||
"Please make sure that the operator and all arguments are on the same line",
|
||||
[Op]
|
||||
)
|
||||
);
|
||||
warn_unary_operator_eol({unary_op, {Line, _Begin, _End}, Op}) ->
|
||||
elixir_errors:warn(Line, ?file(),
|
||||
io_lib:format(
|
||||
"unary operator ~ts followed by new line. "
|
||||
"Please make sure the unary operator and the expression that follows "
|
||||
"the operator are on the same line",
|
||||
[Op]
|
||||
)
|
||||
).
|
||||
|
||||
warn_pipe({arrow_op, {Line, _Begin, _End}, Op}, {_, [_ | _], [_ | _]}) ->
|
||||
elixir_errors:warn(Line, ?file(),
|
||||
io_lib:format(
|
||||
|
||||
@@ -142,7 +142,7 @@ tokenize(String, Line, Column, Opts) ->
|
||||
existing_atoms_only=ExistingAtomsOnly,
|
||||
check_terminators=CheckTerminators,
|
||||
preserve_comments=PreserveComments,
|
||||
identifier_tokenizer=elixir_config:get(identifier_tokenizer)
|
||||
identifier_tokenizer=elixir_config:safe_get(identifier_tokenizer, 'Elixir.String.Tokenizer')
|
||||
}).
|
||||
|
||||
tokenize(String, Line, Opts) ->
|
||||
@@ -670,7 +670,7 @@ eol(Line, Column, Tokens) -> [{eol, {Line, Column, Column + 1}} | Tokens].
|
||||
unsafe_to_atom(Part, Line, #elixir_tokenizer{}) when
|
||||
is_binary(Part) andalso size(Part) > 255;
|
||||
is_list(Part) andalso length(Part) > 255 ->
|
||||
{error, {Line, "atom length must be less than system limit", ":"}};
|
||||
{error, {Line, "atom length must be less than system limit: ", [$: | Part]}};
|
||||
unsafe_to_atom(Binary, _Line, #elixir_tokenizer{existing_atoms_only=true}) when is_binary(Binary) ->
|
||||
{ok, binary_to_existing_atom(Binary, utf8)};
|
||||
unsafe_to_atom(Binary, _Line, #elixir_tokenizer{}) when is_binary(Binary) ->
|
||||
|
||||
@@ -96,21 +96,17 @@ relative_to_cwd(Path) ->
|
||||
characters_to_list(Data) when is_list(Data) ->
|
||||
Data;
|
||||
characters_to_list(Data) ->
|
||||
try elixir_config:get(bootstrap) of
|
||||
case elixir_config:safe_get(bootstrap, true) of
|
||||
true -> unicode:characters_to_list(Data);
|
||||
false -> 'Elixir.String':to_charlist(Data)
|
||||
catch
|
||||
_:_ -> unicode:characters_to_list(Data)
|
||||
end.
|
||||
|
||||
characters_to_binary(Data) when is_binary(Data) ->
|
||||
Data;
|
||||
characters_to_binary(Data) ->
|
||||
try elixir_config:get(bootstrap) of
|
||||
true -> unicode:characters_to_binary(Data);
|
||||
case elixir_config:safe_get(bootstrap, true) of
|
||||
true -> unicode:characters_to_binary(Data);
|
||||
false -> 'Elixir.List':to_string(Data)
|
||||
catch
|
||||
_:_ -> unicode:characters_to_binary(Data)
|
||||
end.
|
||||
|
||||
%% Returns the caller as a stacktrace entry.
|
||||
|
||||
@@ -66,6 +66,15 @@ defmodule DateTest do
|
||||
{:ok, Calendar.Holocene.date(12000, 01, 01)}
|
||||
end
|
||||
|
||||
test "add/2" do
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
Date.add(~D[0000-01-01], 3652425)
|
||||
end
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
Date.add(~D[0000-01-01], -1)
|
||||
end
|
||||
end
|
||||
|
||||
test "diff/2" do
|
||||
assert Date.diff(~D[2000-01-31], ~D[2000-01-01]) == 30
|
||||
assert Date.diff(~D[2000-01-01], ~D[2000-01-31]) == -30
|
||||
@@ -221,13 +230,20 @@ defmodule DateTimeTest do
|
||||
assert DateTime.from_unix(-62167219201) == {:error, :invalid_unix_time}
|
||||
|
||||
max_datetime = %DateTime{
|
||||
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {0, 0},
|
||||
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {999999, 6},
|
||||
minute: 59, month: 12, second: 59, std_offset: 0, time_zone: "Etc/UTC",
|
||||
utc_offset: 0, year: 9999, zone_abbr: "UTC"
|
||||
}
|
||||
|
||||
assert DateTime.from_unix(253402300799) == {:ok, max_datetime}
|
||||
assert DateTime.from_unix(253402300799999999, :microsecond) == {:ok, max_datetime}
|
||||
assert DateTime.from_unix(253402300800) == {:error, :invalid_unix_time}
|
||||
|
||||
minus_datetime = %DateTime{
|
||||
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {999999, 6},
|
||||
minute: 59, month: 12, second: 59, std_offset: 0, time_zone: "Etc/UTC",
|
||||
utc_offset: 0, year: 1969, zone_abbr: "UTC"
|
||||
}
|
||||
assert DateTime.from_unix(-1, :microsecond) == {:ok, minus_datetime}
|
||||
end
|
||||
|
||||
test "from_unix!/2" do
|
||||
|
||||
@@ -70,6 +70,11 @@ defmodule EnumTest do
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 2, []) == [[1, 2, 3], [3, 4, 5], [5, 6]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 3, []) == [[1, 2, 3], [4, 5, 6]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5], 4, 4, 6..10) == [[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, []) == [[1, 2], [4, 5]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 2, 3, []) == [[1, 2], [4, 5]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 3, []) == [[1, 2], [4, 5], [7]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 3, [8]) == [[1, 2], [4, 5], [7, 8]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 4, []) == [[1, 2], [5, 6]]
|
||||
end
|
||||
|
||||
test "chunk_by/2" do
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
defmodule Dialyzer.With do
|
||||
def with_else do
|
||||
with :ok <- ok_or_error(),
|
||||
:ok <- ok_or_other_error() do
|
||||
:ok <- ok_or_other_error(),
|
||||
:ok <- ok_or_tuple_error(),
|
||||
:ok <- ok_or_tuple_list_error() do
|
||||
:ok
|
||||
else
|
||||
:error ->
|
||||
:error
|
||||
|
||||
:other_error ->
|
||||
:other_error
|
||||
|
||||
{:error, msg} when is_list(msg) or is_tuple(msg) ->
|
||||
:error
|
||||
|
||||
{:error, msg} when is_list(msg) when is_tuple(msg) ->
|
||||
:error
|
||||
|
||||
{:error, _msg} ->
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,4 +32,14 @@ defmodule Dialyzer.With do
|
||||
defp ok_or_other_error do
|
||||
Enum.random([:ok, :other_error])
|
||||
end
|
||||
|
||||
@spec ok_or_tuple_error() :: :ok | {:error, :err}
|
||||
defp ok_or_tuple_error do
|
||||
Enum.random([:ok, {:error, :err}])
|
||||
end
|
||||
|
||||
@spec ok_or_tuple_list_error() :: :ok | {:error, [:err]}
|
||||
defp ok_or_tuple_list_error do
|
||||
Enum.random([:ok, {:error, [:err]}])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -238,7 +238,7 @@ defmodule Kernel.ErrorsTest do
|
||||
|
||||
test "different defs with defaults" do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/3 defaults conflicts with def hello/2",
|
||||
"nofile:3: def hello/3 defaults conflicts with hello/2",
|
||||
~C'''
|
||||
defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults1 do
|
||||
def hello(a, b \\ nil), do: a + b
|
||||
@@ -247,7 +247,7 @@ defmodule Kernel.ErrorsTest do
|
||||
'''
|
||||
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/2 conflicts with defaults from def hello/3",
|
||||
"nofile:3: def hello/2 conflicts with defaults from hello/3",
|
||||
~C'''
|
||||
defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults2 do
|
||||
def hello(a, b \\ nil, c \\ nil), do: a + b + c
|
||||
|
||||
@@ -1180,6 +1180,14 @@ defmodule Kernel.ExpansionTest do
|
||||
assert_raise CompileError, ~r"unhandled operator ->", fn ->
|
||||
expand(quote do: (foo -> bar))
|
||||
end
|
||||
|
||||
assert_raise CompileError,
|
||||
~r"size in bitstring expects an integer or a variable as argument, got: foo\(\)",
|
||||
fn ->
|
||||
expand(quote do
|
||||
fn <<_::size(foo)>> -> :ok end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
@@ -17,7 +17,11 @@ defmodule Kernel.ImplTest do
|
||||
end
|
||||
|
||||
defmodule Behaviour do
|
||||
@callback foo :: any
|
||||
@callback foo() :: any
|
||||
end
|
||||
|
||||
defmodule BehaviourWithArgument do
|
||||
@callback foo(any) :: any
|
||||
end
|
||||
|
||||
defmodule MacroBehaviour do
|
||||
@@ -285,6 +289,20 @@ defmodule Kernel.ImplTest do
|
||||
end) =~ "got @impl Kernel.ImplTest.MacroBehaviour for def bar/0 but the given behaviour was not declared with @behaviour"
|
||||
end
|
||||
|
||||
test "does not warn for @impl when using default arguments" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string ~S"""
|
||||
defmodule Kernel.ImplTest.ImplAttributes do
|
||||
@behaviour Kernel.ImplTest.Behaviour
|
||||
@behaviour Kernel.ImplTest.BehaviourWithArgument
|
||||
|
||||
@impl true
|
||||
def foo(args \\ []), do: args
|
||||
end
|
||||
"""
|
||||
end) == ""
|
||||
end
|
||||
|
||||
test "does not warn for no @impl when overriding callback" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
|
||||
@@ -84,6 +84,25 @@ defmodule Kernel.WarningTest do
|
||||
end) =~ message
|
||||
end
|
||||
|
||||
test "unary operator followed by new line" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
1
|
||||
+
|
||||
1
|
||||
"""
|
||||
end) =~ "unary operator + followed by new line"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
!
|
||||
1
|
||||
"""
|
||||
end) =~ "unary operator ! followed by new line"
|
||||
after
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test "useless attr" do
|
||||
message = capture_err(fn ->
|
||||
Code.eval_string """
|
||||
@@ -468,6 +487,27 @@ defmodule Kernel.WarningTest do
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test "generated clause not match" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string("""
|
||||
defmodule Sample do
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
def hello, do: nil
|
||||
def hello, do: nil
|
||||
end
|
||||
end
|
||||
end
|
||||
defmodule UseSample do
|
||||
use Sample
|
||||
end
|
||||
""")
|
||||
end) =~ "this clause cannot match because a previous clause at line 10 always matches"
|
||||
after
|
||||
purge(Sample)
|
||||
purge(UseSample)
|
||||
end
|
||||
|
||||
test "clause with defaults should be first" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string ~S"""
|
||||
|
||||
@@ -108,4 +108,38 @@ defmodule MapSetTest do
|
||||
list = MapSet.to_list(MapSet.new(5..120))
|
||||
assert Enum.sort(list) == Enum.to_list(5..120)
|
||||
end
|
||||
|
||||
test "MapSet v1 compatibility" do
|
||||
result = 1..5 |> map_set_v1() |> MapSet.new()
|
||||
assert MapSet.equal?(result, MapSet.new(1..5))
|
||||
|
||||
result = MapSet.put(map_set_v1(1..5), 6)
|
||||
assert MapSet.equal?(result, MapSet.new(1..6))
|
||||
|
||||
result = MapSet.union(map_set_v1(1..5), MapSet.new(6..10))
|
||||
assert MapSet.equal?(result, MapSet.new(1..10))
|
||||
|
||||
result = MapSet.intersection(map_set_v1(1..10), MapSet.new(6..15))
|
||||
assert MapSet.equal?(result, MapSet.new(6..10))
|
||||
|
||||
result = MapSet.difference(map_set_v1(1..10), MapSet.new(6..50))
|
||||
assert MapSet.equal?(result, MapSet.new(1..5))
|
||||
|
||||
result = MapSet.delete(map_set_v1(1..10), 1)
|
||||
assert MapSet.equal?(result, MapSet.new(2..10))
|
||||
|
||||
assert MapSet.size(map_set_v1(1..5)) == 5
|
||||
assert MapSet.to_list(map_set_v1(1..5)) == Enum.to_list(1..5)
|
||||
|
||||
assert MapSet.disjoint?(map_set_v1(1..5), MapSet.new(10..15))
|
||||
refute MapSet.disjoint?(map_set_v1(1..5), MapSet.new(5..10))
|
||||
|
||||
assert MapSet.subset?(map_set_v1(3..7), MapSet.new(1..10))
|
||||
refute MapSet.subset?(map_set_v1(7..12), MapSet.new(1..10))
|
||||
end
|
||||
|
||||
defp map_set_v1(enumerable) do
|
||||
map = Map.new(enumerable, &{&1, true})
|
||||
%{__struct__: MapSet, map: map}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -521,6 +521,11 @@ defmodule RegistryTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "child_spec/1 uses :name as :id" do
|
||||
assert %{id: :custom_name} = Registry.child_spec([name: :custom_name])
|
||||
assert %{id: Registry} = Registry.child_spec([])
|
||||
end
|
||||
|
||||
defp register_task(registry, key, value) do
|
||||
parent = self()
|
||||
{:ok, task} =
|
||||
|
||||
@@ -80,7 +80,11 @@ defmodule SupervisorTest do
|
||||
restart: :permanent, shutdown: :infinity) ==
|
||||
%{id: :foo, start: {:foo, :bar, []}, restart: :permanent, shutdown: :infinity}
|
||||
|
||||
assert_raise ArgumentError, ~r"The module Unknown was given as a child", fn ->
|
||||
assert_raise ArgumentError, ~r"The module SupervisorTest was given as a child.*\nbut it does not implement"m, fn ->
|
||||
Supervisor.child_spec(SupervisorTest, [])
|
||||
end
|
||||
|
||||
assert_raise ArgumentError, ~r"The module Unknown was given as a child.*but it does not exist"m, fn ->
|
||||
Supervisor.child_spec(Unknown, [])
|
||||
end
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ quoted_atom_test() ->
|
||||
|
||||
oversized_atom_test() ->
|
||||
OversizedAtom = [$: | string:copies("a", 256)],
|
||||
{1, "atom length must be less than system limit", ":"} = tokenize_error(OversizedAtom).
|
||||
{1, "atom length must be less than system limit: ", OversizedAtom} = tokenize_error(OversizedAtom).
|
||||
|
||||
op_atom_test() ->
|
||||
[{atom, {1, 1, 6}, f0_1}] = tokenize(":f0_1").
|
||||
|
||||
@@ -109,7 +109,7 @@ 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
|
||||
suppress_warning(quote do
|
||||
case right do
|
||||
x when x in [nil, false] ->
|
||||
raise ExUnit.AssertionError,
|
||||
@@ -121,7 +121,7 @@ defmodule ExUnit.Assertions do
|
||||
end)
|
||||
|
||||
match_expr =
|
||||
no_warning(quote do
|
||||
suppress_warning(quote do
|
||||
case right do
|
||||
unquote(left) ->
|
||||
unquote(return)
|
||||
@@ -401,6 +401,40 @@ defmodule ExUnit.Assertions do
|
||||
quote(do: unquote(left) = received)
|
||||
end
|
||||
|
||||
pattern_finder =
|
||||
quote do
|
||||
fn message ->
|
||||
unquote(suppress_warning(quote do
|
||||
case message do
|
||||
unquote(pattern) ->
|
||||
_ = unquote(vars)
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end))
|
||||
end
|
||||
end
|
||||
|
||||
failure_message_hit =
|
||||
failure_message || quote do
|
||||
"""
|
||||
Found message matching #{unquote(binary)} after #{timeout}ms.
|
||||
|
||||
This means the message was delivered too close to the timeout value, you may want to either:
|
||||
|
||||
1. Give an increased timeout to `assert_receive/2`
|
||||
2. Increase the default timeout to all `assert_receive` in your
|
||||
test_helper.exs by setting ExUnit.configure(assert_receive_timeout: ...)
|
||||
"""
|
||||
end
|
||||
|
||||
failure_message_miss =
|
||||
failure_message || quote do
|
||||
"No message matching #{unquote(binary)} after #{timeout}ms."
|
||||
end
|
||||
|
||||
quote do
|
||||
timeout = unquote(timeout)
|
||||
|
||||
@@ -412,29 +446,10 @@ defmodule ExUnit.Assertions do
|
||||
timeout ->
|
||||
{:messages, messages} = Process.info(self(), :messages)
|
||||
|
||||
pattern_finder = fn message ->
|
||||
case message do
|
||||
unquote(pattern) ->
|
||||
_ = unquote(vars)
|
||||
true
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
if Enum.any?(messages, pattern_finder) do
|
||||
flunk(unquote(failure_message) || """
|
||||
Found message matching #{unquote(binary)} after #{timeout}ms.
|
||||
|
||||
This means the message was delivered too close to the timeout value, you may want to either:
|
||||
|
||||
1. Give an increased timeout to `assert_receive/2`
|
||||
2. Increase the default timeout to all `assert_receive` in your
|
||||
test_helper.exs by setting ExUnit.configure(assert_receive_timeout: ...)
|
||||
""")
|
||||
if Enum.any?(messages, unquote(pattern_finder)) do
|
||||
flunk(unquote(failure_message_hit))
|
||||
else
|
||||
failure_message = unquote(failure_message) || "No message matching #{unquote(binary)} after #{timeout}ms."
|
||||
flunk(failure_message <>
|
||||
flunk(unquote(failure_message_miss) <>
|
||||
ExUnit.Assertions.__pins__(unquote(pins)) <>
|
||||
ExUnit.Assertions.__mailbox__(messages))
|
||||
end
|
||||
@@ -515,7 +530,7 @@ defmodule ExUnit.Assertions do
|
||||
|> elem(1)
|
||||
end
|
||||
|
||||
defp no_warning({name, meta, [expr, [do: clauses]]}) do
|
||||
defp suppress_warning({name, meta, [expr, [do: clauses]]}) do
|
||||
clauses = Enum.map clauses, fn {:->, meta, args} ->
|
||||
{:->, [generated: true] ++ meta, args}
|
||||
end
|
||||
|
||||
@@ -179,6 +179,12 @@ defmodule ExUnit.AssertionsTest do
|
||||
:hello = assert_receive :hello
|
||||
end
|
||||
|
||||
test "assert receive accepts custom failure message" do
|
||||
send self(), :hello
|
||||
assert_receive message, 0, "failure message"
|
||||
:hello = message
|
||||
end
|
||||
|
||||
test "assert receive with message in mailbox after timeout, but before reading mailbox tells user to increase timeout" do
|
||||
parent = self()
|
||||
# This is testing a race condition, so it's not
|
||||
@@ -297,7 +303,7 @@ defmodule ExUnit.AssertionsTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "assert received leaks" do
|
||||
test "assert received binds variables" do
|
||||
send self(), {:hello, :world}
|
||||
assert_received {:hello, world}
|
||||
:world = world
|
||||
|
||||
@@ -14,6 +14,9 @@ defmodule IEx.Evaluator do
|
||||
old_leader = Process.group_leader
|
||||
Process.group_leader(self(), leader)
|
||||
|
||||
evaluator? = !!Process.get(:iex_evaluator)
|
||||
Process.put(:iex_evaluator, true)
|
||||
|
||||
state = loop_state(server, IEx.History.init, opts)
|
||||
command == :ack && :proc_lib.init_ack(self())
|
||||
|
||||
@@ -21,6 +24,15 @@ defmodule IEx.Evaluator do
|
||||
loop(state)
|
||||
after
|
||||
Process.group_leader(self(), old_leader)
|
||||
|
||||
# If there was an evaluator, nest failures.
|
||||
if evaluator? do
|
||||
send self(), {:done, server}
|
||||
else
|
||||
Process.delete(:iex_evaluator)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -114,7 +114,8 @@ defmodule IEx.Introspection do
|
||||
defp open_mfa(module, fun, arity) do
|
||||
with {:module, _} <- Code.ensure_loaded(module),
|
||||
source when is_list(source) <- module.module_info(:compile)[:source] do
|
||||
open_abstract_code(module, fun, arity, List.to_string(source))
|
||||
source = rewrite_elixir_source(module, source)
|
||||
open_abstract_code(module, fun, arity, source)
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
@@ -158,6 +159,21 @@ defmodule IEx.Introspection do
|
||||
end
|
||||
end
|
||||
|
||||
defp rewrite_elixir_source(module, source) do
|
||||
case :application.get_application(module) do
|
||||
{:ok, app} when app in [:eex, :elixir, :ex_unit, :iex, :logger, :mix] ->
|
||||
{in_app, [lib_or_src | _]} =
|
||||
source
|
||||
|> Path.split()
|
||||
|> Enum.reverse()
|
||||
|> Enum.split_while(& &1 not in ["lib", "src"])
|
||||
|
||||
Application.app_dir(app, Path.join([lib_or_src | Enum.reverse(in_app)]))
|
||||
_ ->
|
||||
List.to_string(source)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Prints documentation.
|
||||
"""
|
||||
|
||||
@@ -123,8 +123,8 @@ defmodule IEx.HelpersTest do
|
||||
end
|
||||
|
||||
describe "open" do
|
||||
@iex_helpers Path.expand("../../lib/iex/helpers.ex", __DIR__)
|
||||
@elixir_erl Path.expand("../../../elixir/src/elixir.erl", __DIR__)
|
||||
@iex_helpers "iex/lib/iex/helpers.ex"
|
||||
@elixir_erl "elixir/src/elixir.erl"
|
||||
@editor System.get_env("ELIXIR_EDITOR")
|
||||
|
||||
test "opens __FILE__ and __LINE__" do
|
||||
@@ -137,48 +137,48 @@ defmodule IEx.HelpersTest do
|
||||
|
||||
test "opens Elixir module" do
|
||||
assert capture_iex("open(IEx.Helpers)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:1/
|
||||
~r/#{@iex_helpers}:1$/
|
||||
end
|
||||
|
||||
test "opens function" do
|
||||
assert capture_iex("open(h)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens function/arity" do
|
||||
assert capture_iex("open(b/1)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
assert capture_iex("open(h/0)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens module.function" do
|
||||
assert capture_iex("open(IEx.Helpers.b)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
assert capture_iex("open(IEx.Helpers.h)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens module.function/arity" do
|
||||
assert capture_iex("open(IEx.Helpers.b/1)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
assert capture_iex("open(IEx.Helpers.h/0)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens Erlang module" do
|
||||
assert capture_iex("open(:elixir)") |> maybe_trim_quotes() =~
|
||||
~r/#{@elixir_erl}:\d+/
|
||||
~r/#{@elixir_erl}:\d+$/
|
||||
end
|
||||
|
||||
test "opens Erlang module.function" do
|
||||
assert capture_iex("open(:elixir.start)") |> maybe_trim_quotes() =~
|
||||
~r/#{@elixir_erl}:\d+/
|
||||
~r/#{@elixir_erl}:\d+$/
|
||||
end
|
||||
|
||||
test "opens Erlang module.function/arity" do
|
||||
assert capture_iex("open(:elixir.start/2)") |> maybe_trim_quotes() =~
|
||||
~r/#{@elixir_erl}:\d+/
|
||||
~r/#{@elixir_erl}:\d+$/
|
||||
end
|
||||
|
||||
test "errors if module is not available" do
|
||||
|
||||
@@ -33,7 +33,7 @@ defmodule Mix.Compilers.Test do
|
||||
|
||||
case test_files_to_run do
|
||||
[] when stale ->
|
||||
Mix.shell.info "No stale tests."
|
||||
Mix.shell.info "No stale tests"
|
||||
:noop
|
||||
|
||||
[] when test_patterns == [] ->
|
||||
@@ -45,8 +45,9 @@ defmodule Mix.Compilers.Test do
|
||||
:noop
|
||||
|
||||
test_files ->
|
||||
task = Task.async(ExUnit, :run, [])
|
||||
|
||||
try do
|
||||
task = Task.async(ExUnit, :run, [])
|
||||
Kernel.ParallelRequire.files(test_files, parallel_require_callbacks)
|
||||
ExUnit.Server.cases_loaded()
|
||||
%{failures: failures} = results = Task.await(task, :infinity)
|
||||
@@ -56,6 +57,13 @@ defmodule Mix.Compilers.Test do
|
||||
end
|
||||
|
||||
{:ok, results}
|
||||
catch
|
||||
kind, reason ->
|
||||
stack = System.stacktrace
|
||||
# In case there is an error, shutdown the runner task
|
||||
# before the error propagates up and trigger links.
|
||||
Task.shutdown(task)
|
||||
:erlang.raise(kind, reason, stack)
|
||||
after
|
||||
agent_stop(stale_manifest_pid)
|
||||
end
|
||||
|
||||
@@ -214,10 +214,12 @@ defmodule Mix.SCM.Git do
|
||||
end
|
||||
end
|
||||
|
||||
@rev_command 'git --git-dir=.git config remote.origin.url && git --git-dir=.git rev-parse --verify --quiet HEAD'
|
||||
|
||||
defp get_rev_info do
|
||||
destructure [origin, rev],
|
||||
:os.cmd('git --git-dir=.git config remote.origin.url && git --git-dir=.git rev-parse --verify --quiet HEAD')
|
||||
|> IO.iodata_to_binary
|
||||
:os.cmd(@rev_command)
|
||||
|> List.to_string()
|
||||
|> String.split("\n", trim: true)
|
||||
%{origin: origin, rev: rev}
|
||||
end
|
||||
@@ -257,7 +259,7 @@ defmodule Mix.SCM.Git do
|
||||
:error ->
|
||||
version =
|
||||
:os.cmd('git --version')
|
||||
|> IO.iodata_to_binary
|
||||
|> List.to_string()
|
||||
|> parse_version
|
||||
|
||||
Mix.State.put(:git_version, version)
|
||||
|
||||
@@ -32,13 +32,19 @@ defmodule Mix.Tasks.Local.Hex do
|
||||
@spec run(OptionParser.argv) :: boolean
|
||||
def run(argv) do
|
||||
{opts, _} = OptionParser.parse!(argv, switches: @switches)
|
||||
force? = Keyword.get(opts, :force, false)
|
||||
if_missing? = Keyword.get(opts, :if_missing, false)
|
||||
|
||||
should_install? =
|
||||
case {force?, if_missing?} do
|
||||
{false, true} -> Code.ensure_loaded?(Hex)
|
||||
_ -> true
|
||||
if Keyword.get(opts, :if_missing, false) do
|
||||
not Code.ensure_loaded?(Hex)
|
||||
else
|
||||
true
|
||||
end
|
||||
|
||||
argv =
|
||||
if Keyword.get(opts, :force, false) do
|
||||
["--force"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
should_install? && run_install(argv)
|
||||
|
||||
@@ -99,7 +99,7 @@ defmodule Mix.Tasks.Profile.Cprof do
|
||||
{opts, head} = OptionParser.parse_head!(args,
|
||||
aliases: [r: :require, p: :parallel, e: :eval, c: :config],
|
||||
strict: @switches)
|
||||
Mix.Tasks.Run.run(["--no-mixexs" | args], opts, head,
|
||||
Mix.Tasks.Run.run(["--no-mix-exs" | args], opts, head,
|
||||
&profile_code(&1, opts),
|
||||
&profile_code(File.read!(&1), opts))
|
||||
end
|
||||
|
||||
@@ -116,7 +116,7 @@ defmodule Mix.Tasks.Profile.Fprof do
|
||||
{opts, head} = OptionParser.parse_head!(args,
|
||||
aliases: [r: :require, p: :parallel, e: :eval, c: :config],
|
||||
strict: @switches)
|
||||
Mix.Tasks.Run.run(["--no-mixexs" | args], opts, head,
|
||||
Mix.Tasks.Run.run(["--no-mix-exs" | args], opts, head,
|
||||
&profile_code(&1, opts),
|
||||
&profile_code(File.read!(&1), opts))
|
||||
end
|
||||
|
||||
@@ -38,7 +38,7 @@ defmodule Mix.Tasks.Run do
|
||||
* `--no-deps-check` - does not check dependencies
|
||||
* `--no-archives-check` - does not check archives
|
||||
* `--no-halt` - does not halt the system after running the command
|
||||
* `--no-mixexs` - allows the command to run even if there is no mix.exs
|
||||
* `--no-mix-exs` - allows the command to run even if there is no mix.exs
|
||||
* `--no-start` - does not start applications after compilation
|
||||
* `--no-elixir-version-check` - does not check the Elixir version from mix.exs
|
||||
|
||||
@@ -48,7 +48,7 @@ defmodule Mix.Tasks.Run do
|
||||
def run(args) do
|
||||
{opts, head} = OptionParser.parse_head!(args,
|
||||
aliases: [r: :require, p: :parallel, e: :eval, c: :config],
|
||||
strict: [parallel: :boolean, require: :keep, eval: :keep, config: :keep, mixexs: :boolean,
|
||||
strict: [parallel: :boolean, require: :keep, eval: :keep, config: :keep, mix_exs: :boolean,
|
||||
halt: :boolean, compile: :boolean, deps_check: :boolean, start: :boolean,
|
||||
archives_check: :boolean, elixir_version_check: :boolean, parallel_require: :keep])
|
||||
|
||||
@@ -87,10 +87,12 @@ defmodule Mix.Tasks.Run do
|
||||
cond do
|
||||
Mix.Project.get ->
|
||||
Mix.Task.run "app.start", args
|
||||
"--no-mixexs" in args ->
|
||||
"--no-mix-exs" in args ->
|
||||
:ok
|
||||
true ->
|
||||
Mix.Project.get!
|
||||
Mix.raise "Cannot execute \"mix run\" without a Mix.Project, " <>
|
||||
"please ensure you are running Mix in a directory with a mix.exs file " <>
|
||||
"or pass the --no-mix-exs flag"
|
||||
end
|
||||
|
||||
process_load(opts, expr_evaluator)
|
||||
|
||||
@@ -40,7 +40,7 @@ defmodule Mix.Tasks.TestTest do
|
||||
assert_stale_run_output "2 tests, 0 failures"
|
||||
|
||||
assert_stale_run_output """
|
||||
No stale tests.
|
||||
No stale tests
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -124,7 +124,7 @@ defmodule Mix.Tasks.TestTest do
|
||||
|
||||
Port.command(port, "\n")
|
||||
|
||||
assert receive_until_match(port, "No stale tests.", []) =~ "Restarting..."
|
||||
assert receive_until_match(port, "No stale tests", []) =~ "Restarting..."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{application, elixir,
|
||||
[{description, "elixir"},
|
||||
{vsn, "1.5.1"},
|
||||
{vsn, "1.5.3"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
Reference in New Issue
Block a user