This commit is contained in:
Zach Daniel 2022-04-05 02:20:36 -04:00
parent deedd976da
commit 86798987d4
25 changed files with 469 additions and 189 deletions

View file

@ -1,4 +1,4 @@
FROM hexpm/elixir:1.12.2-erlang-24.0.5-ubuntu-xenial-20210114 FROM hexpm/elixir:1.13.3-erlang-24.0.5-ubuntu-xenial-20210114
# install build dependencies # install build dependencies
USER root USER root
RUN apt-get update RUN apt-get update
@ -9,6 +9,8 @@ RUN curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update RUN apt-get update
RUN apt-get install -y nodejs yarn RUN apt-get install -y nodejs yarn
RUN mix local.hex --force && \
mix local.rebar --force
ENV MIX_ENV=prod ENV MIX_ENV=prod
COPY ./assets/package.json assets/package.json COPY ./assets/package.json assets/package.json
COPY ./assets/package-lock.json assets/package-lock.json COPY ./assets/package-lock.json assets/package-lock.json
@ -17,9 +19,7 @@ COPY ./mix.exs .
COPY ./mix.lock . COPY ./mix.lock .
COPY ./config/config.exs config/config.exs COPY ./config/config.exs config/config.exs
COPY ./config/prod.exs config/prod.exs COPY ./config/prod.exs config/prod.exs
RUN mix local.hex --force && \ RUN mix deps.get && \
mix local.rebar --force && \
mix deps.get && \
mix deps.compile mix deps.compile
COPY ./lib ./lib COPY ./lib ./lib
COPY ./priv ./priv COPY ./priv ./priv

View file

@ -12,10 +12,10 @@
} }
/* Comment */ /* Comment */
.dark .highlight .err { /* .dark .highlight .err {
color: #a61717; color: #a61717;
background-color: #e3d2d2 background-color: #e3d2d2
} } */
/* Error */ /* Error */
.dark .highlight .g { .dark .highlight .g {

View file

@ -10,6 +10,8 @@ import Config
config :ash_hq, config :ash_hq,
ecto_repos: [AshHq.Repo] ecto_repos: [AshHq.Repo]
config :ash, allow_flow: true
config :ash_hq, ash_apis: [AshHq.Docs] config :ash_hq, ash_apis: [AshHq.Docs]
config :ash_hq, AshHq.Docs, config :ash_hq, AshHq.Docs,

View file

@ -3,12 +3,17 @@ defmodule AshHq.Docs.Preparations.LoadSearchData do
def prepare(query, _, _) do def prepare(query, _, _) do
query_string = Ash.Query.get_argument(query, :query) query_string = Ash.Query.get_argument(query, :query)
to_load = AshHq.Docs.Extensions.Search.load_for_search(query.resource)
if query_string do if query_string do
query query
|> Ash.Query.load(search_headline: [query: query_string]) |> Ash.Query.load(
search_headline: [query: query_string],
match_rank: [query: query_string],
name_matches: %{query: query_string, similarity: 0.7}
)
|> Ash.Query.load(to_load)
|> Ash.Query.sort(match_rank: {:asc, %{query: query_string}}) |> Ash.Query.sort(match_rank: {:asc, %{query: query_string}})
|> Ash.Query.load(match_rank: [query: query_string])
else else
query query
end end

View file

@ -174,6 +174,9 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
[ [
Transformer.build_entity!(Ash.Resource.Dsl, [:actions, :read], :prepare, Transformer.build_entity!(Ash.Resource.Dsl, [:actions, :read], :prepare,
preparation: AshHq.Docs.Preparations.LoadSearchData preparation: AshHq.Docs.Preparations.LoadSearchData
),
Transformer.build_entity!(Ash.Resource.Dsl, [:actions, :read], :prepare,
preparation: Ash.Resource.Preparation.Builtins.build(limit: 10)
) )
] ]
end end

View file

@ -0,0 +1,80 @@
defmodule AshHq.Docs.Search do
use Ash.Flow
flow do
api AshHq.Docs
argument :query, :string do
allow_nil? false
end
argument :library_versions, {:array, :uuid} do
allow_nil? false
end
returns :build_results
end
steps do
read :options, AshHq.Docs.Option, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
read :dsls, AshHq.Docs.Dsl, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
read :guides, AshHq.Docs.Guide, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
read :library_versions, AshHq.Docs.LibraryVersion, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
read :extensions, AshHq.Docs.Extension, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
read :functions, AshHq.Docs.Function, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
read :modules, AshHq.Docs.Module, :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end
custom :build_results, AshHq.Docs.Search.Steps.BuildResults do
input %{
dsls: result(:dsls),
options: result(:options),
guides: result(:guides),
library_versions: result(:library_versions),
extensions: result(:extensions),
functions: result(:functions),
modules: result(:modules)
}
end
end
end

View file

@ -0,0 +1,132 @@
defmodule AshHq.Docs.Search.Steps.BuildResults do
use Ash.Flow.Step
def run(input, _opts, _context) do
search_results =
input
|> Map.values()
|> List.flatten()
|> Enum.sort_by(
# false comes first, and we want all things where the name matches to go first
&{name_match_rank(&1), -&1.match_rank, Map.get(&1, :extension_order, -1),
Enum.count(Map.get(&1, :path, []))}
)
sort_rank =
search_results
|> Enum.with_index()
|> Map.new(fn {item, i} ->
{item.id, i}
end)
results =
search_results
|> Enum.group_by(fn
%{extension_type: type} ->
type
%AshHq.Docs.Function{module_name: module_name} ->
module_name
%AshHq.Docs.Module{name: name} ->
name
%AshHq.Docs.Guide{
library_version: %{version: version, library_display_name: library_display_name}
} ->
"#{library_display_name} #{version}"
%AshHq.Docs.Extension{
library_version: %{version: version, library_display_name: library_display_name}
} ->
"#{library_display_name} #{version}"
%AshHq.Docs.LibraryVersion{library_display_name: library_display_name, version: version} ->
"#{library_display_name} #{version}"
other ->
raise "Nothing matching #{inspect(other)}"
end)
|> Enum.sort_by(fn {_type, items} ->
items
|> Enum.map(&Map.get(sort_rank, &1.id))
|> Enum.min()
end)
|> Enum.map(fn {type, items} ->
{type, group_by_paths(items, sort_rank)}
end)
item_list = item_list(results)
{:ok, %{item_list: item_list, results: results}}
end
defp item_list(results) do
List.flatten(do_item_list(results))
end
defp do_item_list({_key, %{items: items, further: further}}) do
do_item_list(items) ++ do_item_list(further)
end
defp do_item_list(items) when is_list(items) do
Enum.map(items, &do_item_list/1)
end
defp do_item_list(item), do: item
defp group_by_paths(items, sort_rank) do
items
|> Enum.map(&{Map.get(&1, :path, []), &1})
|> do_group_by_paths(sort_rank)
end
defp do_group_by_paths(items, sort_rank, path_acc \\ []) do
{items_for_group, further} =
Enum.split_with(items, fn
{[], _} ->
true
_ ->
false
end)
further_items =
further
|> Enum.group_by(
fn {[next | _rest], _item} ->
next
end,
fn {[_next | rest], item} ->
{rest, item}
end
)
|> Enum.sort_by(fn {_nested, items} ->
items
|> Enum.map(&elem(&1, 1))
|> Enum.sort_by(&Map.get(sort_rank, &1.id))
end)
|> Enum.map(fn {nested, items} ->
{nested, do_group_by_paths(items, sort_rank, path_acc ++ [nested])}
end)
items =
items_for_group
|> Enum.map(&elem(&1, 1))
|> Enum.sort_by(&Map.get(sort_rank, &1.id))
%{path: path_acc, items: items, further: further_items}
end
defp name_match_rank(record) do
if record.name_matches do
-search_length(record)
else
0
end
end
defp search_length(%resource{} = record) do
String.length(Map.get(record, AshHq.Docs.Extensions.Search.doc_attribute(resource)))
end
end

View file

@ -32,9 +32,7 @@ defmodule AshHq.Docs.Dsl do
end end
actions do actions do
read :read do defaults [:read, :destroy]
primary? true
end
create :create do create :create do
argument :options, {:array, :map} argument :options, {:array, :map}

View file

@ -32,6 +32,8 @@ defmodule AshHq.Docs.Extension do
end end
actions do actions do
defaults [:read, :update, :destroy]
create :import do create :import do
argument :library_version, :uuid do argument :library_version, :uuid do
allow_nil? false allow_nil? false

View file

@ -20,7 +20,7 @@ defmodule AshHq.Docs.Function do
postgres do postgres do
table "functions" table "functions"
repo(AshHq.Repo) repo AshHq.Repo
references do references do
reference(:library_version, on_delete: :delete) reference(:library_version, on_delete: :delete)
@ -68,7 +68,10 @@ defmodule AshHq.Docs.Function do
end end
actions do actions do
defaults [:read, :update, :destroy]
create :create do create :create do
primary? true
argument :library_version, :uuid argument :library_version, :uuid
change manage_relationship(:library_version, type: :replace) change manage_relationship(:library_version, type: :replace)

View file

@ -0,0 +1,21 @@
defmodule AshHq.Docs.Guide.Changes.SetRoute do
use Ash.Resource.Change
def change(changeset, _, _) do
if !Ash.Changeset.get_attribute(changeset, :route) &&
(Ash.Changeset.changing_attribute?(:name) || Ash.Changeset.changing_attribute?(:category)) do
category = Ash.Changeset.get_attribute(changeset, :category)
name = Ash.Changeset.get_attribute(changeset, :name)
Ash.Changeset.change_attribute(changeset, :route, to_path(category) <> "/" <> to_path(name))
else
changeset
end
end
defp to_path(string) do
string
|> String.split(~r/\s/, trim: true)
|> Enum.join("-")
|> String.downcase()
end
end

View file

@ -9,13 +9,26 @@ defmodule AshHq.Docs.Guide do
search do search do
doc_attribute :text doc_attribute :text
load_for_search [:url_safe_name, library_version: [:library_name, :library_display_name]] load_for_search library_version: [:library_name, :library_display_name]
end end
code_interface do code_interface do
define_for AshHq.Docs define_for AshHq.Docs
end end
actions do
defaults [:read, :update, :destroy]
create :create do
primary? true
allow_nil_input [:route]
end
end
changes do
change AshHq.Docs.Guide.Changes.SetRoute
end
postgres do postgres do
repo AshHq.Repo repo AshHq.Repo
table "guides" table "guides"
@ -46,10 +59,15 @@ defmodule AshHq.Docs.Guide do
constraints trim?: false, allow_empty?: true constraints trim?: false, allow_empty?: true
writable? false writable? false
end end
end
calculations do attribute :category, :string do
calculate :url_safe_name, :string, expr(fragment("lower(replace(?, ' ', '-'))", name)) default "Guides"
allow_nil? false
end
attribute :route, :string do
allow_nil? false
end
end end
relationships do relationships do

View file

@ -16,9 +16,7 @@ defmodule AshHq.Docs.Library do
end end
actions do actions do
read :read do defaults [:read, :create, :update, :destroy]
primary? true
end
read :by_name do read :by_name do
argument :name, :string do argument :name, :string do

View file

@ -33,9 +33,7 @@ defmodule AshHq.Docs.LibraryVersion do
end end
actions do actions do
read :read do defaults [:read, :update, :destroy]
primary? true
end
read :by_version do read :by_version do
get? true get? true

View file

@ -49,7 +49,10 @@ defmodule AshHq.Docs.Module do
end end
actions do actions do
defaults [:read, :update, :destroy]
create :create do create :create do
primary? true
argument :functions, {:array, :map} argument :functions, {:array, :map}
argument :library_version, :uuid argument :library_version, :uuid

View file

@ -64,11 +64,10 @@ defmodule AshHq.Docs.Option do
end end
actions do actions do
read :read do defaults [:read, :update, :destroy]
primary? true
end
create :create do create :create do
primary? true
argument :library_version, :uuid argument :library_version, :uuid
argument :extension_id, :uuid do argument :extension_id, :uuid do

View file

@ -37,28 +37,28 @@ defmodule AshHqWeb.Components.DocSidebar do
<span class="font-light text-gray-500">{selected_version_name(library, @selected_versions)}</span> <span class="font-light text-gray-500">{selected_version_name(library, @selected_versions)}</span>
</LivePatch> </LivePatch>
{#if @library && @library_version && library.id == @library.id} {#if @library && @library_version && library.id == @library.id}
{#if !Enum.empty?(@library_version.guides)} {#for {category, guides} <- guides_by_category(@library_version.guides)}
<div class="ml-2 text-gray-500"> <div class="ml-2 text-gray-500">
Guides {category}
</div> </div>
{/if} {#for guide <- guides}
{#for guide <- @library_version.guides} <li class="ml-3">
<li class="ml-3"> <LivePatch
<LivePatch to={Routes.guide_link(
to={Routes.guide_link( library,
library, selected_version_name(library, @selected_versions),
selected_version_name(library, @selected_versions), guide.route
guide.url_safe_name )}
)} class={
class={ "flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700", "dark:bg-gray-600": @guide && @guide.id == guide.id
"dark:bg-gray-600": @guide && @guide.id == guide.id }
} >
> <Heroicons.Outline.BookOpenIcon class="h-4 w-4" />
<Heroicons.Outline.BookOpenIcon class="h-4 w-4" /> <span class="ml-3 mr-2">{guide.name}</span>
<span class="ml-3 mr-2">{guide.name}</span> </LivePatch>
</LivePatch> </li>
</li> {/for}
{/for} {/for}
{#if !Enum.empty?(@library_version.guides)} {#if !Enum.empty?(@library_version.guides)}
@ -192,6 +192,17 @@ defmodule AshHqWeb.Components.DocSidebar do
""" """
end end
defp guides_by_category(guides) do
guides
|> Enum.group_by(& &1.category)
|> Enum.sort_by(fn {category, _guides} ->
Enum.find_index(["Guides", "Concepts", "Info"], &(&1 == category)) || :infinity
end)
|> Enum.map(fn {category, guides} ->
{category, Enum.sort_by(guides, & &1.order)}
end)
end
defp selected_version_name(library, selected_versions) do defp selected_version_name(library, selected_versions) do
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil -> nil ->

View file

@ -213,71 +213,12 @@ defmodule AshHqWeb.Components.Search do
if socket.assigns[:search] in [nil, ""] || socket.assigns[:selected_versions] in [nil, %{}] do if socket.assigns[:search] in [nil, ""] || socket.assigns[:selected_versions] in [nil, %{}] do
assign(socket, :results, %{}) assign(socket, :results, %{})
else else
search_results = %{results: results, item_list: item_list} =
AshHq.Docs AshHq.Docs.Search.run!(
|> Ash.Api.resources() socket.assigns.search,
|> Enum.filter(fn resource -> Map.values(socket.assigns.selected_versions)
AshHq.Docs.Extensions.Search in Ash.Resource.Info.extensions(resource) &&
AshHq.Docs.Extensions.Search.type(resource) in socket.assigns.selected_types
end)
|> Enum.flat_map(fn resource ->
to_load = AshHq.Docs.Extensions.Search.load_for_search(resource)
resource.search!(socket.assigns.search, Map.values(socket.assigns.selected_versions),
query: Ash.Query.limit(resource, 25),
load:
List.wrap(to_load) ++
[name_matches: %{query: socket.assigns.search, similarity: 0.7}]
)
end)
|> Enum.sort_by(
# false comes first, and we want all things where the name matches to go first
&{name_match_rank(&1), -&1.match_rank, Map.get(&1, :extension_order, -1),
Enum.count(Map.get(&1, :path, []))}
) )
sort_rank =
search_results
|> Enum.with_index()
|> Map.new(fn {item, i} ->
{item.id, i}
end)
results =
search_results
|> Enum.group_by(fn
%{extension_type: type} ->
type
%AshHq.Docs.Function{module_name: module_name} ->
module_name
%AshHq.Docs.Module{name: name} ->
name
%AshHq.Docs.Guide{
library_version: %{version: version, library_display_name: library_display_name}
} ->
"#{library_display_name} #{version}"
%AshHq.Docs.Extension{
library_version: %{version: version, library_display_name: library_display_name}
} ->
"#{library_display_name} #{version}"
%AshHq.Docs.LibraryVersion{library_display_name: library_display_name, version: version} ->
"#{library_display_name} #{version}"
end)
|> Enum.sort_by(fn {_type, items} ->
items
|> Enum.map(&Map.get(sort_rank, &1.id))
|> Enum.min()
end)
|> Enum.map(fn {type, items} ->
{type, group_by_paths(items, sort_rank)}
end)
item_list = item_list(results)
selected_item = Enum.at(item_list, 0) selected_item = Enum.at(item_list, 0)
socket socket
@ -287,32 +228,6 @@ defmodule AshHqWeb.Components.Search do
end end
end end
defp name_match_rank(record) do
if record.name_matches do
-search_length(record)
else
0
end
end
defp search_length(%resource{} = record) do
String.length(Map.get(record, AshHq.Docs.Extensions.Search.doc_attribute(resource)))
end
defp item_list(results) do
List.flatten(do_item_list(results))
end
defp do_item_list({_key, %{items: items, further: further}}) do
do_item_list(items) ++ do_item_list(further)
end
defp do_item_list(items) when is_list(items) do
Enum.map(items, &do_item_list/1)
end
defp do_item_list(item), do: item
defp set_selected_item(socket, nil), do: socket defp set_selected_item(socket, nil), do: socket
defp set_selected_item(socket, selected_item) do defp set_selected_item(socket, selected_item) do
@ -320,47 +235,4 @@ defmodule AshHqWeb.Components.Search do
|> assign(:selected_item, selected_item) |> assign(:selected_item, selected_item)
|> push_event("js:scroll-to", %{id: selected_item.id, boundary_id: socket.assigns[:id]}) |> push_event("js:scroll-to", %{id: selected_item.id, boundary_id: socket.assigns[:id]})
end end
defp group_by_paths(items, sort_rank) do
items
|> Enum.map(&{Map.get(&1, :path, []), &1})
|> do_group_by_paths(sort_rank)
end
defp do_group_by_paths(items, sort_rank, path_acc \\ []) do
{items_for_group, further} =
Enum.split_with(items, fn
{[], _} ->
true
_ ->
false
end)
further_items =
further
|> Enum.group_by(
fn {[next | _rest], _item} ->
next
end,
fn {[_next | rest], item} ->
{rest, item}
end
)
|> Enum.sort_by(fn {_nested, items} ->
items
|> Enum.map(&elem(&1, 1))
|> Enum.sort_by(&Map.get(sort_rank, &1.id))
end)
|> Enum.map(fn {nested, items} ->
{nested, do_group_by_paths(items, sort_rank, path_acc ++ [nested])}
end)
items =
items_for_group
|> Enum.map(&elem(&1, 1))
|> Enum.sort_by(&Map.get(sort_rank, &1.id))
%{path: path_acc, items: items, further: further_items}
end
end end

View file

@ -29,7 +29,7 @@ defmodule AshHqWeb.Pages.Docs do
def render(assigns) do def render(assigns) do
~F""" ~F"""
<div class="grid content-start overflow-hidden"> <div class="grid content-start overflow-hidden">
<div class="lg:hidden flex flex-row justify-start space-x-12 items-center border-b border-t border-gray-600 py-3"> <div class="xl:hidden flex flex-row justify-start space-x-12 items-center border-b border-t border-gray-600 py-3">
<button class="dark:hover:text-gray-600" phx-click={show_sidebar()}> <button class="dark:hover:text-gray-600" phx-click={show_sidebar()}>
<Heroicons.Outline.MenuIcon class="w-8 h-8 ml-4" /> <Heroicons.Outline.MenuIcon class="w-8 h-8 ml-4" />
</button> </button>
@ -217,7 +217,7 @@ defmodule AshHqWeb.Pages.Docs do
guide = guide =
if socket.assigns[:params]["guide"] && socket.assigns.library_version do if socket.assigns[:params]["guide"] && socket.assigns.library_version do
Enum.find(socket.assigns.library_version.guides, fn guide -> Enum.find(socket.assigns.library_version.guides, fn guide ->
guide.url_safe_name == socket.assigns[:params]["guide"] guide.route == Enum.join(socket.assigns[:params]["guide"], "/")
end) end)
end end

View file

@ -21,7 +21,7 @@ defmodule AshHqWeb.Router do
live_session :main, root_layout: {AshHqWeb.LayoutView, "root.html"} do live_session :main, root_layout: {AshHqWeb.LayoutView, "root.html"} do
live "/", AppViewLive, :home live "/", AppViewLive, :home
live "/docs/", AppViewLive, :docs_dsl live "/docs/", AppViewLive, :docs_dsl
live "/docs/guides/:library/:version/:guide", AppViewLive, :docs_dsl live "/docs/guides/:library/:version/*guide", AppViewLive, :docs_dsl
live "/docs/dsl/:library", AppViewLive, :docs_dsl live "/docs/dsl/:library", AppViewLive, :docs_dsl
live "/docs/dsl/:library/:version", AppViewLive, :docs_dsl live "/docs/dsl/:library/:version", AppViewLive, :docs_dsl
live "/docs/dsl/:library/:version/:extension", AppViewLive, :docs_dsl live "/docs/dsl/:library/:version/:extension", AppViewLive, :docs_dsl

View file

@ -1,6 +1,6 @@
defmodule AshHqWeb.Routes do defmodule AshHqWeb.Routes do
def guide_link(library, version, guide) do def guide_link(library, version, route) do
"/docs/guides/#{sanitize_name(library.name)}/#{sanitize_name(version)}/#{sanitize_name(guide)}" "/docs/guides/#{sanitize_name(library.name)}/#{sanitize_name(version)}/#{route}"
end end
def library_link(library, nil) do def library_link(library, nil) do
@ -49,10 +49,10 @@ defmodule AshHqWeb.Routes do
end end
def doc_link(%AshHq.Docs.Guide{ def doc_link(%AshHq.Docs.Guide{
url_safe_name: url_safe_name, route: route,
library_version: %{library_name: library_name, version: version} library_version: %{library_name: library_name, version: version}
}) do }) do
"/docs/guides/#{sanitize_name(library_name)}/#{sanitize_name(version)}/#{url_safe_name}" "/docs/guides/#{sanitize_name(library_name)}/#{sanitize_name(version)}/#{route}"
end end
def doc_link(%AshHq.Docs.LibraryVersion{library_name: library_name, version: version}) do def doc_link(%AshHq.Docs.LibraryVersion{library_name: library_name, version: version}) do

View file

@ -56,22 +56,24 @@ defmodule AshHqWeb.AppViewLive do
<div class="flex flex-row align-middle items-center space-x-2"> <div class="flex flex-row align-middle items-center space-x-2">
<a <a
href="/docs/guides/ash/main/getting-started" href="/docs/guides/ash/main/getting-started"
target="_blank"
class="dark:text-gray-400 dark:hover:text-gray-200 hover:text-gray-600" class="dark:text-gray-400 dark:hover:text-gray-200 hover:text-gray-600"
>Get Started</a> >Get Started</a>
<div>|</div> <div>|</div>
<a <a
href="/docs/guides/ash/main/overview" href="/docs/guides/ash/main/overview"
target="_blank"
class="dark:text-gray-400 dark:hover:text-gray-200 hover:text-gray-600" class="dark:text-gray-400 dark:hover:text-gray-200 hover:text-gray-600"
>Docs</a> >Docs</a>
<div>|</div> <div>|</div>
<a href="https://github.com/ash-project"> <a href="https://github.com/ash-project" target="_blank">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 dark:fill-gray-400 dark:hover:fill-gray-200 hover:fill-gray-600" class="w-6 h-6 dark:fill-gray-400 dark:hover:fill-gray-200 hover:fill-gray-600"
viewBox="0 0 24 24" viewBox="0 0 24 24"
><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /></svg> ><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /></svg>
</a> </a>
<a href="https://discord.gg/D7FNG2q"> <a href="https://discord.gg/D7FNG2q" target="_blank">
<svg <svg
class="w-6 h-6 fill-black dark:fill-gray-400 dark:hover:fill-gray-200 hover:fill-gray-600" class="w-6 h-6 fill-black dark:fill-gray-400 dark:hover:fill-gray-200 hover:fill-gray-600"
viewBox="0 0 71 55" viewBox="0 0 71 55"
@ -88,7 +90,7 @@ defmodule AshHqWeb.AppViewLive do
</defs> </defs>
</svg> </svg>
</a> </a>
<a href="https://twitter.com/ashframework"> <a href="https://twitter.com/ashframework" target="_blank">
<svg <svg
class="w-6 h-6 dark:fill-gray-400 dark:hover:fill-gray-200 hover:fill-gray-600" class="w-6 h-6 dark:fill-gray-400 dark:hover:fill-gray-200 hover:fill-gray-600"
version="1.1" viewBox="0 0 248 204" style="enable-background:new 0 0 248 204;" > version="1.1" viewBox="0 0 248 204" style="enable-background:new 0 0 248 204;" >
@ -228,7 +230,7 @@ defmodule AshHqWeb.AppViewLive do
AshHq.Docs.load!(version, AshHq.Docs.load!(version,
extensions: extensions_query, extensions: extensions_query,
guides: :url_safe_name, guides: [],
modules: modules_query modules: modules_query
) )
else else

View file

@ -1,5 +1,5 @@
%{ %{
"ash": {:git, "https://github.com/ash-project/ash.git", "59075efa0390f827471fc92437faff2551f07755", []}, "ash": {:git, "https://github.com/ash-project/ash.git", "a012287c3b621ac15d0d7b00735c6ebc9c477163", []},
"ash_phoenix": {:git, "https://github.com/ash-project/ash_phoenix.git", "9de0ff87780bd16d06dcde1bac6a8e041a31659d", []}, "ash_phoenix": {:git, "https://github.com/ash-project/ash_phoenix.git", "9de0ff87780bd16d06dcde1bac6a8e041a31659d", []},
"ash_postgres": {:git, "https://github.com/ash-project/ash_postgres.git", "48c6142040d389272d235b7e62d354a3a37a825e", []}, "ash_postgres": {:git, "https://github.com/ash-project/ash_postgres.git", "48c6142040d389272d235b7e62d354a3a37a825e", []},
"castore": {:hex, :castore, "0.1.16", "2675f717adc700475345c5512c381ef9273eb5df26bdd3f8c13e2636cf4cc175", [:mix], [], "hexpm", "28ed2c43d83b5c25d35c51bc0abf229ac51359c170cba76171a462ced2e4b651"}, "castore": {:hex, :castore, "0.1.16", "2675f717adc700475345c5512c381ef9273eb5df26bdd3f8c13e2636cf4cc175", [:mix], [], "hexpm", "28ed2c43d83b5c25d35c51bc0abf229ac51359c170cba76171a462ced2e4b651"},

View file

@ -0,0 +1,23 @@
defmodule AshHq.Repo.Migrations.MigrateResources4 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
alter table(:guides) do
add :category, :text, default: "Guides"
add :route, :text, null: false
end
end
def down do
alter table(:guides) do
remove :route
remove :category
end
end
end

View file

@ -0,0 +1,110 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "order",
"type": "bigint"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": false,
"default": "\"\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "text",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "text_html",
"type": "text"
},
{
"allow_nil?": true,
"default": "\"Guides\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "category",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "route",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "guides_library_version_id_fkey",
"on_delete": "delete",
"on_update": null,
"table": "library_versions"
},
"size": null,
"source": "library_version_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "23DE1EB8A453CE613602880677BF2962E48EE5B14AA8104CAE812E9EABE6963E",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshHq.Repo",
"table": "guides"
}