Compare commits

...
14 Commits
Author SHA1 Message Date
José Valim 5591ab2dff Update CHANGELOG.md 2016-10-29 10:41:14 +02:00
José Valim 59910fff71 Make prune part of the public API in the formatter
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-05-13 09:24:59 +02:00
José Valim f179a23675 Ensure poorly formatted chardata is pruned in Logger watcher
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-05-13 09:24:54 +02:00
José Valim bfa16ebf35 Support canonical URLs
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-02-21 21:36:27 +01:00
James Fish 4906beeb2d Fix remsh'ing to node with different beam files
Signed-off-by: James Fish <james@fishcakez.com>
2015-11-07 14:42:44 +00:00
José Valim 7ab6d2e982 Use String.to_char_list when converting shell commands
We have found this bug as rebar failed to compile projects
with non-latin characters on the path. Using String.to_char_list
performs the proper conversion, passing the proper arguments
to rebar.

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-10-30 11:31:29 -02:00
Eric Meadows-Jönsson ac502ebee2 inspect invalid ANSI sequences
Signed-off-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
2015-10-05 17:36:26 -05:00
Luper Rouch 4fd0c4e013 Fix default value of :time option in File.Stat docs
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-10-01 16:42:35 +02:00
José Valim 05fecbcbcf Merge pull request #3811 from michalmuskala/optimize_map
Optimize Enum.map/2 for lists

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-10-01 16:41:58 +02:00
José Valim 448f473d08 Merge pull request #3810 from lexmag/optimize-keyword-new
Optimize `Keyword.new/2` function

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-10-01 16:39:06 +02:00
José Valim eb8d293ac5 Do not delete only information from deps
If we delete this information, we lose the environments the
dependency has been originally restricted to, raising improper
error messages.

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-09-29 17:28:48 +02:00
José Valim 59588ca424 Ensure we print unicode codepoints on errors, closes #3804
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-09-29 15:03:36 +02:00
José Valim 64ffd185cc Ensure we keep ordering and last properties in keyword
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-09-29 13:28:02 +02:00
José Valim 40beaf4f07 Add a test for umbrellas with build embedded
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2015-09-28 23:53:07 +02:00
21 changed files with 123 additions and 48 deletions
+4
View File
@@ -207,3 +207,7 @@ and is using Erlang 17.1, remember to update to at least Erlang 17.3.
* [Kernel] `?\xHEX` is deprecated in favor of `0xHEX`. There is no situation where the former should be used in favor of the latter and the latter is always cleaner
* [Kernel] Giving `as: true | false` to `alias/2` and `require/2` have been deprecated (it was undocumented behaviour)
* [String] Passing an empty string to `starts_with?`, `contains?` and `ends_with?` had dubious behaviour and have been deprecated to help developers identify possible bugs in their source code
## v1.0
The CHANGELOG for v1.0 releases can be found [in the v1.0 branch](https://github.com/elixir-lang/elixir/blob/v1.0/CHANGELOG.md).
+2 -1
View File
@@ -1,6 +1,7 @@
REBAR ?= "$(CURDIR)/rebar"
PREFIX ?= /usr/local
DOCS := v1.1
CANONICAL := stable
ELIXIRC := bin/elixirc --verbose --ignore-module-conflict
ERLC := erlc -I lib/elixir/include
ERL := erl -I lib/elixir/include -noshell -pa lib/elixir/ebin
@@ -130,7 +131,7 @@ clean_exbeam:
LOGO_PATH = $(shell test -f ../docs/logo.png && echo "--logo ../docs/logo.png")
SOURCE_REF = $(shell head="$$(git rev-parse HEAD)" tag="$$(git tag --points-at $$head | tail -1)" ; echo "$${tag:-$$head}\c")
COMPILE_DOCS = bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" -m "$(3)" -u "https://github.com/elixir-lang/elixir" --source-ref "$(call SOURCE_REF)" $(call LOGO_PATH) -o doc/$(2) -p http://elixir-lang.org/docs.html
COMPILE_DOCS = bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" -m "$(3)" -u "https://github.com/elixir-lang/elixir" --source-ref "v$(VERSION)" $(call LOGO_PATH) -o doc/$(2) -a http://elixir-lang.org/docs/$(CANONICAL)/$(2)/ -p http://elixir-lang.org/docs.html
docs: compile ../ex_doc/bin/ex_doc docs_elixir docs_eex docs_mix docs_iex docs_ex_unit docs_logger
+8 -2
View File
@@ -1039,8 +1039,14 @@ defmodule Enum do
"""
@spec map(t, (element -> any)) :: list
def map(collection, fun) when is_list(collection) do
for item <- collection, do: fun.(item)
def map(enumerable, fun)
def map([], _fun) do
[]
end
def map([head | tail], fun) do
[fun.(head) | map(tail, fun)]
end
def map(collection, fun) do
+1 -1
View File
@@ -48,7 +48,7 @@ defmodule File.Stat do
The time type returned in `atime`, `mtime`, and `ctime` is dependent on the
time type set in options. `{:time, type}` where type can be `:local`,
`:universal`, or `:posix`. Default is `:local`.
`:universal`, or `:posix`. Default is `:universal`.
"""
record = Record.extract(:file_info, from_lib: "kernel/include/file.hrl")
+2 -2
View File
@@ -131,12 +131,12 @@ defmodule IO.ANSI do
@doc "Clears screen"
defsequence :clear, "2", "J"
@doc "Clears line"
defsequence :clear_line, "2", "K"
defp format_sequence(other) do
raise ArgumentError, "invalid ANSI sequence specification: #{other}"
raise ArgumentError, "invalid ANSI sequence specification: #{inspect other}"
end
@doc ~S"""
+8 -4
View File
@@ -65,6 +65,9 @@ defmodule Keyword do
iex> Keyword.new([{:b, 1}, {:a, 2}])
[a: 2, b: 1]
iex> Keyword.new([{:a, 1}, {:a, 2}, {:a, 3}])
[a: 3]
"""
@spec new(Enum.t) :: t
def new(pairs) do
@@ -80,17 +83,18 @@ defmodule Keyword do
## Examples
iex> Keyword.new([:a, :b], fn (x) -> {x, x} end)
[b: :b, a: :a]
iex> Keyword.new([:a, :b], fn (x) -> {x, x} end) |> Enum.sort
[a: :a, b: :b]
"""
@spec new(Enum.t, (term -> {key, value})) :: t
def new(pairs, transform) when is_function(transform, 1) do
def new(pairs, transform) do
fun = fn el, acc ->
{k, v} = transform.(el)
put_new(acc, k, v)
end
:lists.foldr(fun, [], Enum.reverse(pairs))
keywords = :lists.foldl(fun, [], Enum.reverse(pairs))
:lists.reverse(keywords)
end
@doc """
+1 -1
View File
@@ -487,7 +487,7 @@ tokenize([T|Rest], Line, Column, Scope, Tokens) when ?is_horizontal_space(T) ->
end;
tokenize([T|Rest], Line, Column, _Scope, Tokens) ->
Message = io_lib:format("\"~s\" (column ~p, codepoint U+~4.16.0B)", [[T], Column, T]),
Message = io_lib:format("\"~ts\" (column ~p, codepoint U+~4.16.0B)", [[T], Column, T]),
{error, {Line, "unexpected token: ", Message}, Rest, Tokens}.
strip_horizontal_space(T) ->
+4 -1
View File
@@ -87,8 +87,11 @@ defmodule IO.ANSITest do
end
test "format invalid sequence" do
assert_raise ArgumentError, "invalid ANSI sequence specification: brigh", fn ->
assert_raise ArgumentError, "invalid ANSI sequence specification: :brigh", fn ->
IO.ANSI.format([:brigh, "Hello!"], true)
end
assert_raise ArgumentError, "invalid ANSI sequence specification: nil", fn ->
IO.ANSI.format(["Hello!", nil], true)
end
end
end
@@ -11,8 +11,8 @@ defmodule Kernel.ErrorsTest do
test "invalid token" do
assert_compile_fail SyntaxError,
"nofile:1: unexpected token: \" \" (column 7, codepoint U+00A0)",
'[foo: \u00A0]\noops'
"nofile:1: unexpected token: \"\u200B\" (column 7, codepoint U+200B)",
'[foo: \u200B]\noops'
end
test "invalid quoted token" do
+4 -5
View File
@@ -83,7 +83,8 @@ defmodule IEx.CLI do
{:badrpc, reason} ->
abort "Could not contact remote node #{remote}, reason: #{inspect reason}. Aborting..."
{:module, IEx} ->
{remote, :erlang, :apply, [remote_start_function, []]}
{mod, fun, args} = remote_start_mfa()
{remote, mod, fun, args}
_ ->
abort "Could not find IEx on remote node #{remote}. Aborting..."
end
@@ -108,7 +109,7 @@ defmodule IEx.CLI do
&local_start/0
end
defp remote_start_function do
defp remote_start_mfa do
ref = make_ref
opts = options()
@@ -120,9 +121,7 @@ defmodule IEx.CLI do
end
end
fn ->
IEx.start(opts, {__MODULE__, :remote_start, [parent, ref]})
end
{IEx, :start, [opts, {__MODULE__, :remote_start, [parent, ref]}]}
end
defp options do
+7 -2
View File
@@ -74,11 +74,16 @@ defmodule Logger.Backends.Console do
enabled: Keyword.get(colors, :enabled, IO.ANSI.enabled?)}
end
defp log_event(level, msg, ts, md, %{colors: colors} = state) do
defp log_event(level, msg, ts, md, %{colors: colors, device: device} = state) do
output =
format_event(level, msg, ts, md, state)
|> color_event(level, colors)
IO.write(state.device, output)
try do
IO.write(device, output)
rescue
ArgumentError ->
IO.write(device, Logger.Formatter.prune(output))
end
end
defp format_event(level, msg, ts, md, %{format: format, metadata: keys}) do
+20
View File
@@ -44,6 +44,26 @@ defmodule Logger.Formatter do
@type pattern :: :date | :level | :levelpad | :message | :metadata | :node | :time
@valid_patterns [:time, :date, :message, :level, :node, :metadata, :levelpad]
@default_pattern "\n$time $metadata[$level] $levelpad$message\n"
@replacement "�"
@doc """
Prune non-valid UTF-8 codepoints.
Typically called after formatting when the data cannot be printed.
"""
@spec prune(IO.chardata) :: IO.chardata
def prune(binary) when is_binary(binary), do: prune_binary(binary, "")
def prune([h|t]) when h in 0..1114111, do: [h|prune(t)]
def prune([h|t]), do: [prune(h)|prune(t)]
def prune([]), do: []
def prune(_), do: @replacement
defp prune_binary(<<h::utf8, t::binary>>, acc),
do: prune_binary(t, <<acc::binary, h::utf8>>)
defp prune_binary(<<_, t::binary>>, acc),
do: prune_binary(t, <<acc::binary, @replacement>>)
defp prune_binary(<<>>, acc),
do: acc
@doc ~S"""
Compiles a format string into an array that the `format/5` can handle.
+3 -3
View File
@@ -10,9 +10,9 @@ defmodule Logger.Watcher do
"""
def start_link(m, f, a) do
import Supervisor.Spec
child = worker(__MODULE__, [],
[function: :watcher, restart: :transient])
options = [strategy: :simple_one_for_one, name: @name]
child = worker(__MODULE__, [], [function: :watcher, restart: :transient])
options = [strategy: :simple_one_for_one, name: @name,
max_restarts: 30, max_seconds: 3]
case Supervisor.start_link([child], options) do
{:ok, _} = ok ->
_ = for {mod, handler, args} <- apply(m, f, a) do
@@ -4,6 +4,14 @@ defmodule Logger.FormatterTest do
import Logger.Formatter
test "prune/1" do
assert prune(1) == "�"
assert prune(<<"hí", 233>>) == "hí�"
assert prune(["hi"|233]) == ["hi"|"�"]
assert prune([233|"hi"]) == [233|"hi"]
assert prune([[]|[]]) == [[]]
end
defmodule CompileMod do
def format(_level, _msg, _ts, _md) do
true
+3 -3
View File
@@ -80,16 +80,16 @@ defmodule Logger.UtilsTest do
{'~ts~ts~ts', ["abcdeabcde", "", ""]}
end
test "timestamp" do
test "timestamp/1" do
assert {{_, _, _}, {_, _, _, _}} = timestamp(true)
end
test "format_date" do
test "format_date/1" do
date = {2015, 1, 30}
assert format_date(date) == ["2015", ?-, [?0, "1"], ?-, "30"]
end
test "format_time" do
test "format_time/1" do
time = {12, 30, 10, 1}
assert format_time(time) == ["12", ?:, "30", ?:, "10", ?., [?0, ?0, "1"]]
+1 -1
View File
@@ -214,7 +214,7 @@ defmodule Mix.Dep.Converger do
# When in_upper is true
#
# When a parent dependency specifies only that is a subset
# When a parent dependency specifies :only that is a subset
# of a child dependency, we are going to abort as the parent
# dependency must explicitly outline a superset of child
# dependencies.
+2 -8
View File
@@ -209,20 +209,14 @@ defmodule Mix.Dep.Loader do
## Fetching
# We need to override the dependencies so they mirror
# the :only requirement in the parent. Furthermore, if
# a dependency has :only, we must remove it as deps
# always run in a given environment (which does not
# necessarily mirror the application one unless
# configured otherwise).
# the :only requirement in the parent.
defp attach_only(deps, opts) do
if only = opts[:only] do
Enum.map(deps, fn %{opts: opts} = dep ->
%{dep | opts: Keyword.put(opts, :only, only)}
end)
else
Enum.map(deps, fn %{opts: opts} = dep ->
%{dep | opts: Keyword.delete(opts, :only)}
end)
deps
end
end
+2 -2
View File
@@ -111,11 +111,11 @@ defmodule Mix.Shell do
{:unix, _} ->
command = command
|> String.replace("\"", "\\\"")
|> :binary.bin_to_list
|> String.to_char_list
'sh -c "' ++ command ++ '"'
{:win32, osname} ->
command = :binary.bin_to_list(command)
command = String.to_char_list(command)
case {System.get_env("COMSPEC"), osname} do
{nil, :windows} -> 'command.com /c ' ++ command
{nil, _} -> 'cmd /c ' ++ command
+4 -7
View File
@@ -2,12 +2,9 @@ defmodule DepsRepo do
use Mix.Project
def project do
[
app: :deps_repo,
version: "0.1.0",
deps: [
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
]
]
opts = Process.get(:custom_deps_git_repo_opts) || []
[app: :deps_repo,
version: "0.1.0",
deps: [{:git_repo, "0.1.0", [git: MixTest.Case.fixture_path("git_repo")] ++ opts}]]
end
end
+31 -1
View File
@@ -273,7 +273,35 @@ defmodule Mix.DepTest do
end
end
test "nested deps on only matching" do
# deps_repo wants git_repo for test, git_repo is restricted to only test
# We assert the dependencies match as expected, happens in umbrella apps
Process.put(:custom_deps_git_repo_opts, [only: :test])
# We need to pass env: :test so the child dependency is loaded
# in the first place (otherwise only :prod deps are loaded)
deps = [{:deps_repo, "0.1.0", path: "custom/deps_repo", env: :test},
{:git_repo, "0.2.0", git: MixTest.Case.fixture_path("git_repo"), only: :test}]
with_deps deps, fn ->
in_fixture "deps_status", fn ->
loaded = Mix.Dep.loaded([])
assert [:git_repo, :deps_repo] = Enum.map(loaded, &(&1.app))
assert [unavailable: _, noappfile: _] = Enum.map(loaded, &(&1.status))
loaded = Mix.Dep.loaded([env: :dev])
assert [:deps_repo] = Enum.map(loaded, &(&1.app))
assert [noappfile: _] = Enum.map(loaded, &(&1.status))
loaded = Mix.Dep.loaded([env: :test])
assert [:git_repo, :deps_repo] = Enum.map(loaded, &(&1.app))
assert [unavailable: _, noappfile: _] = Enum.map(loaded, &(&1.status))
end
end
end
test "nested deps on only conflict" do
# deps_repo wants all git_repo, git_repo is restricted to only test
deps = [{:deps_repo, "0.1.0", path: "custom/deps_repo"},
{:git_repo, "0.2.0", git: MixTest.Case.fixture_path("git_repo"), only: :test}]
@@ -300,6 +328,7 @@ defmodule Mix.DepTest do
end
test "nested deps with valid only subset" do
# deps_repo wants git_repo for prod, git_repo is restricted to only prod and test
deps = [{:deps_repo, "0.1.0", path: "custom/deps_repo", only: :prod},
{:git_repo, "0.2.0", git: MixTest.Case.fixture_path("git_repo"), only: [:prod, :test]}]
@@ -324,6 +353,7 @@ defmodule Mix.DepTest do
end
test "nested deps with invalid only subset" do
# deps_repo wants git_repo for dev, git_repo is restricted to only test
deps = [{:deps_repo, "0.1.0", path: "custom/deps_repo", only: :dev},
{:git_repo, "0.2.0", git: MixTest.Case.fixture_path("git_repo"), only: [:test]}]
@@ -349,7 +379,7 @@ defmodule Mix.DepTest do
end
end
test "nested deps on only diverged" do
test "nested deps converge and diverge only not in_upper" do
loaded_only = fn deps ->
with_deps deps, fn ->
in_fixture "deps_status", fn ->
+6 -2
View File
@@ -34,9 +34,13 @@ defmodule Mix.UmbrellaTest do
end
end
test "compiles umbrella with protocol consolidation" do
test "compiles umbrella with build embedded (and protocol consolidation)" do
in_fixture "umbrella_dep/deps/umbrella", fn ->
Mix.Project.in_project(:umbrella, ".", [consolidate_protocols: true], fn _ ->
Mix.Project.in_project(:umbrella, ".", [build_embedded: true], fn _ ->
assert_raise Mix.Error, ~r"Cannot execute task because the project was not yet compiled", fn ->
Mix.Tasks.App.Start.run []
end
Mix.Task.run "compile"
assert_received {:mix_shell, :info, ["Generated bar app"]}