はじめに
-
Elixir 楽しんでいますか
- 1.12で追加された関数を調べたいとおもいます
- Elixirのバージョン切り替えは、asdfを使っています
- この記事は、2021/06/05(土) 00:00〜2021/06/07(月) 23:59 開催のautoracex #31の成果です
ところで、Elixirってなにに使えるの?
方法①
-
公式ページのドキュメントをみて、
since 1.12.0
を拾っていく - 例: Enum#count_until/2
- スクレイピングしてみるのはおもしろいかもしれません
方法②
- それぞれバージョンの違うノードをたてて、
:code.all_loaded()
でそれぞれのノードの結果を比較する - 今回はこっちでやってみます
- プログラミングElixir(第2版)の223ページをとても参考にしました
ディレクトリ構成
├── node_new
│ └── new.ex
└── node_old
└── old.ex
node_old
node_old/old.ex
defmodule FunctionsAnswer do
@version "1.11.4"
def start do
pid = spawn(__MODULE__, :generator, [])
:global.register_name(Utils.name(), pid)
end
def generator do
receive do
{:get, pid} ->
send(pid, {:get, {Utils.functions(), @version}})
generator()
end
end
end
defmodule Utils do
@name "awesome"
def name, do: @name
def functions do
:code.all_loaded()
|> Enum.filter(fn {mod, _} -> "#{mod}" =~ ~r{^[A-Z]} end)
|> Enum.map(fn {mod, _} -> mod end)
|> Enum.reduce(%{}, fn mod, acc ->
Map.put(acc, mod, mod.__info__(:functions))
end)
end
end
-
functions
関数のところは手前味噌の「1260 (Elixir 1.11.2-otp-23)」をご参照ください
$ cd node_old
$ asdf local elixir 1.11.4-otp-23
$ asdf local erlang 23.0.1
$ asdf reshim elixir 1.11.4-otp-23
$ asdf reshim erlang 23.0.1
$ iex --sname node_old
iex> c "old.ex"
iex> FunctionsAnswer.start
node_new
node_new/new.ex
defmodule Differ do
@version "1.12.1"
def start do
pid = spawn(__MODULE__, :receiver, [])
send :global.whereis_name(Utils.name()), {:get, pid}
end
def receiver do
receive do
{:get, {got_functions, got_version}} ->
diff(got_functions, got_version)
|> Enum.filter(fn {mod, _} -> "#{mod}" |> String.split(".") |> Enum.count |> Kernel.==(2) end)
|> Map.new
|> write_markdown(got_version)
end
end
defp diff(got_functions, got_version) do
Utils.functions()
|> Enum.map(fn {mod, functions} ->
{new_functions, old_functions} = if Version.compare(@version, got_version) == :gt,
do: {functions, got_functions},
else: {got_functions, functions}
old_elixir = Map.get(old_functions, mod, []) |> MapSet.new()
new_elixir = new_functions |> MapSet.new()
{mod, [MapSet.difference(new_elixir, old_elixir), MapSet.difference(old_elixir, new_elixir)]}
end)
|> Enum.reject(fn {_mod, [only_new, only_old]} -> only_new == MapSet.new and only_old == MapSet.new end)
|> Map.new
end
defp write_markdown(diff_map, got_version) do
create_markdown(diff_map, got_version)
|> (&(File.write("output.md", &1))).()
end
defp create_markdown(diff_map, got_version) do
{only_new_index, only_old_index, new_version, old_version} = if Version.compare(@version, got_version) == :gt,
do: {0, 1, @version, got_version},
else: {1, 0, @version, got_version}
"""
# はじめに
- [Elixir](https://elixir-lang.org/) 楽しんでいますか:bangbang::bangbang::bangbang:
- この記事では`#{old_version}` => `#{new_version}`の差異をお示しします
# [#{old_version}](https://hexdocs.pm/elixir/#{old_version}/Kernel.html) => [#{new_version}](https://hexdocs.pm/elixir/#{new_version}/Kernel.html)増えた関数
#{diff_content(diff_map, only_new_index, new_version)}
# [#{old_version}](https://hexdocs.pm/elixir/#{old_version}/Kernel.html) => [#{new_version}](https://hexdocs.pm/elixir/#{new_version}/Kernel.html)なくなった関数
#{if diff_content(diff_map, only_old_index, old_version) == [],
do: "- 差分なし",
else: diff_content(diff_map, only_old_index, old_version)}
# Wrapping up :lgtm::lgtm::lgtm::lgtm:
- Enjoy [Elixir](https://elixir-lang.org/) :bangbang::bangbang::bangbang:
"""
end
defp diff_content(diff_map, index, version) do
diff_map
|> Enum.reject(fn {mod, _} -> mod == __MODULE__ end)
|> Enum.map(fn {mod, diffs} -> {mod, Enum.at(diffs, index)} end)
|> Enum.map(fn {mod, map_set} ->
module_name = "#{mod}" |> String.split(".") |> Enum.at(-1)
diff_functions =
Enum.reject(map_set, fn {name, _} -> Atom.to_string(name) |> String.starts_with?("__") end)
|> Enum.map(fn {name, arity} ->
"- [#{name}/#{arity}](https://hexdocs.pm/elixir/#{version}/#{module_name}.html##{name}/#{arity})"
end)
|> Enum.join("\n")
if diff_functions == "" do
""
else
"""
## [#{module_name}](https://hexdocs.pm/elixir/#{version}/#{module_name}.html)
#{diff_functions}
"""
end
end)
|> Enum.reject(& &1 == "")
end
end
defmodule Utils do
@name "awesome"
def name, do: @name
def functions do
:code.all_loaded()
|> Enum.filter(fn {mod, _} -> "#{mod}" =~ ~r{^[A-Z]} end)
|> Enum.map(fn {mod, _} -> mod end)
|> Enum.reduce(%{}, fn mod, acc ->
Map.put(acc, mod, mod.__info__(:functions))
end)
end
end
- ちょっと長いですが、
.md
を吐き出すためのゴニョゴニョが長いだけです
$ cd node_new
$ asdf local elixir 1.12.1-otp-24
$ asdf local erlang 24.0.2
$ asdf reshim elixir 1.12.1-otp-24
$ asdf reshim erlang 24.0.2
$ iex --sname node_new
iex> Node.connect :"node_old@20-02noMacBook-Pro"
iex> c "new.ex"
iex> Differ.start
-
Node.connect/1で、
node_old
につないでいます- 逆からつないでもいいはずです
-
Differ.start
を実行すると、output.md
を吐き出します - 実行結果は別の記事であげます
Wrapping up 


- Enjoy Elixir