Skip to content

Commit

Permalink
Enum protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismccord committed Aug 6, 2023
1 parent b75f7d8 commit 2152fa3
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 4 deletions.
43 changes: 43 additions & 0 deletions lib/phoenix_live_view/async_assign.ex
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,47 @@ defmodule Phoenix.LiveView.AsyncAssign do
socket
end
end

defimpl Enumerable, for: Phoenix.LiveView.AsyncAssign do
alias Phoenix.LiveView.AsyncAssign

def count(%AsyncAssign{result: result, loading?: false, error: nil, canceled?: false}),
do: Enum.count(result)

def count(%AsyncAssign{}), do: 0

def member?(%AsyncAssign{result: result, loading?: false, error: nil, canceled?: false}, item) do
Enum.member?(result, item)
end

def member?(%AsyncAssign{}, _item) do
raise RuntimeError, "cannot lookup member? while loading"
end

def reduce(
%AsyncAssign{result: result, loading?: false, error: nil, canceled?: false},
acc,
fun
) do
do_reduce(result, acc, fun)
end

def reduce(%AsyncAssign{}, acc, _fun), do: acc

defp do_reduce(_list, {:halt, acc}, _fun), do: {:halted, acc}
defp do_reduce(list, {:suspend, acc}, fun), do: {:suspended, acc, &do_reduce(list, &1, fun)}
defp do_reduce([], {:cont, acc}, _fun), do: {:done, acc}

defp do_reduce([item | tail], {:cont, acc}, fun) do
do_reduce(tail, fun.(item, acc), fun)
end

def slice(%AsyncAssign{result: result, loading?: false, error: nil, canceled?: false}) do
fn start, length, step -> Enum.slice(result, start..(start + length - 1)//step) end
end

def slice(%AsyncAssign{}) do
fn _start, _length, _step -> [] end
end
end
end
8 changes: 8 additions & 0 deletions test/phoenix_live_view/integrations/assign_async_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,12 @@ defmodule Phoenix.LiveView.AssignAsyncTest do
assert render_async(lv, 200) =~ "data: 123"
end
end

test "enum", %{conn: conn} do
{:ok, lv, _html} = live(conn, "/async?test=enum")

html = render_async(lv, 200)
assert html =~ "data: [1, 2, 3]"
assert html =~ "<div>1</div><div>2</div><div>3</div>"
end
end
23 changes: 19 additions & 4 deletions test/support/live_views/general.ex
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ defmodule Phoenix.LiveViewTest.AsyncLive do
end
end

on_mount {__MODULE__, :defaults}

def on_mount(:defaults, _params, _session, socket) do
{:cont, assign(socket, enum: false)}
end

def render(assigns) do
~H"""
<.live_component module={LC} id="lc" />
Expand All @@ -363,6 +369,9 @@ defmodule Phoenix.LiveViewTest.AsyncLive do
<div :if={[email protected]? && @async.data.result == nil}>no data found</div>
<div :if={[email protected]? && @async.data.result}>data: <%= inspect(@async.data.result) %></div>
<div :if={err = @async.data.error}>error: <%= inspect(err) %></div>
<%= if @enum do %>
<div :for={i <- @async.data}><%= i %></div>
<% end %>
"""
end

Expand Down Expand Up @@ -402,14 +411,20 @@ defmodule Phoenix.LiveViewTest.AsyncLive do
end)}
end

def mount(%{"test" => "enum"}, _session, socket) do
{:ok,
socket |> assign(enum: true) |> assign_async(:data, fn -> {:ok, %{data: [1, 2, 3]}} end)}
end

def handle_info(:boom, _socket), do: exit(:boom)

def handle_info(:cancel, socket), do: {:noreply, cancel_async(socket, :data)}

def handle_info(:renew_canceled, socket) do
{:noreply, assign_async(socket, :data, fn ->
Process.sleep(100)
{:ok, %{data: 123}}
end)}
{:noreply,
assign_async(socket, :data, fn ->
Process.sleep(100)
{:ok, %{data: 123}}
end)}
end
end

0 comments on commit 2152fa3

Please sign in to comment.