improvement: formatting & various other improvements

This commit is contained in:
Zach Daniel 2022-12-15 03:28:03 -05:00
parent 8ca9084efd
commit dabbf8b3b6
34 changed files with 1198 additions and 530 deletions

View file

@ -16,7 +16,7 @@
"priv/scripts/**/*.{ex,exs}",
"{config,lib,test}/**/*.{ex,exs}"
],
plugins: [Surface.Formatter.Plugin],
plugins: [Spark.Formatter, Surface.Formatter.Plugin],
subdirectories: ["priv/*/migrations"],
locals_without_parens: [
auto_sanitize_name_attribute?: 1,

View file

@ -18,6 +18,13 @@ config :ash_hq, AshHq.Repo,
timeout: :timer.minutes(10),
ownership_timeout: :timer.minutes(10)
config :spark, :formatter,
remove_parens?: true,
"AshHq.Resource": [
type: Ash.Resource
],
"Ash.Flow": []
# Configures the endpoint
config :ash_hq, AshHqWeb.Endpoint,
url: [host: "localhost"],

View file

@ -8,6 +8,77 @@ defmodule AshHq.Accounts.User do
alias AshHq.Accounts.Preparations, warn: false
import Ash.Changeset
postgres do
table "users"
repo AshHq.Repo
end
policies do
policy action(:read) do
authorize_if expr(id == ^actor(:id))
end
policy action(:change_password) do
description "Allow the user to change their own password"
authorize_if expr(id == ^actor(:id))
end
policy action(:deliver_update_email_instructions) do
description "Allow a user to request an update their own email"
authorize_if expr(id == ^actor(:id))
end
policy action(:by_email_and_password) do
description "Allow looking up by email/password combo (logging in) for unauthenticated users only."
forbid_if actor_present()
authorize_if always()
end
policy action(:deliver_user_confirmation_instructions) do
description "A logged in user can request email confirmation for themselves to be sent again"
authorize_if expr(id == ^actor(:id))
end
policy action(:update_merch_settings) do
description "A logged in user can update their merch settings"
authorize_if expr(id == ^actor(:id))
end
policy action(:register) do
description "Allow looking up by email/password combo (logging in) for unauthenticated users only."
forbid_if actor_present()
authorize_if always()
end
end
attributes do
uuid_primary_key :id
attribute :email, :ci_string,
allow_nil?: false,
constraints: [
max_length: 160
]
attribute :confirmed_at, :utc_datetime_usec
attribute :hashed_password, :string, private?: true
attribute :encrypted_name, AshHq.Types.EncryptedString
attribute :encrypted_address, AshHq.Types.EncryptedString
attribute :shirt_size, :string
create_timestamp :created_at
update_timestamp :updated_at
end
relationships do
has_one :token, AshHq.Accounts.UserToken do
destination_attribute :user_id
private? true
end
end
actions do
defaults [:read]
@ -177,93 +248,22 @@ defmodule AshHq.Accounts.User do
end
end
attributes do
uuid_primary_key :id
attribute :email, :ci_string,
allow_nil?: false,
constraints: [
max_length: 160
]
attribute :confirmed_at, :utc_datetime_usec
attribute :hashed_password, :string, private?: true
attribute :encrypted_name, AshHq.Types.EncryptedString
attribute :encrypted_address, AshHq.Types.EncryptedString
attribute :shirt_size, :string
create_timestamp :created_at
update_timestamp :updated_at
end
calculations do
calculate :address, :string, decrypt(:encrypted_address)
calculate :name, :string, decrypt(:encrypted_name)
end
identities do
identity :unique_email, [:email]
end
postgres do
table "users"
repo AshHq.Repo
end
policies do
policy action(:read) do
authorize_if expr(id == ^actor(:id))
end
policy action(:change_password) do
description "Allow the user to change their own password"
authorize_if expr(id == ^actor(:id))
end
policy action(:deliver_update_email_instructions) do
description "Allow a user to request an update their own email"
authorize_if expr(id == ^actor(:id))
end
policy action(:by_email_and_password) do
description "Allow looking up by email/password combo (logging in) for unauthenticated users only."
forbid_if actor_present()
authorize_if always()
end
policy action(:deliver_user_confirmation_instructions) do
description "A logged in user can request email confirmation for themselves to be sent again"
authorize_if expr(id == ^actor(:id))
end
policy action(:update_merch_settings) do
description "A logged in user can update their merch settings"
authorize_if expr(id == ^actor(:id))
end
policy action(:register) do
description "Allow looking up by email/password combo (logging in) for unauthenticated users only."
forbid_if actor_present()
authorize_if always()
end
end
relationships do
has_one :token, AshHq.Accounts.UserToken do
destination_attribute :user_id
private? true
end
end
resource do
description """
Represents the user of a system.
"""
end
identities do
identity :unique_email, [:email]
end
validations do
validate match(:email, ~r/^[^\s]+@[^\s]+$/), message: "must have the @ sign and no spaces"
end
calculations do
calculate :address, :string, decrypt(:encrypted_address)
calculate :name, :string, decrypt(:encrypted_name)
end
end

View file

@ -6,6 +6,40 @@ defmodule AshHq.Accounts.UserToken do
notifiers: [AshHq.Accounts.EmailNotifier],
authorizers: [Ash.Policy.Authorizer]
postgres do
table "user_tokens"
repo AshHq.Repo
references do
reference :user, on_delete: :delete, on_update: :update
end
end
policies do
policy always() do
description """
There are currently no usages of user tokens resource that should be publicly
accessible, they should all be using authorize?: false.
"""
forbid_if always()
end
end
attributes do
uuid_primary_key :id
attribute :token, :binary
attribute :context, :string
attribute :sent_to, :string
create_timestamp :created_at
end
relationships do
belongs_to :user, AshHq.Accounts.User
end
actions do
defaults [:read]
@ -45,47 +79,13 @@ defmodule AshHq.Accounts.UserToken do
end
end
attributes do
uuid_primary_key :id
attribute :token, :binary
attribute :context, :string
attribute :sent_to, :string
create_timestamp :created_at
end
identities do
identity :token_context, [:context, :token]
end
postgres do
table "user_tokens"
repo(AshHq.Repo)
references do
reference(:user, on_delete: :delete, on_update: :update)
end
end
policies do
policy always() do
description """
There are currently no usages of user tokens resource that should be publicly
accessible, they should all be using authorize?: false.
"""
forbid_if always()
end
end
relationships do
belongs_to :user, AshHq.Accounts.User
end
resource do
description """
Represents a token allowing a user to log in, reset their password, or confirm their email.
"""
end
identities do
identity :token_context, [:context, :token]
end
end

View file

@ -7,6 +7,10 @@ defmodule AshHq.Blog.Post do
require Ash.Query
render_markdown do
render_attributes body: :body_html
end
admin do
table_columns [:slug, :title, :state, :created_at, :id]
@ -17,6 +21,50 @@ defmodule AshHq.Blog.Post do
end
end
attributes do
uuid_primary_key :id
attribute :tag_line, :string do
allow_nil? false
constraints max_length: 250
end
attribute :tag_names, {:array, :string} do
constraints items: [
match: ~r/^[a-zA-Z]*$/
]
end
attribute :author, :string do
allow_nil? false
end
attribute :body_html, :string do
writable? false
end
timestamps()
end
relationships do
has_many :tags, AshHq.Blog.Tag do
manual fn posts, %{query: query} ->
all_tags = Enum.flat_map(posts, & &1.tag_names)
tags =
query
|> Ash.Query.unset([:limit, :offset])
|> Ash.Query.filter(name in ^all_tags)
|> AshHq.Blog.read!()
{:ok,
Map.new(posts, fn post ->
{post.id, Enum.filter(tags, &(&1.name in post.tag_names))}
end)}
end
end
end
actions do
defaults [:create, :read, :update]
@ -50,35 +98,6 @@ defmodule AshHq.Blog.Post do
define :by_slug, args: [:slug]
end
render_markdown do
render_attributes body: :body_html
end
attributes do
uuid_primary_key :id
attribute :tag_line, :string do
allow_nil? false
constraints max_length: 250
end
attribute :tag_names, {:array, :string} do
constraints items: [
match: ~r/^[a-zA-Z]*$/
]
end
attribute :author, :string do
allow_nil? false
end
attribute :body_html, :string do
writable? false
end
timestamps()
end
changes do
change fn changeset, _ ->
Ash.Changeset.after_action(changeset, fn _, %{tag_names: tag_names} = record ->
@ -107,23 +126,4 @@ defmodule AshHq.Blog.Post do
end,
on: [:create, :update]
end
relationships do
has_many :tags, AshHq.Blog.Tag do
manual fn posts, %{query: query} ->
all_tags = Enum.flat_map(posts, & &1.tag_names)
tags =
query
|> Ash.Query.unset([:limit, :offset])
|> Ash.Query.filter(name in ^all_tags)
|> AshHq.Blog.read!()
{:ok,
Map.new(posts, fn post ->
{post.id, Enum.filter(tags, &(&1.name in post.tag_names))}
end)}
end
end
end
end

View file

@ -10,6 +10,13 @@ defmodule AshHq.Blog.Tag do
columns [:name]
end
attributes do
attribute :name, :string do
allow_nil? false
primary_key? true
end
end
actions do
defaults [:create, :read, :update, :destroy]
@ -19,13 +26,6 @@ defmodule AshHq.Blog.Tag do
end
end
attributes do
attribute :name, :string do
allow_nil? false
primary_key? true
end
end
code_interface do
define_for AshHq.Blog
define :upsert, args: [:name]

View file

@ -78,6 +78,9 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Highlighter do
try_parse_multi(rest)
end
end
rescue
_ ->
~s[<code class="inline">#{code}</code>]
end
defp highlight_code_block(_full_block, lang, code) do

View file

@ -37,7 +37,7 @@ defmodule AshHq.Docs.Extensions.Search do
"Whether or not the name attribute will be sanitized by default. If not, you should have a change on the resource that sets it."
],
show_docs_on: [
type: :atom,
type: {:or, [:atom, {:list, :atom}]},
doc:
"An attribute/calculation/aggregate that should map to a sanitized name that should match to signal that docs should be loaded"
],
@ -124,6 +124,7 @@ defmodule AshHq.Docs.Extensions.Search do
:show_docs_on,
sanitized_name_attribute(resource)
)
|> List.wrap()
end
def library_version_attribute(resource) do

View file

@ -35,7 +35,9 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
table: Transformer.get_option(dsl_state, [:postgres], :table),
sanitized_name_attribute: sanitized_name_attribute,
show_docs_on:
Transformer.get_option(dsl_state, [:search], :show_docs_on) || sanitized_name_attribute
List.wrap(
Transformer.get_option(dsl_state, [:search], :show_docs_on) || sanitized_name_attribute
)
}
{:ok,
@ -64,6 +66,15 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
name = :"#{dest}_for"
type = Ash.Resource.Info.attribute(dsl_state, dest).type
expr =
Enum.reduce(config.show_docs_on, nil, fn attr, expr ->
if expr do
expr(^expr or ^ref(attr) == ^arg(:for))
else
expr(^ref(attr) == ^arg(:for))
end
end)
dsl_state
|> Transformer.add_entity(
[:calculations],
@ -74,7 +85,7 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
arguments: [html_for_argument()],
calculation:
Ash.Query.expr(
if ^ref(config.show_docs_on) == ^arg(:for) do
if ^expr do
^ref(dest)
else
nil
@ -97,6 +108,15 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
config.doc_attribute
end
expr =
Enum.reduce(config.show_docs_on, nil, fn attr, expr ->
if expr do
expr(^expr or ^ref(attr) == ^arg(:for))
else
expr(^ref(attr) == ^arg(:for))
end
end)
dsl_state
|> Transformer.add_entity(
[:calculations],
@ -107,7 +127,7 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
arguments: [html_for_argument()],
calculation:
Ash.Query.expr(
if ^ref(config.show_docs_on) == ^arg(:for) do
if ^expr do
^ref(html_for_attribute)
else
nil

View file

@ -5,12 +5,13 @@ defmodule AshHq.Docs.Dsl do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "An entity or section in an Ash DSL"
end
postgres do
table "dsls"
repo AshHq.Repo
render_markdown do
render_attributes doc: :doc_html
references do
reference :library_version, on_delete: :delete
end
end
search do
@ -29,43 +30,8 @@ defmodule AshHq.Docs.Dsl do
use_path_for_name? true
end
postgres do
table "dsls"
repo AshHq.Repo
references do
reference :library_version, on_delete: :delete
end
end
code_interface do
define_for AshHq.Docs
define :read
end
actions do
defaults [:destroy]
read :read do
primary? true
pagination offset?: true, countable: true, default_limit: 25, required?: false
end
create :create do
primary? true
argument :options, {:array, :map}
argument :library_version, :uuid
argument :extension_id, :uuid do
allow_nil? false
end
change {AshHq.Docs.Changes.AddArgToRelationship, arg: :extension_id, rel: :options}
change {AshHq.Docs.Changes.AddArgToRelationship, arg: :library_version, rel: :options}
change manage_relationship(:options, type: :direct_control)
change manage_relationship(:library_version, type: :append_and_remove)
end
render_markdown do
render_attributes doc: :doc_html
end
attributes do
@ -108,15 +74,6 @@ defmodule AshHq.Docs.Dsl do
timestamps()
end
aggregates do
first :extension_type, :extension, :type
first :extension_order, :extension, :order
first :extension_name, :extension, :name
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
@ -130,4 +87,47 @@ defmodule AshHq.Docs.Dsl do
has_many :options, AshHq.Docs.Option
has_many :dsls, __MODULE__
end
actions do
defaults [:destroy]
read :read do
primary? true
pagination offset?: true, countable: true, default_limit: 25, required?: false
end
create :create do
primary? true
argument :options, {:array, :map}
argument :library_version, :uuid
argument :extension_id, :uuid do
allow_nil? false
end
change {AshHq.Docs.Changes.AddArgToRelationship, arg: :extension_id, rel: :options}
change {AshHq.Docs.Changes.AddArgToRelationship, arg: :library_version, rel: :options}
change manage_relationship(:options, type: :direct_control)
change manage_relationship(:library_version, type: :append_and_remove)
end
end
code_interface do
define_for AshHq.Docs
define :read
end
resource do
description "An entity or section in an Ash DSL"
end
aggregates do
first :extension_type, :extension, :type
first :extension_order, :extension, :order
first :extension_name, :extension, :name
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
end

View file

@ -5,19 +5,6 @@ defmodule AshHq.Docs.Extension do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "An Ash DSL extension."
end
render_markdown do
render_attributes doc: :doc_html
end
search do
doc_attribute :doc
load_for_search library_version: [:library_display_name, :library_name]
end
postgres do
table "extensions"
repo AshHq.Repo
@ -27,40 +14,13 @@ defmodule AshHq.Docs.Extension do
end
end
identities do
identity :unique_name_by_library_version, [:name, :library_version_id]
search do
doc_attribute :doc
load_for_search library_version: [:library_display_name, :library_name]
end
code_interface do
define_for AshHq.Docs
define :destroy
end
actions do
defaults [:update, :destroy]
read :read do
primary? true
pagination offset?: true, countable: true, default_limit: 25, required?: false
end
create :create do
primary? true
argument :library_version, :uuid do
allow_nil? false
end
argument :dsls, {:array, :map}
change manage_relationship(:library_version, type: :append_and_remove)
change {AshHq.Docs.Changes.AddArgToRelationship, arg: :library_version, rel: :dsls}
change {AshHq.Docs.Changes.AddArgToRelationship,
attr: :id, arg: :extension_id, rel: :dsls, generate: &Ash.UUID.generate/0}
change manage_relationship(:dsls, type: :create)
end
render_markdown do
render_attributes doc: :doc_html
end
attributes do
@ -106,4 +66,44 @@ defmodule AshHq.Docs.Extension do
has_many :dsls, AshHq.Docs.Dsl
has_many :options, AshHq.Docs.Option
end
actions do
defaults [:update, :destroy]
read :read do
primary? true
pagination offset?: true, countable: true, default_limit: 25, required?: false
end
create :create do
primary? true
argument :library_version, :uuid do
allow_nil? false
end
argument :dsls, {:array, :map}
change manage_relationship(:library_version, type: :append_and_remove)
change {AshHq.Docs.Changes.AddArgToRelationship, arg: :library_version, rel: :dsls}
change {AshHq.Docs.Changes.AddArgToRelationship,
attr: :id, arg: :extension_id, rel: :dsls, generate: &Ash.UUID.generate/0}
change manage_relationship(:dsls, type: :create)
end
end
code_interface do
define_for AshHq.Docs
define :destroy
end
resource do
description "An Ash DSL extension."
end
identities do
identity :unique_name_by_library_version, [:name, :library_version_id]
end
end

View file

@ -5,13 +5,13 @@ defmodule AshHq.Docs.Function do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "A function in a module exposed by an Ash library"
end
postgres do
table "functions"
repo AshHq.Repo
render_markdown do
render_attributes doc: :doc_html, heads: :heads_html
header_ids? false
references do
reference :library_version, on_delete: :delete
end
end
search do
@ -29,13 +29,9 @@ defmodule AshHq.Docs.Function do
show_docs_on :module_sanitized_name
end
postgres do
table "functions"
repo AshHq.Repo
references do
reference :library_version, on_delete: :delete
end
render_markdown do
render_attributes doc: :doc_html, heads: :heads_html
header_ids? false
end
attributes do
@ -83,8 +79,14 @@ defmodule AshHq.Docs.Function do
timestamps()
end
code_interface do
define_for AshHq.Docs
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
belongs_to :module, AshHq.Docs.Module do
allow_nil? true
end
end
actions do
@ -103,6 +105,14 @@ defmodule AshHq.Docs.Function do
end
end
code_interface do
define_for AshHq.Docs
end
resource do
description "A function in a module exposed by an Ash library"
end
aggregates do
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
@ -110,14 +120,4 @@ defmodule AshHq.Docs.Function do
first :module_name, :module, :name
first :module_sanitized_name, :module, :sanitized_name
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
belongs_to :module, AshHq.Docs.Module do
allow_nil? true
end
end
end

View file

@ -9,9 +9,28 @@ defmodule AshHq.Docs.Guide do
AshAdmin.Resource
]
postgres do
repo AshHq.Repo
table "guides"
references do
reference :library_version, on_delete: :delete
end
end
search do
doc_attribute :text
show_docs_on [:sanitized_name, :sanitized_route]
type "Guides"
load_for_search library_version: [:library_name, :library_display_name]
end
render_markdown do
render_attributes text: :text_html
end
graphql do
type :guide
# foo
queries do
list :list_guides, :read_for_version
@ -26,57 +45,6 @@ defmodule AshHq.Docs.Guide do
end
end
resource do
description "Represents a markdown guide exposed by a library"
end
render_markdown do
render_attributes text: :text_html
end
search do
doc_attribute :text
type "Guides"
load_for_search library_version: [:library_name, :library_display_name]
end
code_interface do
define_for AshHq.Docs
end
actions do
defaults [:create, :update, :destroy]
read :read do
primary? true
pagination offset?: true, countable: true, default_limit: 25, required?: false
end
read :read_for_version do
argument :library_versions, {:array, :uuid} do
allow_nil? false
constraints max_length: 20, min_length: 1
end
pagination offset?: true, countable: true, default_limit: 25, required?: false
filter expr(library_version.id in ^arg(:library_versions))
end
end
changes do
change AshHq.Docs.Guide.Changes.SetRoute
end
postgres do
repo AshHq.Repo
table "guides"
references do
reference :library_version, on_delete: :delete
end
end
attributes do
uuid_primary_key :id
@ -108,6 +76,11 @@ defmodule AshHq.Docs.Guide do
allow_nil? false
end
attribute :sanitized_route, :string do
allow_nil? false
writable? false
end
attribute :default, :boolean do
default false
allow_nil? false
@ -121,4 +94,50 @@ defmodule AshHq.Docs.Guide do
allow_nil? true
end
end
actions do
defaults [:create, :update, :destroy]
read :read do
primary? true
pagination offset?: true, countable: true, default_limit: 25, required?: false
end
read :read_for_version do
argument :library_versions, {:array, :uuid} do
allow_nil? false
constraints max_length: 20, min_length: 1
end
pagination offset?: true, countable: true, default_limit: 25, required?: false
filter expr(library_version.id in ^arg(:library_versions))
end
end
code_interface do
define_for AshHq.Docs
end
resource do
description "Represents a markdown guide exposed by a library"
end
changes do
change AshHq.Docs.Guide.Changes.SetRoute
change fn changeset, _ ->
if Ash.Changeset.changing_attribute?(changeset, :route) do
route = Ash.Changeset.get_attribute(changeset, :route)
Ash.Changeset.force_change_attribute(
changeset,
:sanitized_route,
AshHqWeb.DocRoutes.sanitize_name(route, true)
)
else
changeset
end
end
end
end

View file

@ -3,40 +3,11 @@ defmodule AshHq.Docs.Library do
use AshHq.Resource,
data_layer: AshPostgres.DataLayer
resource do
description "Represents a library that will be imported into AshHq"
end
identities do
identity :unique_order, [:order]
identity :unique_name, [:name]
end
postgres do
table "libraries"
repo AshHq.Repo
end
code_interface do
define_for AshHq.Docs
define :read
define :by_name, args: [:name], get?: true
define :create
end
actions do
defaults [:read, :create, :update, :destroy]
read :by_name do
argument :name, :string do
allow_nil? false
end
filter expr(name == ^arg(:name))
end
end
attributes do
uuid_primary_key :id
@ -54,16 +25,50 @@ defmodule AshHq.Docs.Library do
attribute :description, :string
attribute :repo_org, :string do
allow_nil? false
default "ash-project"
end
timestamps()
end
relationships do
has_many :versions, AshHq.Docs.LibraryVersion
end
actions do
defaults [:read, :create, :update, :destroy]
read :by_name do
argument :name, :string do
allow_nil? false
end
filter expr(name == ^arg(:name))
end
end
code_interface do
define_for AshHq.Docs
define :read
define :by_name, args: [:name], get?: true
define :create
end
resource do
description "Represents a library that will be imported into AshHq"
end
identities do
identity :unique_order, [:order]
identity :unique_name, [:name]
end
aggregates do
first :latest_version, :versions, :version do
sort version: :desc
end
end
relationships do
has_many :versions, AshHq.Docs.LibraryVersion
end
end

View file

@ -5,8 +5,9 @@ defmodule AshHq.Docs.LibraryVersion do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "Represents a version of a library that has been imported."
postgres do
table "library_versions"
repo AshHq.Repo
end
search do
@ -15,21 +16,25 @@ defmodule AshHq.Docs.LibraryVersion do
load_for_search [:library_name, :library_display_name]
end
postgres do
table "library_versions"
repo AshHq.Repo
attributes do
uuid_primary_key :id
attribute :version, :string do
allow_nil? false
end
timestamps()
end
identities do
identity :unique_version_for_library, [:version, :library_id]
end
relationships do
belongs_to :library, AshHq.Docs.Library do
allow_nil? true
end
code_interface do
define_for AshHq.Docs
define :build, args: [:library, :version]
define :defined_for, args: [:library, :versions]
define :by_version, args: [:library, :version]
define :destroy
has_many :extensions, AshHq.Docs.Extension
has_many :guides, AshHq.Docs.Guide
has_many :modules, AshHq.Docs.Module
has_many :mix_tasks, AshHq.Docs.MixTask
end
actions do
@ -109,39 +114,34 @@ defmodule AshHq.Docs.LibraryVersion do
end
end
code_interface do
define_for AshHq.Docs
define :build, args: [:library, :version]
define :defined_for, args: [:library, :versions]
define :by_version, args: [:library, :version]
define :destroy
end
resource do
description "Represents a version of a library that has been imported."
end
identities do
identity :unique_version_for_library, [:version, :library_id]
end
preparations do
prepare AshHq.Docs.LibraryVersion.Preparations.SortBySortableVersionInstead
end
aggregates do
first :library_name, :library, :name
first :library_display_name, :library, :display_name
end
attributes do
uuid_primary_key :id
attribute :version, :string do
allow_nil? false
end
timestamps()
end
calculations do
calculate :sortable_version,
{:array, :string},
expr(fragment("string_to_array(?, '.')", version))
end
preparations do
prepare AshHq.Docs.LibraryVersion.Preparations.SortBySortableVersionInstead
end
relationships do
belongs_to :library, AshHq.Docs.Library do
allow_nil? true
end
has_many :extensions, AshHq.Docs.Extension
has_many :guides, AshHq.Docs.Guide
has_many :modules, AshHq.Docs.Module
has_many :mix_tasks, AshHq.Docs.MixTask
end
end

View file

@ -5,12 +5,13 @@ defmodule AshHq.Docs.MixTask do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "Represents a mix task that has been exposed by a library"
end
postgres do
table "mix_tasks"
repo AshHq.Repo
render_markdown do
render_attributes doc: :doc_html
references do
reference :library_version, on_delete: :delete
end
end
search do
@ -27,13 +28,8 @@ defmodule AshHq.Docs.MixTask do
type "Mix Tasks"
end
postgres do
table "mix_tasks"
repo AshHq.Repo
references do
reference :library_version, on_delete: :delete
end
render_markdown do
render_attributes doc: :doc_html
end
attributes do
@ -68,6 +64,12 @@ defmodule AshHq.Docs.MixTask do
timestamps()
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
end
actions do
defaults [:update, :destroy]
@ -88,15 +90,13 @@ defmodule AshHq.Docs.MixTask do
define_for AshHq.Docs
end
resource do
description "Represents a mix task that has been exposed by a library"
end
aggregates do
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
end
end

View file

@ -5,12 +5,13 @@ defmodule AshHq.Docs.Module do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "Represents a module that has been exposed by a library"
end
postgres do
table "modules"
repo AshHq.Repo
render_markdown do
render_attributes doc: :doc_html
references do
reference :library_version, on_delete: :delete
end
end
search do
@ -25,13 +26,8 @@ defmodule AshHq.Docs.Module do
type "Code"
end
postgres do
table "modules"
repo AshHq.Repo
references do
reference :library_version, on_delete: :delete
end
render_markdown do
render_attributes doc: :doc_html
end
attributes do
@ -66,6 +62,14 @@ defmodule AshHq.Docs.Module do
timestamps()
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
has_many :functions, AshHq.Docs.Function
end
actions do
defaults [:update, :destroy]
@ -89,17 +93,13 @@ defmodule AshHq.Docs.Module do
define_for AshHq.Docs
end
resource do
description "Represents a module that has been exposed by a library"
end
aggregates do
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
has_many :functions, AshHq.Docs.Function
end
end

View file

@ -5,12 +5,13 @@ defmodule AshHq.Docs.Option do
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
resource do
description "Represents an option on a DSL section or entity"
end
postgres do
table "options"
repo AshHq.Repo
render_markdown do
render_attributes doc: :doc_html
references do
reference :library_version, on_delete: :delete
end
end
search do
@ -31,18 +32,8 @@ defmodule AshHq.Docs.Option do
show_docs_on :dsl_sanitized_path
end
postgres do
table "options"
repo AshHq.Repo
references do
reference :library_version, on_delete: :delete
end
end
code_interface do
define_for AshHq.Docs
define :read
render_markdown do
render_attributes doc: :doc_html
end
attributes do
@ -85,6 +76,20 @@ defmodule AshHq.Docs.Option do
timestamps()
end
relationships do
belongs_to :dsl, AshHq.Docs.Dsl do
allow_nil? true
end
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
belongs_to :extension, AshHq.Docs.Extension do
allow_nil? true
end
end
actions do
defaults [:update, :destroy]
@ -106,6 +111,15 @@ defmodule AshHq.Docs.Option do
end
end
code_interface do
define_for AshHq.Docs
define :read
end
resource do
description "Represents an option on a DSL section or entity"
end
aggregates do
first :extension_type, [:dsl, :extension], :type
first :extension_name, [:dsl, :extension], :name
@ -115,18 +129,4 @@ defmodule AshHq.Docs.Option do
first :library_id, [:library_version, :library], :id
first :dsl_sanitized_path, :dsl, :sanitized_path
end
relationships do
belongs_to :dsl, AshHq.Docs.Dsl do
allow_nil? true
end
belongs_to :library_version, AshHq.Docs.LibraryVersion do
allow_nil? true
end
belongs_to :extension, AshHq.Docs.Extension do
allow_nil? true
end
end
end

View file

@ -5,29 +5,11 @@ defmodule AshHq.MailingList.Email do
data_layer: AshPostgres.DataLayer,
notifiers: AshHq.MailingList.EmailNotifier
resource do
description "An email for the mailing list"
end
postgres do
repo AshHq.Repo
table "emails"
end
identities do
identity :unique_email, [:email]
end
actions do
defaults [:create, :read]
end
code_interface do
define_for AshHq.MailingList
define :all, action: :read
end
attributes do
uuid_primary_key :id
@ -38,6 +20,24 @@ defmodule AshHq.MailingList.Email do
timestamps()
end
actions do
defaults [:create, :read]
end
code_interface do
define_for AshHq.MailingList
define :all, action: :read
end
resource do
description "An email for the mailing list"
end
identities do
identity :unique_email, [:email]
end
validations do
validate match(:email, ~r/^[^\s]+@[^\s]+$/), message: "must have the @ sign and no spaces"
end

View file

@ -106,7 +106,11 @@ defmodule AshHqWeb.DocRoutes do
end
end
def sanitize_name(name) do
String.downcase(String.replace(name, ~r/[^A-Za-z0-9_]/, "-"))
def sanitize_name(name, allow_forward_slash? \\ false) do
if allow_forward_slash? do
String.downcase(String.replace(name, ~r/[^A-Za-z0-9\/_]/, "-"))
else
String.downcase(String.replace(name, ~r/[^A-Za-z0-9_]/, "-"))
end
end
end

View file

@ -14,18 +14,18 @@ defmodule AshHqWeb.Helpers do
end
def source_link(%AshHq.Docs.Module{file: file}, library, library_version) do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
"https://github.com/#{library.repo_org}/#{library.name}/tree/v#{library_version.version}/#{file}"
end
def source_link(%AshHq.Docs.MixTask{file: file}, library, library_version) do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
"https://github.com/#{library.repo_org}/#{library.name}/tree/v#{library_version.version}/#{file}"
end
def source_link(%AshHq.Docs.Function{file: file, line: line}, library, library_version) do
if line do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}#L#{line}"
"https://github.com/#{library.repo_org}/#{library.name}/tree/v#{library_version.version}/#{file}#L#{line}"
else
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
"https://github.com/#{library.repo_org}/#{library.name}/tree/v#{library_version.version}/#{file}"
end
end
@ -137,6 +137,10 @@ defmodule AshHqWeb.Helpers do
end
end
if !version do
raise "No such library in link: #{source}"
end
case Version.parse(version.version) do
{:ok, %Version{pre: pre, build: build}}
when pre != [] or not is_nil(build) ->

View file

@ -462,7 +462,8 @@ defmodule AshHqWeb.Pages.Docs do
guides_query =
AshHq.Docs.Guide
|> Ash.Query.new()
|> load_for_search(List.last(List.wrap(socket.assigns[:params]["guide"])))
|> load_for_search(Enum.join(List.wrap(socket.assigns[:params]["guide"]), "/"))
|> IO.inspect()
modules_query =
AshHq.Docs.Module
@ -691,15 +692,14 @@ defmodule AshHqWeb.Pages.Docs do
end
defp matches_path?(guide, guide_route) do
guide.route == Enum.join(guide_route, "/")
guide.sanitized_route == DocRoutes.sanitize_name(Enum.join(guide_route, "/"), true)
end
defp matches_name?(guide, [guide_name]) do
DocRoutes.sanitize_name(guide.name) == guide_name
defp matches_name?(guide, list) do
guide_name = List.last(list)
DocRoutes.sanitize_name(guide.name) == DocRoutes.sanitize_name(guide_name)
end
defp matches_name?(_, _), do: false
defp assign_dsl(socket) do
case socket.assigns[:params]["dsl_path"] do
nil ->

View file

@ -11,6 +11,7 @@
<% end %>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js"></script>
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/assets/app.css") %>"/>
<script>
const configuredThemeRow = document.cookie

View file

@ -46,6 +46,8 @@ defmodule AshHq.MixProject do
{:ash_csv, github: "ash-project/ash_csv"},
{:tails, github: "zachdaniel/tails"},
{:earmark, "~> 1.5.0-pre1", override: true},
{:nimble_options, "~> 0.5.1", override: true},
{:spark, "~> 0.3"},
{:surface, "~> 0.9.1"},
{:surface_heroicons, "~> 0.6.0"},
{:ua_inspector, "~> 3.0"},
@ -77,7 +79,6 @@ defmodule AshHq.MixProject do
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.18"},
{:nimble_options, "~> 0.4.0", override: true},
{:finch, "~> 0.10.2"},
{:floki, ">= 0.30.0"},
{:esbuild, "~> 0.3", runtime: Mix.env() == :dev},

View file

@ -1,14 +1,14 @@
%{
"absinthe": {:hex, :absinthe, "1.7.0", "36819e7b1fd5046c9c734f27fe7e564aed3bda59f0354c37cd2df88fd32dd014", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0 or ~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "566a5b5519afc9b29c4d367f0c6768162de3ec03e9bf9916f9dc2bcbe7c09643"},
"absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"},
"ash": {:git, "https://github.com/ash-project/ash.git", "a31da97d4ce1fa396e35e25f0c4d6f1eeb51a05a", []},
"ash": {:git, "https://github.com/ash-project/ash.git", "01185db356e276dcd7ead510fbf1e28a91ef2b1d", []},
"ash_admin": {:git, "https://github.com/ash-project/ash_admin.git", "5dfb19785c587d56f6443e99b6f976cf6c2c92c8", []},
"ash_blog": {:git, "https://github.com/ash-project/ash_blog.git", "79d1b9077b2712d4d4651eaa36c8e63119efd565", []},
"ash_csv": {:git, "https://github.com/ash-project/ash_csv.git", "bad0f6961bf5d135450dacda687d9df0549e80ae", []},
"ash_graphql": {:git, "https://github.com/ash-project/ash_graphql.git", "fb7b60f9e1793c912e238043aa115cd6585c19b7", []},
"ash_json_api": {:git, "https://github.com/ash-project/ash_json_api.git", "2525172e72d0e8b0e369155f32e5edfbf25b9691", []},
"ash_phoenix": {:git, "https://github.com/ash-project/ash_phoenix.git", "5ffcd1916ece639c684273e632cc0aecebe82f26", []},
"ash_postgres": {:hex, :ash_postgres, "1.1.1", "2bbc2b39d9e387f89b964b29b042f88dd352b71e486d9aea7f9390ab1db3ced4", [:mix], [{:ash, "~> 2.1", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "fe47a6e629b6b23ce17c1d70b1bd4b3fd732df513b67126514fb88be86a6439e"},
"ash_postgres": {:hex, :ash_postgres, "1.2.2", "0da0d9e1878f8b24829a8971e497e7bafe5596aced3604ba39d96e70174a8b11", [:mix], [{:ash, ">= 2.4.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "8796f1de22863451a445f33a0874904145efeecfff75175f6c1ce62754c8df4e"},
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"castore": {:hex, :castore, "0.1.18", "deb5b9ab02400561b6f5708f3e7660fc35ca2d51bfc6a940d2f513f89c2975fc", [:mix], [], "hexpm", "61bbaf6452b782ef80b33cdb45701afbcf0a918a45ebe7e73f1130d661e66a06"},
@ -24,15 +24,15 @@
"credo": {:hex, :credo, "1.6.6", "f51f8d45db1af3b2e2f7bee3e6d3c871737bda4a91bff00c5eec276517d1a19c", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "625520ce0984ee0f9f1f198165cd46fa73c1e59a17ebc520038b8fce056a5bdc"},
"csv": {:hex, :csv, "2.5.0", "c47b5a5221bf2e56d6e8eb79e77884046d7fd516280dc7d9b674251e0ae46246", [:mix], [{:parallel_stream, "~> 1.0.4 or ~> 1.1.0", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "e821f541487045c7591a1963eeb42afff0dfa99bdcdbeb3410795a2f59c77d34"},
"dataloader": {:hex, :dataloader, "1.0.10", "a42f07641b1a0572e0b21a2a5ae1be11da486a6790f3d0d14512d96ff3e3bbe9", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0 or ~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "54cd70cec09addf4b2ace14cc186a283a149fd4d3ec5475b155951bf33cd963f"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"docsh": {:hex, :docsh, "0.7.2", "f893d5317a0e14269dd7fe79cf95fb6b9ba23513da0480ec6e77c73221cae4f2", [:rebar3], [{:providers, "1.8.1", [hex: :providers, repo: "hexpm", optional: false]}], "hexpm", "4e7db461bb07540d2bc3d366b8513f0197712d0495bb85744f367d3815076134"},
"earmark": {:hex, :earmark, "1.5.0-pre1", "e04aca73692bc3cda3429d6df99c8dae2bf76411e5e76d006a4bc04ac81ef1c1", [:mix], [{:earmark_parser, "~> 1.4.21", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "26ec0473ad2ef995b9672f89309a7a4952887f69b78cfc7af14e320bc6546bfa"},
"earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"ecto": {:hex, :ecto, "3.9.2", "017db3bc786ff64271108522c01a5d3f6ba0aea5c84912cfb0dd73bf13684108", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "21466d5177e09e55289ac7eade579a642578242c7a3a9f91ad5c6583337a9d15"},
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"},
"ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"ecto_sql": {:hex, :ecto_sql, "3.9.1", "9bd5894eecc53d5b39d0c95180d4466aff00e10679e13a5cfa725f6f85c03c22", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fd470a4fff2e829bbf9dcceb7f3f9f6d1e49b4241e802f614de6b8b67c51118"},
"eflame": {:hex, :eflame, "1.0.1", "0664d287e39eef3c413749254b3af5f4f8b00be71c1af67d325331c4890be0fc", [:mix], [], "hexpm", "e0b08854a66f9013129de0b008488f3411ae9b69b902187837f994d7a99cf04e"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "85d4a87d216678dae30f348270eb90f9ed49ce20", [ref: "85d4a87d216678dae30f348270eb90f9ed49ce20"]},
@ -67,7 +67,7 @@
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"},
"nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
"nimble_options": {:hex, :nimble_options, "0.5.1", "5c166f7669e40333191bea38e3bd3811cc13f459f1e4be49e89128a21b5d8c4d", [:mix], [], "hexpm", "d176cf7baa4fef0ceb301ca3eb8b55bd7de3e45f489c4f8b4f2849f1f114ef3e"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"parallel_stream": {:hex, :parallel_stream, "1.1.0", "f52f73eb344bc22de335992377413138405796e0d0ad99d995d9977ac29f1ca9", [:mix], [], "hexpm", "684fd19191aedfaf387bbabbeb8ff3c752f0220c8112eb907d797f4592d6e871"},
@ -92,7 +92,7 @@
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
"sourceror": {:hex, :sourceror, "0.11.2", "549ce48be666421ac60cfb7f59c8752e0d393baa0b14d06271d3f6a8c1b027ab", [:mix], [], "hexpm", "9ab659118896a36be6eec68ff7b0674cba372fc8e210b1e9dc8cf2b55bb70dfb"},
"spark": {:hex, :spark, "0.2.8", "adce1887be100fae124c65f9dcfb6675fefdcd0ffd6355bb02f638bf88e26630", [:mix], [{:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "1619dc8fd899980e33a7a63939e0a02783ed32c3344d192ff6669bf44ff637a6"},
"spark": {:hex, :spark, "0.2.18", "4f0bc3860682317911d10aee97c4818d3d5ed700f646a87ee1ea6e4a65650f33", [:mix], [{:nimble_options, "~> 0.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "fc2c0bb7556cfa60c97b7bdf9f83ef85b366ba697a006abe3083dccb0a6adf88"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
"surface": {:hex, :surface, "0.9.1", "6a343564b1d6c17c619ac933cec5680ffe8c68f0cd2d85f780b70f6607750a96", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.11", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "131312d35d190739d0e0f1681acb9fce6962d3f81439011a3331bad2976ca372"},

View file

@ -0,0 +1,21 @@
defmodule AshHq.Repo.Migrations.MigrateResources33 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(:libraries) do
add(:repo_org, :text, null: false, default: "ash-project")
end
end
def down do
alter table(:libraries) do
remove(:repo_org)
end
end
end

View file

@ -0,0 +1,25 @@
defmodule AshHq.Repo.Migrations.MigrateResources34 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(:sanitized_route, :text)
end
execute("""
UPDATE guides SET sanitized_route = REGEXP_REPLACE(guides.route,'[^A-Za-z0-9\/_]/','-','g');
""")
end
def down do
alter table(:guides) do
remove(:sanitized_route)
end
end
end

View file

@ -0,0 +1,21 @@
defmodule AshHq.Repo.Migrations.MigrateResources35 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
modify :sanitized_route, :text, null: false
end
end
def down do
alter table(:guides) do
modify :sanitized_route, :text, null: true
end
end
end

View file

@ -63,6 +63,34 @@ AshHq.Docs.Library.create!(
upsert_identity: :unique_name
)
AshHq.Docs.Library.create!(
%{
name: "ash_authentication",
display_name: "AshAuthentication",
order: 55,
repo_org: "alembic",
description: """
Provides drop-in support for user authentication with various strategies and tons of customizability.
"""
},
upsert?: true,
upsert_identity: :unique_name
)
AshHq.Docs.Library.create!(
%{
name: "ash_authentication_phoenix",
display_name: "AshAuthenticationPhoenix",
order: 56,
repo_org: "alembic",
description: """
Phoenix helpers and UI components in support of AshAuthentication.
"""
},
upsert?: true,
upsert_identity: :unique_name
)
AshHq.Docs.Library.create!(
%{
name: "ash_csv",

View file

@ -0,0 +1,182 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "sanitized_name",
"type": "text"
},
{
"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?": false,
"default": "\"Topics\"",
"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?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "sanitized_route",
"type": "text"
},
{
"allow_nil?": false,
"default": "false",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "default",
"type": "boolean"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "inserted_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "updated_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "guides_library_version_id_fkey",
"on_delete": "delete",
"on_update": null,
"schema": "public",
"table": "library_versions"
},
"size": null,
"source": "library_version_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [
{
"code?": false,
"down": "DROP INDEX guides_name_lower_index;",
"name": "name_index",
"up": "CREATE INDEX guides_name_lower_index ON guides(lower(name));\n"
},
{
"code?": false,
"down": "DROP INDEX guides_name_trigram_index;",
"name": "trigram_index",
"up": "CREATE INDEX guides_name_trigram_index ON guides USING GIST (name gist_trgm_ops);\n"
},
{
"code?": false,
"down": "DROP INDEX guides_search_index;",
"name": "search_index",
"up": "CREATE INDEX guides_search_index ON guides USING GIN((\n setweight(to_tsvector('english', name), 'A') ||\n setweight(to_tsvector('english', text), 'D')\n));\n"
}
],
"has_create_action": true,
"hash": "32E0B060B2EECF6DB8FE11EDDEB2058F0474B8B7AA32C0B919B0C2BD6CC9D649",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshHq.Repo",
"schema": null,
"table": "guides"
}

View file

@ -0,0 +1,182 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "sanitized_name",
"type": "text"
},
{
"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?": false,
"default": "\"Topics\"",
"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": null,
"size": null,
"source": "sanitized_route",
"type": "text"
},
{
"allow_nil?": false,
"default": "false",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "default",
"type": "boolean"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "inserted_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "updated_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "guides_library_version_id_fkey",
"on_delete": "delete",
"on_update": null,
"schema": "public",
"table": "library_versions"
},
"size": null,
"source": "library_version_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [
{
"code?": false,
"down": "DROP INDEX guides_name_lower_index;",
"name": "name_index",
"up": "CREATE INDEX guides_name_lower_index ON guides(lower(name));\n"
},
{
"code?": false,
"down": "DROP INDEX guides_name_trigram_index;",
"name": "trigram_index",
"up": "CREATE INDEX guides_name_trigram_index ON guides USING GIST (name gist_trgm_ops);\n"
},
{
"code?": false,
"down": "DROP INDEX guides_search_index;",
"name": "search_index",
"up": "CREATE INDEX guides_search_index ON guides USING GIN((\n setweight(to_tsvector('english', name), 'A') ||\n setweight(to_tsvector('english', text), 'D')\n));\n"
}
],
"has_create_action": true,
"hash": "C6EBDFE59FA544A48DD4123D2B6634225E65FE5FFE0EE01C07CF1156E0E514A6",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshHq.Repo",
"schema": null,
"table": "guides"
}

View file

@ -0,0 +1,116 @@
{
"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": "name",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "display_name",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "order",
"type": "bigint"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "description",
"type": "text"
},
{
"allow_nil?": false,
"default": "\"ash-project\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "repo_org",
"type": "text"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "inserted_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"now()\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "updated_at",
"type": "utc_datetime_usec"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "43691B272EE308EC8D6CD2D075BE6C7932C26F5A573F162D568377E1DAAE7AF8",
"identities": [
{
"base_filter": null,
"index_name": "libraries_unique_name_index",
"keys": [
"name"
],
"name": "unique_name"
},
{
"base_filter": null,
"index_name": "libraries_unique_order_index",
"keys": [
"order"
],
"name": "unique_order"
}
],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshHq.Repo",
"schema": null,
"table": "libraries"
}

View file

@ -400,6 +400,7 @@ defmodule Utils do
defp type({:spark, mod}), do: Module.split(mod) |> List.last()
defp type({:spark_behaviour, mod}), do: Module.split(mod) |> List.last()
defp type({:spark_behaviour, mod, _builtins}), do: Module.split(mod) |> List.last()
defp type(:module), do: "Module"
defp type({:spark_function_behaviour, mod, {_, arity}}) do
type({:or, [{:fun, arity}, {:spark_behaviour, mod}]})
@ -487,9 +488,36 @@ case Enum.at(dsls, 0) do
end)
end)
{:ok, all_modules} =
name
|> String.to_atom()
|> :application.get_key(:modules)
all_modules =
Enum.filter(all_modules, fn module ->
case Code.fetch_docs(module) do
{:docs_v1, _, _, _, type, _, _} when type != :hidden ->
true
_ ->
false
end
end)
acc =
Utils.try_apply(fn -> dsl.code_modules() end, [])
|> Enum.reduce(acc, fn {category, modules}, acc ->
modules =
case modules do
%Regex{} = regex ->
Enum.filter(all_modules, fn module ->
Regex.match?(regex, inspect(module)) || Regex.match?(regex, to_string(module))
end)
other ->
List.wrap(other)
end
modules
|> Enum.with_index()
|> Enum.reduce(acc, fn {module, order}, acc ->