Skip to content

Commit

Permalink
Merge branch 'main' into apb/data-source
Browse files Browse the repository at this point in the history
  • Loading branch information
leandrocp committed Dec 15, 2023
2 parents f366c83 + 28904e8 commit df4e991
Show file tree
Hide file tree
Showing 22 changed files with 301 additions and 94 deletions.
2 changes: 1 addition & 1 deletion lib/beacon/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule Beacon.Application do
def start(_type, _args) do
# Starts just the minimum required apps for beacon to work.
# - Keep loading sites as children of main sup to have control of where and when to trigger it.
# - Loading repo allows to run seeds without trigering module and css recompilation.
# - Loading repo allows to run seeds without triggering module and css recompilation.
children = [
Beacon.Registry,
{Phoenix.PubSub, name: Beacon.PubSub},
Expand Down
29 changes: 23 additions & 6 deletions lib/beacon/content.ex
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ defmodule Beacon.Content do
* `title` - String.t()
* `description` - String.t()
* `template` - String.t()
* `meta_tags` - list(map()) eg: `[%{"property" => "og:title", "content" => "My New Siste"}]`
* `meta_tags` - list(map()) eg: `[%{"property" => "og:title", "content" => "My New Site"}]`
See `Beacon.Content.Page` for more info.
Expand Down Expand Up @@ -750,35 +750,40 @@ defmodule Beacon.Content do
## Options
* `:per_page` - limit how many records are returned, or pass `:infinity` to return all records.
* `:offset` - offsets the number of rows selected from the result.
* `:query` - search pages by path or title.
* `:preloads` - a list of preloads to load.
* `:sort` - column in which the result will be ordered by.
"""
@doc type: :pages
@spec list_pages(Site.t(), keyword()) :: [Page.t()]
def list_pages(site, opts \\ []) do
per_page = Keyword.get(opts, :per_page, 20)
offset = Keyword.get(opts, :offset, 0)
search = Keyword.get(opts, :query)
preloads = Keyword.get(opts, :preloads, [])
sort = Keyword.get(opts, :sort, :title)

site
|> query_list_pages_base()
|> query_list_pages_limit(per_page)
|> query_list_pages_offset(offset)
|> query_list_pages_search(search)
|> query_list_pages_preloads(preloads)
|> query_list_pages_sort(sort)
|> Repo.all()
end

defp query_list_pages_base(site) do
from p in Page,
where: p.site == ^site,
order_by: [asc: p.order, asc: fragment("length(?)", p.path)]
end
defp query_list_pages_base(site), do: from(p in Page, where: p.site == ^site)

defp query_list_pages_limit(query, limit) when is_integer(limit), do: from(q in query, limit: ^limit)
defp query_list_pages_limit(query, :infinity = _limit), do: query
defp query_list_pages_limit(query, _per_page), do: from(q in query, limit: 20)

defp query_list_pages_offset(query, offset) when is_integer(offset), do: from(q in query, offset: ^offset)
defp query_list_pages_offset(query, _offset), do: from(q in query, offset: 0)

defp query_list_pages_search(query, search) when is_binary(search) do
from(q in query, where: ilike(q.path, ^"%#{search}%") or ilike(q.title, ^"%#{search}%"))
end
Expand All @@ -791,6 +796,18 @@ defmodule Beacon.Content do

defp query_list_pages_preloads(query, _preloads), do: query

defp query_list_pages_sort(query, sort), do: from(q in query, order_by: [asc: ^sort])

@doc """
Counts the total number of pages based on the amount of pages.
"""
@spec count_pages(Site.t()) :: integer()
def count_pages(site) do
query = from p in Page, where: p.site == ^site, select: count(p.id)

Repo.one(query)
end

@doc """
Returns all published pages for `site`.
Expand Down
2 changes: 1 addition & 1 deletion lib/beacon/content/layout.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule Beacon.Content.Layout do
schema "beacon_layouts" do
field :site, Beacon.Types.Site
field :title, :string
field :template, :string
field :template, :string, default: "<%= @inner_content %>"
field :meta_tags, {:array, :map}, default: []
field :resource_links, {:array, :map}, default: []

Expand Down
4 changes: 2 additions & 2 deletions lib/beacon/lifecycle/template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ defmodule Beacon.Lifecycle.Template do

def validate_output!(lifecycle, _site, _type) do
raise Beacon.LoaderError, """
excpected output to be a %Phoenix.LiveView.Rendered{} struct
expected output to be a %Phoenix.LiveView.Rendered{} struct
Got:
Expand Down Expand Up @@ -101,7 +101,7 @@ defmodule Beacon.Lifecycle.Template do
@spec render_template(Beacon.Content.Page.t(), module(), map(), Macro.Env.t()) :: Beacon.Template.t()
def render_template(page, page_module, assigns, env) do
template =
case page_module.render(assigns) do
case Beacon.Template.render(page_module, assigns) do
%Phoenix.LiveView.Rendered{} = rendered -> rendered
:not_loaded -> Beacon.Loader.load_page_template(page, page_module, assigns)
end
Expand Down
49 changes: 28 additions & 21 deletions lib/beacon/loader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,13 @@ defmodule Beacon.Loader do

defp load_site_from_db(site) do
with :ok <- Beacon.RuntimeJS.load!(),
:ok <- load_runtime_css(site),
:ok <- load_stylesheets(site),
:ok <- load_components(site),
:ok <- load_snippet_helpers(site),
:ok <- load_layouts(site),
:ok <- load_pages(site),
:ok <- load_error_pages(site),
:ok <- load_stylesheets(site),
:ok <- load_runtime_css(site),
:ok <- load_data_source(site) do
:ok
else
Expand Down Expand Up @@ -201,12 +202,15 @@ defmodule Beacon.Loader do

# too slow to run the css compiler on every test
if Code.ensure_loaded?(Mix.Project) and Mix.env() == :test do
defp load_runtime_css(_site), do: :ok
@doc false
def load_runtime_css(_site), do: :ok
else
defp load_runtime_css(site), do: Beacon.RuntimeCSS.load!(site)
@doc false
def load_runtime_css(site), do: Beacon.RuntimeCSS.load!(site)
end

defp load_stylesheets(site) do
@doc false
def load_stylesheets(site) do
StylesheetModuleLoader.load_stylesheets(site, Content.list_stylesheets(site))
:ok
end
Expand All @@ -224,7 +228,8 @@ defmodule Beacon.Loader do
:ok
end

defp load_layouts(site) do
@doc false
def load_layouts(site) do
site
|> Content.list_published_layouts()
|> Enum.map(fn layout ->
Expand All @@ -238,7 +243,8 @@ defmodule Beacon.Loader do
:ok
end

defp load_pages(site) do
@doc false
def load_pages(site) do
site
|> Content.list_published_pages()
|> Enum.map(fn page ->
Expand Down Expand Up @@ -372,8 +378,9 @@ defmodule Beacon.Loader do

@doc false
def handle_call({:load_page, page}, _from, config) do
result = do_load_page!(page)
:ok = load_runtime_css(page.site)
{:reply, do_load_page(page), config}
{:reply, result, config}
end

def handle_call({:load_page_template, page, page_module, assigns}, _from, config) do
Expand All @@ -391,14 +398,14 @@ defmodule Beacon.Loader do
def handle_info({:layout_published, %{site: site, id: id}}, state) do
layout = Content.get_published_layout(site, id)

with :ok <- load_runtime_css(site),
# TODO: load only used components, depends on https://github.com/BeaconCMS/beacon/issues/84
:ok <- load_components(site),
# TODO: load only used components, depends on https://github.com/BeaconCMS/beacon/issues/84
with :ok <- load_components(site),
# TODO: load only used snippet helpers
:ok <- load_snippet_helpers(site),
:ok <- load_stylesheets(site),
{:ok, _module, _ast} <- Beacon.Loader.LayoutModuleLoader.load_layout!(layout),
:ok <- maybe_reload_error_pages(layout) do
:ok <- maybe_reload_error_pages(layout),
:ok <- load_runtime_css(site),
:ok <- load_stylesheets(site) do
:ok
else
_ -> raise Beacon.LoaderError, message: "failed to load resources for layout #{layout.title} of site #{layout.site}"
Expand All @@ -409,25 +416,25 @@ defmodule Beacon.Loader do

@doc false
def handle_info({:page_published, %{site: site, id: id}}, state) do
:ok = load_runtime_css(site)

site
|> Content.get_published_page(id)
|> do_load_page()
|> do_load_page!()

:ok = load_runtime_css(site)

{:noreply, state}
end

@doc false
def handle_info({:pages_published, site, pages}, state) do
:ok = load_runtime_css(site)

for page <- pages do
site
|> Content.get_published_page(page.id)
|> do_load_page()
|> do_load_page!()
end

:ok = load_runtime_css(site)

{:noreply, state}
end

Expand Down Expand Up @@ -468,9 +475,9 @@ defmodule Beacon.Loader do
{:noreply, state}
end

defp do_load_page(page) when is_nil(page), do: nil
defp do_load_page!(page) when is_nil(page), do: nil

defp do_load_page(page) do
defp do_load_page!(page) do
layout = Content.get_published_layout(page.site, page.layout_id)

# TODO: load only used components, depends on https://github.com/BeaconCMS/beacon/issues/84
Expand Down
2 changes: 1 addition & 1 deletion lib/beacon/loader/page_module_loader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ defmodule Beacon.Loader.PageModuleLoader do

with %Content.Page{} = page <- Beacon.Content.get_published_page(page.site, page.id),
{:ok, ^page_module, _ast} <- do_load_page!(page, :request),
%Phoenix.LiveView.Rendered{} = rendered <- page_module.render(assigns) do
%Phoenix.LiveView.Rendered{} = rendered <- Beacon.Template.render(page_module, assigns) do
rendered
else
_ ->
Expand Down
4 changes: 3 additions & 1 deletion lib/beacon/media_library.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,16 @@ defmodule Beacon.MediaLibrary do
|> Enum.map(&get_url_for(&1, asset))
end

def srcset_for_image(asset, sources) do
def srcset_for_image(%Asset{} = asset, sources) do
asset = Repo.preload(asset, :assets)

asset.assets
|> filter_sources(sources)
|> build_srcset()
end

def srcset_for_image(_asset, _sources), do: []

defp filter_sources(assets, sources) do
Enum.filter(
assets,
Expand Down
37 changes: 25 additions & 12 deletions lib/beacon/registry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,43 @@ defmodule Beacon.Registry do
@doc false
def via(key, value), do: {:via, Registry, {__MODULE__, key, value}}

@doc """
Return a list of all running sites in the current node.
"""
@spec running_sites() :: [Beacon.Types.Site.t()]
def running_sites do
match = {{:site, :"$1"}, :_, :_}
guards = []
body = [:"$1"]

Registry.select(__MODULE__, [{match, guards, body}])
end

@doc false
def config!(site) do
def config!(site) when is_atom(site) do
case lookup({:site, site}) do
{_pid, config} ->
config

_ ->
raise RuntimeError, """
Site #{inspect(site)} was not found. Make sure it's configured and started,
site #{inspect(site)} was not found. Make sure it's configured and started,
see `Beacon.start_link/1` for more info.
"""
end
end

@doc """
Return a list of all running sites in the current node.
"""
@spec running_sites() :: [Beacon.Types.Site.t()]
def running_sites do
match = {{:site, :"$1"}, :_, :_}
guards = []
body = [:"$1"]

Registry.select(__MODULE__, [{match, guards, body}])
@doc false
def update_config(site, fun) when is_atom(site) and is_function(fun, 1) do
result =
Registry.update_value(__MODULE__, {:site, site}, fn config ->
fun.(config)
end)

case result do
{new_value, _old_value} -> new_value
error -> error
end
end

defp lookup(site) do
Expand Down
24 changes: 20 additions & 4 deletions lib/beacon/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,26 @@ defmodule Beacon.Router do

@doc false
def dump_pages do
case :ets.match(@ets_table, :"$1") do
[] -> []
[pages] -> pages
end
@ets_table |> :ets.match(:"$1") |> List.flatten()
end

@doc false
def dump_pages(site) do
match = {{site, :_}, :_}
guards = []
body = [:"$_"]

@ets_table
|> :ets.select([{match, guards, body}])
|> List.flatten()
end

def dump_page_modules(site, fun \\ &Function.identity/1) do
site
|> dump_pages()
|> Enum.map(fn {{^site, _path} = key, {_page_id, _layout_id, _format, page_module, _component_module}} ->
fun.(Tuple.append(key, page_module))
end)
end

@doc false
Expand Down
6 changes: 6 additions & 0 deletions lib/beacon/runtime_css.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ defmodule Beacon.RuntimeCSS do
"""

@callback compile(Beacon.Types.Site.t()) :: {:ok, String.t()} | {:error, any()}
@callback compile(Beacon.Types.Site.t(), template :: String.t()) :: {:ok, String.t()} | {:error, any()}

@doc false
def compile(site) when is_atom(site) do
Beacon.Config.fetch!(site).css_compiler.compile(site)
end

@doc false
def compile(site, template) when is_atom(site) and is_binary(template) do
Beacon.Config.fetch!(site).css_compiler.compile(site, template)
end

@doc false
def fetch(site) do
case :ets.match(:beacon_assets, {{site, :css}, {:_, :_, :"$1"}}) do
Expand Down
Loading

0 comments on commit df4e991

Please sign in to comment.