Skip to content

Commit

Permalink
fix: page variant flashing (#681)
Browse files Browse the repository at this point in the history
  • Loading branch information
APB9785 authored Dec 3, 2024
1 parent 180d578 commit 2efc02b
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 50 deletions.
1 change: 1 addition & 0 deletions dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ defmodule DemoWeb.Router do
plug :fetch_live_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug Beacon.Plug
end

scope "/", DemoWeb do
Expand Down
2 changes: 1 addition & 1 deletion lib/beacon/loader/page.ex
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ defmodule Beacon.Loader.Page do
def render(var!(assigns)) when is_map(var!(assigns)) do
var!(assigns)
|> templates()
|> Beacon.Template.choose_template()
|> Beacon.Template.choose_template(var!(assigns).beacon.private[:variant_roll])
end

def templates(var!(assigns)) when is_map(var!(assigns)) do
Expand Down
25 changes: 25 additions & 0 deletions lib/beacon/plug.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule Beacon.Plug do
@moduledoc """
Used to ensure consistency for Beacon Page rendering.
This is especially important when using Page Variants.
## Usage
Add the plug to your Router's `:browser` pipeline:
```
pipeline :browser do
...
plug Beacon.Plug
end
```
"""
@behaviour Plug

@impl Plug
def init(_opts), do: []

@impl Plug
def call(conn, _opts), do: Plug.Conn.put_session(conn, :beacon_variant_roll, Enum.random(1..100))
end
46 changes: 9 additions & 37 deletions lib/beacon/template.ex
Original file line number Diff line number Diff line change
@@ -1,32 +1,3 @@
defmodule Beacon.Template.LoadMetadata do
@moduledoc """
Metadata passed to page loading lifecycle.
"""

defstruct [:site, :path]

@type t :: %__MODULE__{
site: Beacon.Types.Site.t(),
path: String.t()
}
end

defmodule Beacon.Template.RenderMetadata do
@moduledoc """
Metadata passed to page rendering lifecycle.
"""

defstruct [:site, :path, :page_module, :assigns, :env]

@type t :: %__MODULE__{
site: Beacon.Types.Site.t(),
path: String.t(),
page_module: module(),
assigns: Phoenix.LiveView.Socket.assigns(),
env: Macro.Env.t()
}
end

defmodule Beacon.Template do
@moduledoc """
Template for layouts, pages, and any other resource that display HTML/HEEx.
Expand All @@ -36,16 +7,18 @@ defmodule Beacon.Template do
Template engines that do not support dynamic content can make use of the `:static` field to store its contents.
"""

alias Beacon.Web.BeaconAssigns

require Logger

@typedoc """
The AST representation of a `t:Phoenix.LiveView.Rendered.t/0` struct.
"""
@type ast :: Macro.t()

@type t :: Phoenix.LiveView.Rendered.t() | ast()

# Used for backwards-compatibility with Atom feeds
@doc false
def render_path(site, path_info, query_params \\ %{}) when is_atom(site) and is_list(path_info) and is_map(query_params) do
case Beacon.RouterServer.lookup_page(site, path_info) do
Expand All @@ -55,7 +28,7 @@ defmodule Beacon.Template do
page ->
page_module = Beacon.Loader.fetch_page_module(page.site, page.id)
live_data = Beacon.Web.DataSource.live_data(site, path_info)
beacon_assigns = BeaconAssigns.new(site, page, live_data, path_info, query_params)
beacon_assigns = BeaconAssigns.new(site, page, live_data, path_info, query_params, :beacon)
assigns = Map.put(live_data, :beacon, beacon_assigns)
env = Beacon.Web.PageLive.make_env(site)

Expand All @@ -69,11 +42,10 @@ defmodule Beacon.Template do
end

@doc false
def choose_template([primary]), do: primary
def choose_template([primary | variants]), do: choose_template(variants, Enum.random(1..100), primary)
def choose_template([primary | variants], roll), do: choose_template(variants, roll, primary)

@doc false
def choose_template([], _, primary), do: primary
def choose_template([{weight, template} | _], n, _) when weight >= n, do: template
def choose_template([{weight, _} | variants], n, primary), do: choose_template(variants, n - weight, primary)
defp choose_template([], _, primary), do: primary
defp choose_template(_, nil, primary), do: primary
defp choose_template([{weight, template} | _], n, _) when weight >= n, do: template
defp choose_template([{weight, _} | variants], n, primary), do: choose_template(variants, n - weight, primary)
end
12 changes: 12 additions & 0 deletions lib/beacon/template/load_metadata.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Beacon.Template.LoadMetadata do
@moduledoc """
Metadata passed to page loading lifecycle.
"""

defstruct [:site, :path]

@type t :: %__MODULE__{
site: Beacon.Types.Site.t(),
path: String.t()
}
end
15 changes: 15 additions & 0 deletions lib/beacon/template/render_metadata.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Beacon.Template.RenderMetadata do
@moduledoc """
Metadata passed to page rendering lifecycle.
"""

defstruct [:site, :path, :page_module, :assigns, :env]

@type t :: %__MODULE__{
site: Beacon.Types.Site.t(),
path: String.t(),
page_module: module(),
assigns: Phoenix.LiveView.Socket.assigns(),
env: Macro.Env.t()
}
end
22 changes: 16 additions & 6 deletions lib/beacon/web/beacon_assigns.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ defmodule Beacon.Web.BeaconAssigns do
info_handlers_module: nil,
event_handlers_module: nil,
live_data_keys: [],
live_path: []
live_path: [],
variant_roll: nil
}

@doc false
Expand All @@ -52,15 +53,23 @@ defmodule Beacon.Web.BeaconAssigns do
end

@doc false
def new(site, %Beacon.Content.Page{} = page) do
def new(site, %Beacon.Content.Page{} = page, variant_roll) do
components_module = Beacon.Loader.Components.module_name(site)
page_module = Beacon.Loader.Page.module_name(site, page.id)
%__MODULE__{site: site, private: %{components_module: components_module, page_module: page_module}}

%__MODULE__{
site: site,
private: %{
components_module: components_module,
page_module: page_module,
variant_roll: variant_roll
}
}
end

@doc false
def new(site, %Beacon.Content.Page{} = page, live_data, path_info, query_params, source \\ :beacon)
when is_atom(site) and is_map(live_data) and is_list(path_info) and is_map(query_params) do
def new(site, %Beacon.Content.Page{} = page, live_data, path_info, query_params, source, variant_roll \\ nil)
when is_atom(site) and is_map(live_data) and is_list(path_info) and is_map(query_params) and source in [:beacon, :admin] do
%{site: ^site} = page
page_module = Beacon.Loader.Page.module_name(site, page.id)
live_data = Beacon.Web.DataSource.live_data(site, path_info, Map.drop(query_params, ["path"]))
Expand All @@ -81,7 +90,8 @@ defmodule Beacon.Web.BeaconAssigns do
info_handlers_module: info_handlers_module,
event_handlers_module: event_handlers_module,
live_data_keys: Map.keys(live_data),
live_path: path_info
live_path: path_info,
variant_roll: variant_roll
}
}
end
Expand Down
19 changes: 17 additions & 2 deletions lib/beacon/web/live/page_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,23 @@ defmodule Beacon.Web.PageLive do
:ok = Beacon.PubSub.subscribe_to_page(site, path)
end

variant_roll =
case session["beacon_variant_roll"] do
nil ->
Logger.warning("""
Beacon.Plug is missing from the Router pipeline.
Page Variants will not be used.
""")

nil

roll ->
roll
end

page = RouterServer.lookup_page!(site, path)
socket = Component.assign(socket, beacon: BeaconAssigns.new(site, page))
socket = Component.assign(socket, beacon: BeaconAssigns.new(site, page, variant_roll))

{:ok, socket, layout: {Beacon.Web.Layouts, :dynamic}}
end
Expand Down Expand Up @@ -124,7 +139,7 @@ defmodule Beacon.Web.PageLive do

page = RouterServer.lookup_page!(site, path_info)
live_data = Beacon.Web.DataSource.live_data(site, path_info, Map.drop(params, ["path"]))
beacon_assigns = BeaconAssigns.new(site, page, live_data, path_info, params)
beacon_assigns = BeaconAssigns.new(site, page, live_data, path_info, params, :beacon, socket.assigns.beacon.private.variant_roll)

socket =
socket
Expand Down
8 changes: 4 additions & 4 deletions test/beacon_web/beacon_assigns_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule Beacon.Web.BeaconAssignsTest do
test "build with published page resolves page title", %{site: site} do
page = beacon_published_page_fixture(path: "/blog", title: "blog index")

assigns = BeaconAssigns.new(site, page, %{}, ["blog"], %{})
assigns = BeaconAssigns.new(site, page, %{}, ["blog"], %{}, :beacon)

assert %BeaconAssigns{
site: @site,
Expand All @@ -46,7 +46,7 @@ defmodule Beacon.Web.BeaconAssignsTest do
test "build with path info and query params", %{site: site} do
page = beacon_published_page_fixture(path: "/blog")

assigns = BeaconAssigns.new(site, page, %{}, ["blog"], %{source: "search"})
assigns = BeaconAssigns.new(site, page, %{}, ["blog"], %{source: "search"}, :beacon)

assert %BeaconAssigns{
site: @site,
Expand All @@ -60,7 +60,7 @@ defmodule Beacon.Web.BeaconAssignsTest do
test "build with path params", %{site: site} do
page = beacon_published_page_fixture(path: "/blog/:post")

assigns = BeaconAssigns.new(site, page, %{}, ["blog", "hello"], %{})
assigns = BeaconAssigns.new(site, page, %{}, ["blog", "hello"], %{}, :beacon)

assert %BeaconAssigns{
site: @site,
Expand All @@ -74,7 +74,7 @@ defmodule Beacon.Web.BeaconAssignsTest do
live_data = beacon_live_data_fixture(path: "/blog")
beacon_live_data_assign_fixture(live_data: live_data, format: :text, key: "customer_id", value: "123")

assigns = BeaconAssigns.new(site, page, live_data, ["blog"], %{})
assigns = BeaconAssigns.new(site, page, live_data, ["blog"], %{}, :beacon)

assert %BeaconAssigns{
site: @site,
Expand Down

0 comments on commit 2efc02b

Please sign in to comment.