diff --git a/apps/game_of_life/lib/game_of_life/grid.ex b/apps/game_of_life/lib/game_of_life/grid.ex index efa8a65f..d107c6ca 100644 --- a/apps/game_of_life/lib/game_of_life/grid.ex +++ b/apps/game_of_life/lib/game_of_life/grid.ex @@ -1,7 +1,7 @@ defmodule GameOfLife.Grid do - alias Sim.Torus, as: Grid + alias Ximula.Torus, as: Grid - defdelegate height(grid), to: Sim.Torus + defdelegate height(grid), to: Ximula.Torus def toggle(grid, x, y) do current = Grid.get(grid, x, y) diff --git a/apps/game_of_life/lib/game_of_life/sim_service.ex b/apps/game_of_life/lib/game_of_life/sim_service.ex index b42b190f..c0915029 100644 --- a/apps/game_of_life/lib/game_of_life/sim_service.ex +++ b/apps/game_of_life/lib/game_of_life/sim_service.ex @@ -4,12 +4,13 @@ defmodule GameOfLife.SimService do @behaviour Sim.CommandHandler alias GameOfLife.Simulation + alias Ximula.Grid @impl true def execute(:tick, []) do change_data(fn grid -> changes = grid |> Simulation.sim() - grid = Sim.Grid.apply_changes(grid, changes) + grid = Grid.apply_changes(grid, changes) {grid, [{:changed, changes: changes}]} end) end diff --git a/apps/game_of_life/lib/game_of_life/simulation.ex b/apps/game_of_life/lib/game_of_life/simulation.ex index fbc61dde..86634ad3 100644 --- a/apps/game_of_life/lib/game_of_life/simulation.ex +++ b/apps/game_of_life/lib/game_of_life/simulation.ex @@ -1,5 +1,5 @@ defmodule GameOfLife.Simulation do - alias Sim.Torus, as: Grid + alias Ximula.Torus, as: Grid def sim(grid) do Enum.reduce(0..(Grid.width(grid) - 1), %{}, fn x, changes -> diff --git a/apps/game_of_life/mix.exs b/apps/game_of_life/mix.exs index 5689c770..919bd528 100644 --- a/apps/game_of_life/mix.exs +++ b/apps/game_of_life/mix.exs @@ -4,7 +4,7 @@ defmodule GameOfLife.MixProject do def project do [ app: :game_of_life, - version: "0.6.0", + version: "0.7.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", @@ -32,6 +32,7 @@ defmodule GameOfLife.MixProject do defp deps do [ {:sim, in_umbrella: true}, + {:ximula, github: "grrrisu/Ximula"}, {:phoenix_pubsub, "~> 2.0"}, {:credo, "~> 1.5", only: [:dev, :test], runtime: false} ] diff --git a/apps/game_of_life/test/game_of_life/simulation_test.exs b/apps/game_of_life/test/game_of_life/simulation_test.exs index 3f63f4ca..2fbdb7af 100644 --- a/apps/game_of_life/test/game_of_life/simulation_test.exs +++ b/apps/game_of_life/test/game_of_life/simulation_test.exs @@ -1,7 +1,7 @@ defmodule GameOfLife.SimulationTest do use ExUnit.Case, async: true - alias Sim.Torus, as: Grid + alias Ximula.Torus, as: Grid alias GameOfLife.Simulation # https://de.wikipedia.org/wiki/Conways_Spiel_des_Lebens#Die_Spielregeln diff --git a/apps/game_of_life/test/game_of_life_test.exs b/apps/game_of_life/test/game_of_life_test.exs index 456faa3a..7e640703 100644 --- a/apps/game_of_life/test/game_of_life_test.exs +++ b/apps/game_of_life/test/game_of_life_test.exs @@ -1,7 +1,7 @@ defmodule GameOfLifeTest do use ExUnit.Case, async: false - alias Sim.Torus, as: Grid + alias Ximula.Torus, as: Grid setup do Phoenix.PubSub.subscribe(ThundermoonWeb.PubSub, "GameOfLife") diff --git a/apps/sim/lib/sim/object/grid.ex b/apps/sim/lib/sim/object/grid.ex deleted file mode 100644 index 20d04367..00000000 --- a/apps/sim/lib/sim/object/grid.ex +++ /dev/null @@ -1,112 +0,0 @@ -defmodule Sim.Grid do - alias Sim.Grid - - def create(width, height, default \\ nil) - - def create(width, height, func) when is_function(func) do - 0..(width - 1) - |> Map.new(fn x -> - {x, - 0..(height - 1) - |> Map.new(fn y -> - {y, func.(x, y)} - end)} - end) - end - - def create(width, height, [[_c | _] | _r] = list) do - Grid.create(width, height, fn x, y -> - list |> Enum.at(height - (y + 1)) |> Enum.at(x) - end) - end - - def create(width, height, value) do - Grid.create(width, height, fn _x, _y -> value end) - end - - def apply_changes(grid, changes) do - Enum.reduce(changes, grid, fn {{x, y}, value}, grid -> - Grid.put(grid, x, y, value) - end) - end - - def get(nil, _x, _y), do: {:error, "grid is nil"} - - def get(%{0 => columns} = grid, x, y) - when x >= 0 and x < map_size(grid) and y >= 0 and y < map_size(columns) do - get_in(grid, [x, y]) - end - - def get(grid, x, y) when is_integer(x) and is_integer(y) do - {:error, - "coordinates x: #{x}, y: #{y} outside of grid width: #{width(grid)}, height: #{height(grid)}"} - end - - def get(_grid, x, y) do - {:error, "only integers are allowed as coordinates, x: #{x}, y: #{y}"} - end - - def put(%{0 => columns} = grid, x, y, value) - when x >= 0 and x < map_size(grid) and y >= 0 and y < map_size(columns) do - put_in(grid, [x, y], value) - end - - def put(grid, x, y, _value) when is_integer(x) and is_integer(y) do - {:error, - "coordinates x: #{x}, y: #{y} outside of grid width: #{width(grid)}, height: #{height(grid)}"} - end - - def put(_grid, x, y, _value) do - {:error, "only integers are allowed as coordinates, x: #{x}, y: #{y}"} - end - - def width(grid) do - map_size(grid) - end - - def height(grid) do - map_size(grid[0]) - end - - # return the grid as a list - # note: the order of the values may be in random - def values(grid) do - Enum.map(grid, fn {_x, col} -> - Enum.map(col, fn {_y, value} -> - value - end) - end) - |> List.flatten() - end - - # note: the order of the values may be in random - def filter(grid, func) do - Enum.map(grid, fn {x, col} -> - Enum.filter(col, fn {y, value} -> - func.(x, y, value) - end) - |> Enum.map(&(&1 |> Tuple.to_list() |> List.last())) - end) - |> List.flatten() - end - - # [{x0, y0, value}, {x1, y0, value}, ...] - def map(grid, func \\ &{&1, &2, &3}) do - Enum.map(grid, fn {x, col} -> - Enum.map(col, fn {y, value} -> - {func.(x, y, value)} - end) - end) - |> Enum.reverse() - |> Enum.zip() - |> Enum.map(&Tuple.to_list/1) - |> List.flatten() - |> Enum.map(fn {i} -> i end) - |> Enum.reverse() - end - - def merge_field(grid, x, y, value, func \\ &Map.merge(&1, &2)) do - field = Grid.get(grid, x, y) - Grid.put(grid, x, y, func.(field, value)) - end -end diff --git a/apps/sim/lib/sim/object/torus.ex b/apps/sim/lib/sim/object/torus.ex deleted file mode 100644 index 3b4a2ee5..00000000 --- a/apps/sim/lib/sim/object/torus.ex +++ /dev/null @@ -1,41 +0,0 @@ -defmodule Sim.Torus do - defdelegate create(width, height, value \\ nil), to: Sim.Grid - defdelegate width(grid), to: Sim.Grid - defdelegate height(grid), to: Sim.Grid - - def get(grid, x, y) when is_integer(x) and x >= map_size(grid) do - get(grid, x - width(grid), y) - end - - def get(grid, x, y) when is_integer(x) and x < 0 do - get(grid, x + width(grid), y) - end - - def get(%{0 => columns} = grid, x, y) when is_integer(y) and y >= map_size(columns) do - get(grid, x, y - height(grid)) - end - - def get(grid, x, y) when is_integer(y) and y < 0 do - get(grid, x, y + height(grid)) - end - - defdelegate get(grid, x, y), to: Sim.Grid - - def put(grid, x, y, value) when is_integer(x) and x >= map_size(grid) do - put(grid, x - width(grid), y, value) - end - - def put(grid, x, y, value) when is_integer(x) and x < 0 do - put(grid, x + width(grid), y, value) - end - - def put(%{0 => columns} = grid, x, y, value) when is_integer(y) and y >= map_size(columns) do - put(grid, x, y - height(grid), value) - end - - def put(grid, x, y, value) when is_integer(y) and y < 0 do - put(grid, x, y + height(grid), value) - end - - defdelegate put(grid, x, y, value), to: Sim.Grid -end diff --git a/apps/sim/lib/sim/realm/access_proxy.ex b/apps/sim/lib/sim/realm/access_proxy.ex deleted file mode 100644 index be04c042..00000000 --- a/apps/sim/lib/sim/realm/access_proxy.ex +++ /dev/null @@ -1,168 +0,0 @@ -defmodule Sim.AccessProxy do - use GenServer - - @doc """ - Keeps updates on an agent in sequence to avoid race conditions by overwriting data, - while remaining responsive to just read operations. - - AccessProxy.exclusive_get() # blocks until an other exclusive client updates the data - AccessProxy.get() # never blocks - AccessProxy.update(data) # releases the lock and will reply to the next client in line with the updated data - - Example: - {:ok, pid} = Agent.start_link(fn -> 42 end) - - # normaly the agent will be referenced by name not pid, so that we don't need to monitor the agent - {:ok, _} = Sim.AccessProxy.start_link(agent: pid) - - 1..3 - |> Enum.map(fn _n -> - Task.async(fn -> - value = Sim.AccessProxy.exclusive_get() - Process.sleep(1_000) - :ok = Sim.AccessProxy.update(value + 1) - end) - end) - |> Task.await_many() - 45 = Sim.AccessProxy.get() - """ - - # milliseconds - @max_duration 5_000 - - def start_link(opts) do - GenServer.start_link(__MODULE__, Keyword.delete(opts, :name), name: opts[:name] || __MODULE__) - end - - def get(server \\ __MODULE__, func \\ & &1) do - GenServer.call(server, {:get, func}) - end - - def exclusive_get(server \\ __MODULE__, func \\ & &1) do - GenServer.call(server, {:exclusive_get, func}) - end - - def update(server \\ __MODULE__, data) - - def update(server, func) when is_function(func) do - GenServer.call(server, {:update, func}) - end - - def update(server, data) do - GenServer.call(server, {:update, fn _ -> data end}) - end - - def init(opts) do - {:ok, - %{ - caller: nil, - agent: opts[:agent], - requests: [], - max_duration: opts[:max_duration] || @max_duration - }} - end - - def handle_call({:get, func}, _from, state) do - {:reply, get_data(state.agent, func), state} - end - - def handle_call({:exclusive_get, func}, {pid, _} = from, %{caller: nil} = state) do - monitor_ref = Process.monitor(pid) - start_check_timeout(from, monitor_ref, state.max_duration) - {:reply, get_data(state.agent, func), %{state | caller: {pid, monitor_ref}}} - end - - def handle_call({:exclusive_get, func}, {pid, _}, %{caller: {pid, _}} = state) do - {:reply, get_data(state.agent, func), state} - end - - def handle_call({:exclusive_get, func}, {pid, _} = from, %{caller: caller} = state) do - monitor_ref = Process.monitor(pid) - {:noreply, %{state | requests: state.requests ++ [{from, monitor_ref, func}]}} - end - - def handle_call({:update, func}, {pid, _}, %{caller: {pid, monitor_ref}, requests: []} = state) do - update_data(state.agent, func) - Process.demonitor(monitor_ref, [:flush]) - {:reply, :ok, %{state | caller: nil}} - end - - def handle_call({:update, func}, {pid, _}, %{caller: {pid, monitor_ref}} = state) do - update_data(state.agent, func) - Process.demonitor(monitor_ref, [:flush]) - {next_caller, state} = reply_to_next_caller(state) - {:reply, :ok, %{state | caller: next_caller}} - end - - def handle_call({:update, _func}, _from, state) do - {:reply, - {:error, - "request the data first with AccessProxy#exclusive_get or maybe too much time elapsed since exclusive_get was called"}, - state} - end - - def handle_info( - {:check_timeout, {pid, _ref}, monitor_ref}, - %{caller: {pid, monitor_ref}, requests: []} = state - ) do - Process.demonitor(monitor_ref, [:flush]) - {:noreply, %{state | caller: nil}} - end - - def handle_info( - {:check_timeout, {pid, _ref}, monitor_ref}, - %{caller: {pid, monitor_ref}} = state - ) do - Process.demonitor(monitor_ref, [:flush]) - {next_caller, state} = reply_to_next_caller(state) - {:noreply, %{state | caller: next_caller}} - end - - def handle_info({:check_timeout, _}, state) do - {:noreply, state} - end - - def handle_info({:DOWN, _ref, :process, _pid, _reason}, %{caller: nil} = state) do - {:noreply, state} - end - - def handle_info( - {:DOWN, _ref, :process, pid, _reason}, - %{caller: {pid, _}, requests: []} = state - ) do - {:noreply, %{state | caller: nil}} - end - - def handle_info({:DOWN, _ref, :process, pid, _reason}, %{caller: {pid, _}} = state) do - {next_caller, state} = reply_to_next_caller(state) - {:noreply, %{state | caller: next_caller}} - end - - def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do - requests = Enum.reject(state.requests, fn {req_pid, _ref, _func} -> pid == req_pid end) - {:noreply, %{state | requests: requests}} - end - - def handle_info(_msg, state) do - {:noreply, state} - end - - defp get_data(agent, func) do - Agent.get(agent, func) - end - - defp update_data(agent, func) do - :ok = Agent.update(agent, func) - end - - defp reply_to_next_caller(state) do - [{{pid, _ref} = next_caller, monitor_ref, get_func} | requests] = state.requests - :ok = GenServer.reply(next_caller, get_data(state.agent, get_func)) - start_check_timeout(next_caller, monitor_ref, state.max_duration) - {{pid, monitor_ref}, %{state | requests: requests}} - end - - defp start_check_timeout(current_caller, monitor_ref, max_duration) do - Process.send_after(self(), {:check_timeout, current_caller, monitor_ref}, max_duration) - end -end diff --git a/apps/sim/mix.exs b/apps/sim/mix.exs index 1ea491e9..06510f5a 100644 --- a/apps/sim/mix.exs +++ b/apps/sim/mix.exs @@ -4,7 +4,7 @@ defmodule Sim.MixProject do def project do [ app: :sim, - version: "0.10.0", + version: "0.11.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", diff --git a/apps/sim/test/sim/object/grid_test.exs b/apps/sim/test/sim/object/grid_test.exs deleted file mode 100644 index bfd34ac4..00000000 --- a/apps/sim/test/sim/object/grid_test.exs +++ /dev/null @@ -1,93 +0,0 @@ -defmodule Sim.GridTest do - use ExUnit.Case, async: true - - alias Sim.Grid - - test "create a new grid" do - grid = Grid.create(2, 3) - assert %{0 => %{0 => nil, 1 => nil, 2 => nil}, 1 => %{0 => nil, 1 => nil, 2 => nil}} = grid - end - - test "create a new grid with 0 as default" do - grid = Grid.create(2, 3, 0) - assert %{0 => %{0 => 0, 1 => 0, 2 => 0}, 1 => %{0 => 0, 1 => 0, 2 => 0}} = grid - end - - test "create a new grid with a function" do - grid = Grid.create(2, 3, fn x, y -> x + y end) - assert 3 == Grid.get(grid, 1, 2) - end - - test "create a new grid from a list" do - list = [ - [0, 2, 3], - [4, 5, 6], - [7, 8, 9] - ] - - grid = Grid.create(3, 3, list) - assert 7 == Grid.get(grid, 0, 0) - assert 3 == Grid.get(grid, 2, 2) - end - - test "set and read from grid" do - grid = Grid.create(2, 3) - assert nil == Grid.get(grid, 1, 2) - new_grid = Grid.put(grid, 1, 2, "value") - assert "value" == Grid.get(new_grid, 1, 2) - end - - test "read outside of grid" do - grid = Grid.create(2, 3, "one") - assert {:error, _msg} = Grid.get(grid, 3, 2) - end - - test "put outside of grid" do - grid = Grid.create(2, 3) - assert {:error, _msg} = Grid.put(grid, 3, 2, "one") - end - - test "read with invalid coordinates" do - grid = Grid.create(2, 3, "one") - assert {:error, _msg} = Grid.get(grid, "two", "one") - end - - test "write with invalid coordinates" do - grid = Grid.create(2, 3, "one") - assert {:error, _msg} = Grid.put(grid, "two", "one", "foo") - end - - test "values" do - grid = Grid.create(2, 2, fn x, y -> {x, y} end) - assert [{0, 0}, {0, 1}, {1, 0}, {1, 1}] = Grid.values(grid) - end - - test "filter" do - grid = Grid.create(2, 3, fn x, y -> %{sum: x + y} end) - results = Grid.filter(grid, fn x, y, _v -> rem(x, 2) == 0 && rem(y, 2) == 0 end) - assert [%{sum: 0}, %{sum: 2}] = results - end - - test "map grid" do - grid = Grid.create(2, 3, fn x, y -> x + y end) - - expected = [ - [0, 2, 2], - [1, 2, 3], - [0, 1, 1], - [1, 1, 2], - [0, 0, 0], - [1, 0, 1] - ] - - assert ^expected = Grid.map(grid, fn x, y, v -> [x, y, v] end) - end - - test "merge field" do - grid = Grid.create(1, 2, %{some_value: 3}) - grid = Grid.merge_field(grid, 0, 0, %{some_value: 0}) - grid = Grid.merge_field(grid, 0, 1, %{other_value: 5}) - assert %{some_value: 0} = Grid.get(grid, 0, 0) - assert %{other_value: 5, some_value: 3} = Grid.get(grid, 0, 1) - end -end diff --git a/apps/sim/test/sim/object/torus_test.exs b/apps/sim/test/sim/object/torus_test.exs deleted file mode 100644 index 54ca1452..00000000 --- a/apps/sim/test/sim/object/torus_test.exs +++ /dev/null @@ -1,71 +0,0 @@ -defmodule Sim.TorusTest do - use ExUnit.Case, async: true - - alias Sim.Torus, as: Grid - - test "create a new grid" do - grid = Grid.create(2, 3) - assert %{0 => %{0 => nil, 1 => nil, 2 => nil}, 1 => %{0 => nil, 1 => nil, 2 => nil}} = grid - end - - test "set and read from grid" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert {1, 2} == Grid.get(grid, 1, 2) - new_grid = Grid.put(grid, 1, 2, "value") - assert "value" == Grid.get(new_grid, 1, 2) - end - - test "read outside of grid x too big" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert {1, 2} = Grid.get(grid, 3, 2) - end - - test "read outside of grid x negative" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert {1, 2} = Grid.get(grid, -3, 2) - end - - test "read outside of grid y too big" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert {1, 2} = Grid.get(grid, 1, 5) - end - - test "read outside of grid y negative" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert {1, 1} = Grid.get(grid, 1, -2) - end - - test "put outside of grid x too big" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert new_grid = Grid.put(grid, 3, 2, "value") - assert "value" = Grid.get(new_grid, 1, 2) - end - - test "put outside of grid x negative" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert new_grid = Grid.put(grid, -3, 2, "value") - assert "value" = Grid.get(new_grid, 1, 2) - end - - test "put outside of grid y too big" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert new_grid = Grid.put(grid, 1, 5, "value") - assert "value" = Grid.get(new_grid, 1, 2) - end - - test "put outside of grid y negative" do - grid = Grid.create(2, 3, fn x, y -> {x, y} end) - assert new_grid = Grid.put(grid, 1, -2, "value") - assert "value" = Grid.get(new_grid, 1, 1) - end - - test "read with invalid coordinates" do - grid = Grid.create(2, 3, "one") - assert {:error, _msg} = Grid.get(grid, "two", "one") - end - - test "write with invalid coordinates" do - grid = Grid.create(2, 3, "one") - assert {:error, _msg} = Grid.put(grid, "two", "one", "foo") - end -end diff --git a/apps/sim/test/sim/realm/access_process_test.exs b/apps/sim/test/sim/realm/access_process_test.exs deleted file mode 100644 index 30af84ee..00000000 --- a/apps/sim/test/sim/realm/access_process_test.exs +++ /dev/null @@ -1,135 +0,0 @@ -defmodule Sim.AccessProxyTest do - use ExUnit.Case - - alias Sim.AccessProxy - - setup do - agent = start_supervised!({Agent, fn -> 42 end}) - proxy = start_supervised!({AccessProxy, [agent: agent]}) - supervisor = start_link_supervised!(Task.Supervisor) - - %{agent: agent, proxy: proxy, supervisor: supervisor} - end - - describe "exclusive_get" do - test "are executed in sequence", %{proxy: proxy} do - 1..3 - |> Enum.map(fn _n -> - Task.async(fn -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - :ok = AccessProxy.update(proxy, value + 1) - end) - end) - |> Task.await_many() - - assert 45 == AccessProxy.get() - end - - test "allow update only after exclusive_get", %{proxy: proxy} do - value = AccessProxy.get(proxy) - assert {:error, _} = AccessProxy.update(proxy, value + 1) - assert 42 == AccessProxy.get(proxy) - end - - test "get never blocks", %{proxy: proxy} do - result = - [ - Task.async(fn -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - :ok = AccessProxy.update(proxy, value + 1) - AccessProxy.get() - end), - Task.async(fn -> - Process.sleep(10) - AccessProxy.get() - end), - Task.async(fn -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - :ok = AccessProxy.update(proxy, value + 1) - AccessProxy.get() - end) - ] - |> Enum.map(&Task.await(&1)) - - assert [43, 42, 44] = result - end - - test "remove lock if current client crashes", %{proxy: proxy, supervisor: supervisor} do - result = - [ - Task.Supervisor.async_stream_nolink(supervisor, [1], fn _n -> - _value = AccessProxy.exclusive_get(proxy) - Process.sleep(10) - Process.exit(self(), :upps) - end), - Task.Supervisor.async_stream_nolink(supervisor, [2], fn _n -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - :ok = AccessProxy.update(proxy, value + 1) - AccessProxy.get() - end) - ] - |> Enum.map(&Enum.to_list(&1)) - |> List.flatten() - - assert [exit: :upps, ok: 43] = result - end - - test "remove from requests if queued client crashes", %{proxy: proxy, supervisor: supervisor} do - result = - [ - Task.Supervisor.async_stream_nolink(supervisor, [2], fn _n -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - :ok = AccessProxy.update(proxy, value + 1) - AccessProxy.get() - end), - Task.Supervisor.async_stream_nolink(supervisor, [1], fn _n -> - _value = AccessProxy.exclusive_get(proxy) - Process.sleep(10) - Process.exit(self(), :upps) - end), - Task.Supervisor.async_stream_nolink(supervisor, [2], fn _n -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - :ok = AccessProxy.update(proxy, value + 1) - AccessProxy.get() - end) - ] - |> Enum.map(&Enum.to_list(&1)) - |> List.flatten() - - assert [ok: 43, exit: :upps, ok: 44] = result - end - - test "timeouted request should not be able to update", %{agent: agent} do - proxy = - start_supervised!( - {AccessProxy, [name: :fast_access, agent: agent, max_duration: 50]}, - id: :fast_access - ) - - [ - Task.async(fn -> - value = AccessProxy.exclusive_get(proxy) - Process.sleep(100) - {:error, _msg} = AccessProxy.update(proxy, value + 10) - end), - Task.async(fn -> - value = AccessProxy.exclusive_get(proxy) - :ok = AccessProxy.update(proxy, value + 1) - end), - Task.async(fn -> - value = AccessProxy.exclusive_get(proxy) - :ok = AccessProxy.update(proxy, value + 1) - end) - ] - |> Task.await_many() - - assert 44 == AccessProxy.get() - end - end -end diff --git a/apps/thundermoon_web/assets/package-lock.json b/apps/thundermoon_web/assets/package-lock.json index 67f85fef..1be423b7 100644 --- a/apps/thundermoon_web/assets/package-lock.json +++ b/apps/thundermoon_web/assets/package-lock.json @@ -25,11 +25,11 @@ } }, "../../../deps/phoenix": { - "version": "1.7.7", + "version": "1.7.9", "license": "MIT" }, "../../../deps/phoenix_html": { - "version": "3.3.2" + "version": "3.3.3" }, "../../../deps/phoenix_live_view": { "version": "0.18.18", @@ -131,9 +131,9 @@ } }, "node_modules/@tailwindcss/forms": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.4.tgz", - "integrity": "sha512-YAm12D3R7/9Mh4jFbYSMnsd6jG++8KxogWgqs7hbdo/86aWjjlIEvL7+QYdVELmAI0InXTpZqFIg5e7aDVWI2Q==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.6.tgz", + "integrity": "sha512-Fw+2BJ0tmAwK/w01tEFL5TiaJBX1NLT1/YbWgvm7ws3Qcn11kiXxzNTEQDMs5V3mQemhB56l3u0i9dwdzSQldA==", "dev": true, "dependencies": { "mini-svg-data-uri": "^1.2.3" @@ -169,9 +169,9 @@ } }, "node_modules/alpinejs": { - "version": "3.12.3", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.3.tgz", - "integrity": "sha512-fLz2dfYQ3xCk7Ip8LiIpV2W+9brUyex2TAE7Z0BCvZdUDklJE+n+a8gCgLWzfZ0GzZNZu7HUP8Z0z6Xbm6fsSA==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.1.tgz", + "integrity": "sha512-/LZ7mumW02V7AV5xTTftJFHS0I3KOXLl7tHm4xpxXAV+HJ/zjTT0n8MU7RZ6UoGPhmO/i+KEhQojaH/0RsH5tg==", "dependencies": { "@vue/reactivity": "~3.1.1" } @@ -202,9 +202,9 @@ "dev": true }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "dev": true, "funding": [ { @@ -214,12 +214,16 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -271,9 +275,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -290,10 +294,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -312,9 +316,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001520", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz", - "integrity": "sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA==", + "version": "1.0.30001547", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", + "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", "dev": true, "funding": [ { @@ -480,9 +484,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.490", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz", - "integrity": "sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==", + "version": "1.4.553", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.553.tgz", + "integrity": "sha512-HiRdtyKS2+VhiXvjhMvvxiMC33FJJqTA5EB2YHgFZW6v7HkK4Q9Ahv2V7O2ZPgAjw+MyCJVMQvigj13H8t+wvA==", "dev": true }, "node_modules/escalade": { @@ -550,16 +554,16 @@ "dev": true }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fs-extra": { @@ -583,9 +587,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -596,12 +600,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -692,13 +690,10 @@ "dev": true }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, "engines": { "node": ">= 0.4.0" } @@ -792,9 +787,9 @@ } }, "node_modules/jiti": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", - "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", + "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==", "dev": true, "bin": { "jiti": "bin/jiti.js" @@ -1073,9 +1068,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { @@ -1245,9 +1240,9 @@ } }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -1508,9 +1503,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -1559,9 +1554,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", "dev": true, "engines": { "node": ">= 14" diff --git a/apps/thundermoon_web/lib/thundermoon_web/live/game_of_life_live/grid.ex b/apps/thundermoon_web/lib/thundermoon_web/live/game_of_life_live/grid.ex index f86195f7..364489d9 100644 --- a/apps/thundermoon_web/lib/thundermoon_web/live/game_of_life_live/grid.ex +++ b/apps/thundermoon_web/lib/thundermoon_web/live/game_of_life_live/grid.ex @@ -1,7 +1,7 @@ defmodule ThundermoonWeb.GameOfLifeLive.Grid do use Phoenix.Component - alias Sim.Grid + alias Ximula.Grid def matrix(assigns) do assigns = matrix_assigns(assigns) diff --git a/changelog.md b/changelog.md index 09535150..b91a09e9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## dev +## 0.11.0 - General: @@ -10,6 +10,7 @@ - Sim: - Sim.AccessProxy to block concurrent updates, but allow concurrent reads - more functions for grid: Sim.Grid.values/1 and Sim.Grid.filter/2 + - Grid, Torus and the new AccessProxy have been extracted to [Ximula](https://github.com/grrrisu/Ximula) ## 0.10.0 diff --git a/mix.exs b/mix.exs index 94ecfd21..728676f1 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Thundermoon.Umbrella.MixProject do def project do [ apps_path: "apps", - version: "0.10.0", + version: "0.11.0", start_permanent: Mix.env() == :prod, deps: deps(), aliases: aliases(), diff --git a/mix.lock b/mix.lock index c72491ae..33dacad8 100644 --- a/mix.lock +++ b/mix.lock @@ -2,8 +2,8 @@ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "canada": {:hex, :canada, "2.0.0", "ce5e058f576a0625959fc5427fcde15311fb28a5ebc13775eafd13468ad16553", [:mix], [], "hexpm", "49a648c48d8b0864380f38f02a7f316bd30fd45602205c48197432b5225d8596"}, "canary": {:git, "https://github.com/runhyve/canary.git", "b81d780e1cb7a1c276599f980ab9c9a7c9cd8c12", []}, - "castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"}, - "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, + "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, @@ -12,14 +12,14 @@ "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, - "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, + "ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"}, "esbuild": {:hex, :esbuild, "0.7.1", "fa0947e8c3c3c2f86c9bf7e791a0a385007ccd42b86885e8e893bdb6631f5169", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "66661cdf70b1378ee4dc16573fcee67750b59761b2605a0207c267ab9d19f13c"}, "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "gettext": {:hex, :gettext, "0.23.0", "8065a6eb5a6c94ef3909b23178ffc064cd43a51d977b6c80babc8bb0c5ebfd6f", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "09ca27284fe5a82449eb252f2877e551a46b1c9c7d10d72d087211f0bfed9638"}, - "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, + "gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, @@ -28,22 +28,22 @@ "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"}, "observer_cli": {:hex, :observer_cli, "1.7.4", "3c1bfb6d91bf68f6a3d15f46ae20da0f7740d363ee5bc041191ce8722a6c4fae", [:mix, :rebar3], [{:recon, "~> 2.5.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "50de6d95d814f447458bd5d72666a74624eddb0ef98bdcee61a0153aae0865ff"}, - "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "phoenix": {:hex, :phoenix, "1.7.7", "4cc501d4d823015007ba3cdd9c41ecaaf2ffb619d6fb283199fa8ddba89191e0", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "8966e15c395e5e37591b6ed0bd2ae7f48e961f0f60ac4c733f9566b519453085"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "phoenix": {:hex, :phoenix, "1.7.9", "9a2b873e2cb3955efdd18ad050f1818af097fa3f5fc3a6aaba666da36bdd3f02", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "83e32da028272b4bfd076c61a964e6d2b9d988378df2f1276a0ed21b13b5e997"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"}, - "phoenix_html": {:hex, :phoenix_html, "3.3.2", "d6ce982c6d8247d2fc0defe625255c721fb8d5f1942c5ac051f6177bffa5973f", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "44adaf8e667c1c20fb9d284b6b0fa8dc7946ce29e81ce621860aa7e96de9a11d"}, + "phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"}, - "plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"}, + "plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, - "postgrex": {:hex, :postgrex, "0.17.2", "a3ec9e3239d9b33f1e5841565c4eb200055c52cc0757a22b63ca2d529bbe764c", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "80a918a9e9531d39f7bd70621422f3ebc93c01618c645f2d91306f50041ed90c"}, + "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, - "recon": {:hex, :recon, "2.5.3", "739107b9050ea683c30e96de050bc59248fd27ec147696f79a8797ff9fa17153", [:mix, :rebar3], [], "hexpm", "6c6683f46fd4a1dfd98404b9f78dcabc7fcd8826613a89dcb984727a8c3099d7"}, + "recon": {:hex, :recon, "2.5.4", "05dd52a119ee4059fa9daa1ab7ce81bc7a8161a2f12e9d42e9d551ffd2ba901c", [:mix, :rebar3], [], "hexpm", "e9ab01ac7fc8572e41eb59385efeb3fb0ff5bf02103816535bacaedf327d0263"}, "sentry": {:hex, :sentry, "8.1.0", "8d235b62fce5f8e067ea1644e30939405b71a5e1599d9529ff82899d11d03f2b", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f9fc7641ef61e885510f5e5963c2948b9de1de597c63f781e9d3d6c9c8681ab4"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, @@ -53,6 +53,7 @@ "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "ueberauth_github": {:hex, :ueberauth_github, "0.8.3", "1c478629b4c1dae446c68834b69194ad5cead3b6c67c913db6fdf64f37f0328f", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "ae0ab2879c32cfa51d7287a48219b262bfdab0b7ec6629f24160564247493cc6"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "websock": {:hex, :websock, "0.5.2", "b3c08511d8d79ed2c2f589ff430bd1fe799bb389686dafce86d28801783d8351", [:mix], [], "hexpm", "925f5de22fca6813dfa980fb62fd542ec43a2d1a1f83d2caec907483fe66ff05"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.3", "4908718e42e4a548fc20e00e70848620a92f11f7a6add8cf0886c4232267498d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "cbe5b814c1f86b6ea002b52dd99f345aeecf1a1a6964e209d208fb404d930d3d"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.4", "7af8408e7ed9d56578539594d1ee7d8461e2dd5c3f57b0f2a5352d610ddde757", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d2c238c79c52cbe223fcdae22ca0bb5007a735b9e933870e241fce66afb4f4ab"}, + "ximula": {:git, "https://github.com/grrrisu/Ximula.git", "ec1b863596d4baac9176ed80f30684708738435c", []}, }