From 63182d9f816ea7c5b5590b727b3fd2a7808dce47 Mon Sep 17 00:00:00 2001 From: Tomek Gryszkiewicz Date: Fri, 13 Apr 2018 18:35:10 +0200 Subject: [PATCH] added broadcast_poke; close #60 --- CHANGELOG.md | 3 + lib/drab/client.ex | 14 +++-- lib/drab/commander.ex | 8 ++- lib/drab/core.ex | 9 +++ lib/drab/live.ex | 58 ++++++++++++++++++- test/integration/live_broadcasting_text.exs | 31 ++++++++++ test/support/router.ex | 1 + test/support/web/commanders/live_commander.ex | 25 ++------ .../web/controllers/live_controller.ex | 4 ++ .../web/templates/live/broadcasting.html.drab | 8 +++ .../support/web/templates/page/index.html.eex | 1 + 11 files changed, 129 insertions(+), 33 deletions(-) create mode 100644 test/integration/live_broadcasting_text.exs create mode 100644 test/support/web/templates/live/broadcasting.html.drab diff --git a/CHANGELOG.md b/CHANGELOG.md index e8dfda2..c3c371e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The performance of `poke` and `peek` operations increases significantly, as the event handler does not have to get the assigns from the browser each time. +#### `broadcast_poke` +You now may broadcast the assign, in exactly the same way you're doing `poke`. + ## v0.7.5 ### New Features #### Living assigns inside the shared comamander diff --git a/lib/drab/client.ex b/lib/drab/client.ex index 5d0873c..d936d56 100644 --- a/lib/drab/client.ex +++ b/lib/drab/client.ex @@ -138,6 +138,7 @@ defmodule Drab.Client do if enables_drab?(controller) do commander = commander_for(controller) view = view_for(controller) + action = Phoenix.Controller.action_name(conn) controller_and_action = Phoenix.Token.sign( @@ -146,11 +147,11 @@ defmodule Drab.Client do __controller: controller, __commander: commander, __view: view, - __action: Phoenix.Controller.action_name(conn), + __action: action, __assigns: assigns ) - broadcast_topic = topic(commander.__drab__().broadcasting, controller, conn.request_path) + broadcast_topic = topic(commander.__drab__().broadcasting, controller, conn.request_path, action) templates = DrabModule.all_templates_for(commander.__drab__().modules) @@ -214,8 +215,9 @@ defmodule Drab.Client do end # defp topic(:all, _, _), do: "all" - @spec topic(atom | String.t(), atom | String.t(), String.t()) :: String.t() - defp topic(:same_path, _, path), do: Drab.Core.same_path(path) - defp topic(:same_controller, controller, _), do: Drab.Core.same_controller(controller) - defp topic(topic, _, _) when is_binary(topic), do: Drab.Core.same_topic(topic) + @spec topic(atom | String.t(), atom | String.t(), atom | String.t(), atom | String.t()) :: String.t() + defp topic(:same_path, _, path, _), do: Drab.Core.same_path(path) + defp topic(:same_controller, controller, _, _), do: Drab.Core.same_controller(controller) + defp topic(:same_action, controller, _, action), do: Drab.Core.same_action(controller, action) + defp topic(topic, _, _, _) when is_binary(topic), do: Drab.Core.same_topic(topic) end diff --git a/lib/drab/commander.ex b/lib/drab/commander.ex index cbcc878..4055f9b 100644 --- a/lib/drab/commander.ex +++ b/lib/drab/commander.ex @@ -454,7 +454,7 @@ defmodule Drab.Commander do end end) - @broadcasts ~w(same_path same_controller)a + @broadcasts ~w(same_path same_controller same_action)a @doc """ Set up broadcasting listen subject for the current commander. @@ -467,8 +467,10 @@ defmodule Drab.Commander do Options: * `:same_path` (default) - broadcasts will go to the browsers rendering the same url - * `:same_controller` - broadcasted message will be received by all browsers, which renders the page generated - by the same controller + * `:same_controller` - broadcasted message will be received by all browsers, which + renders the page generated by the same controller + * `:same_action` - the message will be received by the browsers, rendered with the + same controller and action * `"topic"` - any topic you want to set, messages will go to the clients sharing this topic See `Drab.Core.broadcast_js/2` for more. diff --git a/lib/drab/core.ex b/lib/drab/core.ex index fbd08c7..d6c2391 100644 --- a/lib/drab/core.ex +++ b/lib/drab/core.ex @@ -338,6 +338,15 @@ defmodule Drab.Core do @spec same_controller(String.t() | atom) :: String.t() def same_controller(controller), do: "controller:#{controller}" + @doc """ + Helper for broadcasting functions, returns topic for a given controller and action. + + iex> same_action(DrabTestApp.LiveController, :index) + "controller:Elixir.DrabTestApp.LiveController#index" + """ + @spec same_action(String.t() | atom, String.t() | atom) :: String.t() + def same_action(controller, action), do: "action:#{controller}##{action}" + @doc """ Helper for broadcasting functions, returns topic for a given topic string. iex> same_topic("mytopic") diff --git a/lib/drab/live.ex b/lib/drab/live.ex index 9642e5c..14f2be5 100644 --- a/lib/drab/live.ex +++ b/lib/drab/live.ex @@ -312,7 +312,8 @@ defmodule Drab.Live do """ @spec poke(Phoenix.Socket.t(), Keyword.t()) :: result def poke(socket, assigns) do - do_poke(socket, nil, nil, assigns, &Drab.Core.exec_js/2) + # do_poke(socket, nil, nil, assigns, &Drab.Core.exec_js/2) + poke(socket, nil, nil, assigns) end @doc """ @@ -323,7 +324,8 @@ defmodule Drab.Live do """ @spec poke(Phoenix.Socket.t(), String.t(), Keyword.t()) :: result def poke(socket, partial, assigns) do - do_poke(socket, nil, partial, assigns, &Drab.Core.exec_js/2) + # do_poke(socket, nil, partial, assigns, &Drab.Core.exec_js/2) + poke(socket, nil, partial, assigns) end @doc """ @@ -332,11 +334,61 @@ defmodule Drab.Live do iex> poke(socket, MyApp.UserView, "user.html", name: "Bożywój") %Phoenix.Socket{ ... """ - @spec poke(Phoenix.Socket.t(), atom, String.t(), Keyword.t()) :: result + @spec poke(Phoenix.Socket.t(), atom | nil, String.t() | nil, Keyword.t()) :: result def poke(socket, view, partial, assigns) do do_poke(socket, view, partial, assigns, &Drab.Core.exec_js/2) end + @doc """ + Broadcasting version of `poke/2`. + + Please notice that broadcasting living assigns makes sense only for the pages, which was rendered + with the same templates. + + Always returns socket. + + iex> broadcast_poke(socket, count: 42) + %Phoenix.Socket{ ... + """ + @spec broadcast_poke(Phoenix.Socket.t(), Keyword.t()) :: result + def broadcast_poke(socket, assigns) do + # do_poke(socket, nil, nil, assigns, &Drab.Core.broadcast_js/2) + broadcast_poke(socket, nil, nil, assigns) + end + + @doc """ + Like `broadcast_poke/2`, but limited only to the given partial name. + + iex> broadcast_poke(socket, "user.html", name: "Bożywój") + %Phoenix.Socket{ ... + """ + @spec broadcast_poke(Phoenix.Socket.t(), String.t(), Keyword.t()) :: result + def broadcast_poke(socket, partial, assigns) do + # do_poke(socket, nil, partial, assigns, &Drab.Core.broadcast_js/2) + broadcast_poke(socket, nil, partial, assigns) + end + + @doc """ + Like `broadcast_poke/3`, but searches for the partial within the given view. + + iex> broadcast_poke(socket, MyApp.UserView, "user.html", name: "Bożywój") + %Phoenix.Socket{ ... + """ + @spec broadcast_poke(Phoenix.Socket.t(), atom | nil, String.t() | nil, Keyword.t()) :: result + def broadcast_poke(socket, view, partial, assigns) do + # if socket.assigns.__broadcast_topic =~ "same_path:" || + # socket.assigns.__broadcast_topic =~ "action:" do + do_poke(socket, view, partial, assigns, &Drab.Core.broadcast_js/2) + # else + # raise ArgumentError, + # message: """ + # Broadcasting `poke` makes sense only with `:same_path` or `:same_action` options. + + # You tried: `#{socket.assigns.__broadcast_topic}` + # """ + # end + end + @spec do_poke(Phoenix.Socket.t(), atom | nil, String.t() | nil, Keyword.t(), function) :: result defp do_poke(socket, view, partial_name, assigns, function) do if Enum.member?(Keyword.keys(assigns), :conn) do diff --git a/test/integration/live_broadcasting_text.exs b/test/integration/live_broadcasting_text.exs new file mode 100644 index 0000000..cf77075 --- /dev/null +++ b/test/integration/live_broadcasting_text.exs @@ -0,0 +1,31 @@ +defmodule DrabTestApp.LiveBroadcastingTest do + use DrabTestApp.IntegrationCase + + defp broadcasting_index do + broadcasting_url(DrabTestApp.Endpoint, :broadcasting) + end + + defp wait_for_drab() do + broadcasting_index() |> navigate_to() + find_element(:id, "page_loaded_indicator") + end + + setup do + wait_for_drab() + [socket: drab_socket()] + end + + test "poke should be broadcasted" do + change_to_secondary_session() + wait_for_drab() + + change_to_default_session() + click_and_wait("broadcast_button") + assert visible_text(find_element(:id, "broadcast_out")) == "broadcasted" + + change_to_secondary_session() + assert visible_text(find_element(:id, "broadcast_out")) == "broadcasted" + + end + +end diff --git a/test/support/router.ex b/test/support/router.ex index 8e26182..edab4a2 100644 --- a/test/support/router.ex +++ b/test/support/router.ex @@ -45,6 +45,7 @@ defmodule DrabTestApp.Router do get("/tests/live/query", LiveQueryController, :index, as: :live_query) get("/tests/live/table", LiveController, :table, as: :table) get("/tests/live/advanced", LiveController, :advanced, as: :advanced) + get("/tests/live/broadcasting", LiveController, :broadcasting, as: :broadcasting) get("/tests/element", ElementController, :index, as: :element) end diff --git a/test/support/web/commanders/live_commander.ex b/test/support/web/commanders/live_commander.ex index 895453d..022530e 100644 --- a/test/support/web/commanders/live_commander.ex +++ b/test/support/web/commanders/live_commander.ex @@ -4,6 +4,7 @@ defmodule DrabTestApp.LiveCommander do use Drab.Commander, modules: [Drab.Live, Drab.Element] onload(:page_loaded) + broadcasting(:same_action) def page_loaded(socket) do DrabTestApp.IntegrationCase.add_page_loaded_indicator(socket) @@ -79,22 +80,7 @@ defmodule DrabTestApp.LiveCommander do end defhandler update_mini(socket, _sender) do - # IO.inspect(sender) - # poke socket, class1: "btn", class2: "btn-warning", - # hidden: !peek(socket, :hidden), list: [1,2,3], color: "red" - # poke socket, "users.html", color: "color" - # poke socket, color: "blue", count: 13 - # poke socket, "user.html", user: "Bravo" - # poke socket, "partial1.html", in_partial: "updated partial 1", color: "#66FFFF", link: "https://tg.pl/drab" - # poke socket, users: ["a", "b"] #, link: "aaaa" - # poke socket, link: "a" - # partial4 = render_to_string(DrabTestApp.LiveView, "partial4.html", in_partial: "in partial4", - # color: "#aaaabb", link: "http://tg.pl") - # set_prop(socket, "#partial4_placeholder", innerHTML: partial4) - # , user: "Hendryk") poke(socket, users: ["Mirmił", "Hegemon", "Kokosz", "Kajko"]) - # poke(socket, in_partial: "in_partial after") |> IO.inspect() - # peek(socket, :in_partial) |> IO.inspect() end defhandler update_users(socket, _sender) do @@ -109,10 +95,7 @@ defmodule DrabTestApp.LiveCommander do poke(socket, excluded: "Hegemon") end - # defp loop(socket) do - # IO.inspect self() - # IO.inspect socket - # Process.sleep 5000 - # loop(socket) - # end + defhandler broadcast(socket, _sender) do + broadcast_poke(socket, text: "broadcasted") + end end diff --git a/test/support/web/controllers/live_controller.ex b/test/support/web/controllers/live_controller.ex index d628245..6a80f9b 100644 --- a/test/support/web/controllers/live_controller.ex +++ b/test/support/web/controllers/live_controller.ex @@ -86,4 +86,8 @@ defmodule DrabTestApp.LiveController do in_partial: "in partial before" ) end + + def broadcasting(conn, _param) do + render(conn, text: "set in the controller") + end end diff --git a/test/support/web/templates/live/broadcasting.html.drab b/test/support/web/templates/live/broadcasting.html.drab new file mode 100644 index 0000000..d606c4f --- /dev/null +++ b/test/support/web/templates/live/broadcasting.html.drab @@ -0,0 +1,8 @@ +
+
+ +
+ +
+
+
<%= @text %>
\ No newline at end of file diff --git a/test/support/web/templates/page/index.html.eex b/test/support/web/templates/page/index.html.eex index bde0840..9869efd 100644 --- a/test/support/web/templates/page/index.html.eex +++ b/test/support/web/templates/page/index.html.eex @@ -40,6 +40,7 @@ Pages for Integration Tests:
Drab.Live + Drab.Query
Drab.Live and html table
Drab.Live advanced tests
+Drab.Live broadcasting tests
Drab.Element
Shared Controllers