Skip to content

Commit

Permalink
feat: add docs stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
RemoteRabbit committed Nov 5, 2024
1 parent 8e7cdb5 commit 4575631
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 33 deletions.
38 changes: 37 additions & 1 deletion lib/portfolio/blog.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
defmodule Portfolio.Blog do
@moduledoc """
This module handles fetching and caching blog posts.
It uses the ConCache library to cache the list of blog posts for a specified
time-to-live (TTL) of 1 hour. The `list_posts/0` function retrieves the cached
posts or fetches them from a remote source if the cache is empty or expired.
The `get_post_by_slug/1` function retrieves a specific blog post by its slug
from the cached list of posts.
The `refresh_cache/0` function clears the cache and fetches the latest blog
posts from the remote source.
"""

@cache_ttl :timer.hours(1)

@doc """
Retrieves the list of blog posts.
If the cache is empty or expired, it fetches the posts from a remote source
and caches them for the specified TTL.
Returns a list of blog post structs.
"""
def list_posts do
case ConCache.get_or_store(:blog_cache, :posts, fn ->
posts = Portfolio.Blog.Post.fetch_posts("remoterabbit", "blog-posts")
Expand All @@ -11,14 +33,28 @@ defmodule Portfolio.Blog do
end
end

@doc """
Retrieves a blog post by its slug.
Searches the cached list of blog posts for a post with the given slug.
Returns the blog post struct if found, or `nil` if not found.
"""
def get_post_by_slug(slug) do
list_posts()
|> Enum.find(&(&1.slug == slug))
end

@doc """
Refreshes the blog post cache.
Clears the cached list of blog posts and fetches the latest posts from the
remote source.
Returns the updated list of blog post structs.
"""
def refresh_cache do
ConCache.delete(:blog_cache, :posts)
list_posts()
end
end

36 changes: 35 additions & 1 deletion lib/portfolio/blog/post.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
defmodule Portfolio.Blog.Post do
@moduledoc """
This module defines a struct to represent a blog post and provides functions
to fetch and parse blog posts from a GitHub repository.
## Struct Fields
- `title`: The title of the blog post.
- `content`: The HTML content of the blog post.
- `date`: The date the blog post was published.
- `slug`: The slug (URL-friendly version of the title) for the blog post.
- `description`: A short description of the blog post.
- `status`: The status of the blog post (e.g., "published", "draft").
## Functions
- `fetch_posts/2`: Fetches blog posts from a GitHub repository.
- `parse_content/2`: Parses the content of a blog post file.
"""
defstruct [:title, :content, :date, :slug, :description, :status]

@doc """
Fetches blog posts from a GitHub repository.
This function retrieves the contents of the specified `path` (defaulting to "posts/")
in the given `owner` and `repo`. It filters the contents to only include Markdown files,
parses the content of each file, and returns a list of `%Portfolio.Blog.Post{}` structs
representing the published blog posts, sorted in descending order by date.
## Examples
iex> Portfolio.Blog.Post.fetch_posts("owner", "repo")
[%Portfolio.Blog.Post{...}, ...]
iex> Portfolio.Blog.Post.fetch_posts("owner", "repo", "custom/path/")
[%Portfolio.Blog.Post{...}, ...]
"""
def fetch_posts(owner, repo, path \\ "posts/") do
case Tentacat.Contents.find(owner, repo, path) do
{200, contents, _} ->
Expand Down Expand Up @@ -41,4 +76,3 @@ defmodule Portfolio.Blog.Post do
end
end
end

25 changes: 23 additions & 2 deletions lib/portfolio_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ defmodule PortfolioWeb do
This can be used in your application as:
use PortfolioWeb, :controller
use PortfolioWeb, :html
use PortfolioWeb, :controller
use PortfolioWeb, :html
The definitions below will be executed for every controller,
component, etc, so keep them short and clean, focused
Expand All @@ -17,8 +17,14 @@ defmodule PortfolioWeb do
those modules here.
"""

@doc """
Provides the list of static paths for the application.
"""
def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)

@doc """
Defines a quote for the router.
"""
def router do
quote do
use Phoenix.Router, helpers: false
Expand All @@ -30,12 +36,18 @@ defmodule PortfolioWeb do
end
end

@doc """
Defines a quote for channels.
"""
def channel do
quote do
use Phoenix.Channel
end
end

@doc """
Defines a quote for controllers.
"""
def controller do
quote do
use Phoenix.Controller,
Expand All @@ -49,6 +61,9 @@ defmodule PortfolioWeb do
end
end

@doc """
Defines a quote for live views.
"""
def live_view do
quote do
use Phoenix.LiveView,
Expand All @@ -58,6 +73,9 @@ defmodule PortfolioWeb do
end
end

@doc """
Defines a quote for live components.
"""
def live_component do
quote do
use Phoenix.LiveComponent
Expand All @@ -66,6 +84,9 @@ defmodule PortfolioWeb do
end
end

@doc """
Defines a quote for HTML components.
"""
def html do
quote do
use Phoenix.Component
Expand Down
78 changes: 76 additions & 2 deletions lib/portfolio_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -588,8 +588,23 @@ defmodule PortfolioWeb.CoreComponents do
"""
end

## JS Commands
@doc """
Shows an element on the page with a transition animation.
## Parameters
- `js` (optional): A `JS` struct representing the JavaScript context. If not provided, a new `JS` struct will be created.
- `selector`: A CSS selector string identifying the element(s) to be shown.
## Examples
iex> show(~s(#my-element))
%JS{...}
iex> show(%JS{}, ~s(.my-class))
%JS{...}
"""
def show(js \\ %JS{}, selector) do
JS.show(js,
to: selector,
Expand All @@ -600,6 +615,23 @@ defmodule PortfolioWeb.CoreComponents do
)
end

@doc """
Hides an element on the page with a transition animation.
## Parameters
- `js` (optional): A `JS` struct representing the JavaScript context. If not provided, a new `JS` struct will be created.
- `selector`: A CSS selector string identifying the element(s) to be hidden.
## Examples
iex> hide(~s(#my-element))
%JS{...}
iex> hide(%JS{}, ~s(.my-class))
%JS{...}
"""
def hide(js \\ %JS{}, selector) do
JS.hide(js,
to: selector,
Expand All @@ -611,18 +643,60 @@ defmodule PortfolioWeb.CoreComponents do
)
end

@doc ~S"""
Shows a modal dialog with the given `id`.
This function takes an optional `js` argument, which is a `Phoenix.LiveView.JS` struct
that can be used to chain additional JavaScript commands. If no `js` struct is provided,
a new one is created.
The `id` argument is a string that represents the unique identifier of the modal dialog
to be shown. It is used to target the corresponding HTML elements in the DOM.
The function performs the following actions:
1. Shows the modal dialog element with the given `id`.
2. Shows the background overlay element with the ID `#{id}-bg`, applying a transition
effect to fade it in.
3. Shows the container element with the ID `#{id}-container`.
4. Adds the `overflow-hidden` class to the `<body>` element to prevent scrolling while
the modal is open.
5. Focuses the first focusable element within the modal content area with the ID
`#{id}-content`.
Returns the updated `Phoenix.LiveView.JS` struct with the added JavaScript commands.
"""
def show_modal(js \\ %JS{}, id) when is_binary(id) do
js
|> JS.show(to: "##{id}")
|> JS.show(
to: "##{id}-bg",
transition: {"transition-all transform ease-out duration-300", "opacity-0", "opacity-100"}
)
|> show("##{id}-container")
|> JS.show(to: "##{id}-container")
|> JS.add_class("overflow-hidden", to: "body")
|> JS.focus_first(to: "##{id}-content")
end

@doc """
Hides a modal by applying CSS transitions and classes to the modal elements.
This function takes an optional `JS` struct and the `id` of the modal to hide.
It returns a new `JS` struct with the necessary operations to hide the modal.
The modal is hidden by:
1. Fading out the background overlay
2. Hiding the modal container
3. Setting the modal element to `display: hidden`
4. Removing the `overflow-hidden` class from the `body` element
5. Popping the focus from the modal
## Examples
iex> hide_modal(%JS{}, "my-modal")
%JS{...}
"""
def hide_modal(js \\ %JS{}, id) do
js
|> JS.hide(
Expand Down
10 changes: 10 additions & 0 deletions lib/portfolio_web/components/layouts.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
defmodule PortfolioWeb.Layouts do
@moduledoc """
This module provides layout templates for the Phoenix application.
The layouts are used to render the main HTML structure of the application,
including the header, footer, and other common elements.
The `embed_templates` macro is used to embed the layout templates
located in the "layouts/*" directory.
"""

use PortfolioWeb, :html

embed_templates "layouts/*"
Expand Down
35 changes: 32 additions & 3 deletions lib/portfolio_web/controllers/error_html.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
defmodule PortfolioWeb.ErrorHTML do
@moduledoc """
Module for handling error pages in the PortfolioWeb application.
This module provides functions for rendering error pages when an error occurs
in the application. By default, it renders a plain text page based on the
template name (e.g., "404.html" becomes "Not Found").
To customize the error pages, you can uncomment the `embed_templates/1` call
and add pages to the `error` directory, such as:
* `lib/portfolio_web/controllers/error_html/404.html.heex`
* `lib/portfolio_web/controllers/error_html/500.html.heex`
These files can contain HTML templates for rendering more detailed error pages.
"""

use PortfolioWeb, :html

# If you want to customize your error pages,
Expand All @@ -10,9 +26,22 @@ defmodule PortfolioWeb.ErrorHTML do
#
# embed_templates "error_html/*"

# The default is to render a plain text page based on
# the template name. For example, "404.html" becomes
# "Not Found".
@doc """
Renders a plain text page based on the template name.
This function is called when an error occurs and no custom error page is
provided. It takes the template name (e.g., "404.html") and returns a plain
text message based on the template name using `Phoenix.Controller.status_message_from_template/1`.
## Examples
iex> PortfolioWeb.ErrorHTML.render("404.html", [])
"Not Found"
iex> PortfolioWeb.ErrorHTML.render("500.html", [])
"Internal Server Error"
"""
def render(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
Expand Down
25 changes: 25 additions & 0 deletions lib/portfolio_web/controllers/error_json.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
defmodule PortfolioWeb.ErrorJSON do
@doc """
Renders JSON error responses.
This module is responsible for rendering JSON error responses in case of
errors during the request processing. It provides a default implementation
that returns the status message from the template name (e.g., "404.json"
becomes "Not Found").
You can customize the error rendering for specific status codes by adding
clauses to the `render/2` function. For example:
def render("500.json", _assigns) do
%{errors: %{detail: "Internal Server Error"}}
end
## Examples
iex> PortfolioWeb.ErrorJSON.render("404.json", %{})
%{errors: %{detail: "Not Found"}}
iex> PortfolioWeb.ErrorJSON.render("500.json", %{})
%{errors: %{detail: "Internal Server Error"}}
"""

# If you want to customize a particular status code,
# you may add your own clauses, such as:
#
Expand Down
Loading

0 comments on commit 4575631

Please sign in to comment.