defmodule Postex do @moduledoc """ Postex is a simple static blog generator using markdown files that is inspired/copied from [Dashbit's blog post](https://dashbit.co/blog/welcome-to-our-blog-how-it-was-made). See the README for more details! """ defmacro __using__(opts) do prefix = Keyword.get(opts, :prefix) per_page = Keyword.get(opts, :per_page, 10) quote do alias Postex.{MetaData, Post, Validate} for app <- [:earmark, :makeup_elixir] do Application.ensure_all_started(app) end prefix = unquote(prefix) |> Validate.prefix_defined() # Generating posts posts_paths = "posts/**/*.md" |> Path.wildcard() |> Enum.reject(fn post -> String.contains?(post, "draft") end) |> Enum.sort() for post_path <- posts_paths do @external_resource Path.relative_to_cwd(post_path) end posts = posts_paths |> Enum.map(fn post -> Post.parse!(post, unquote(opts)) end) |> Validate.no_duplicate_slugs() |> Validate.url_length(prefix) |> Validate.same_data_fields() |> MetaData.add_related_posts() @posts Enum.sort_by(posts, & &1.date, {:desc, Date}) # Posts API @doc "Returns a list of all the posts" @spec list_posts :: [Post.t()] def list_posts do @posts end @doc "Returns a list of posts for the page, accepts page as integer or string" @spec list_posts(pos_integer | String.t()) :: [Post.t()] def list_posts(page) when is_binary(page) do page |> String.to_integer() |> list_posts() end def list_posts(page) do per_page = unquote(per_page) start_index = (page - 1) * per_page end_index = page * per_page - 1 Enum.slice(@posts, start_index..end_index) end @doc "Number of pages of posts" @spec pages :: non_neg_integer def pages do count = Enum.count(@posts) per_page = unquote(per_page) if rem(count, per_page) > 0 do div(count, per_page) + 1 else div(count, per_page) end end @doc "Returns a list of all the posts with a tag" @spec posts_tagged_with(binary) :: [Post.t()] def posts_tagged_with(tag) do Enum.filter(@posts, fn post -> Enum.member?(post.tags, tag) end) end @doc "Gets a specific post by the id (slug)" @spec get_post(binary) :: Post.t() | nil def get_post(id) do Enum.find(@posts, fn post -> post.id == id end) end @spec fetch_post(binary) :: {:ok, Post.t()} | {:error, :not_found} def fetch_post(id) do case get_post(id) do nil -> {:error, :not_found} post -> {:ok, post} end end # Generating Tags @tags_with_count posts |> Enum.map(fn post -> post.tags end) |> List.flatten() |> Enum.frequencies() # Tags API @doc "Gets a list of all the tags" @spec list_tags :: [binary] def list_tags do Map.keys(@tags_with_count) end @doc "Returns a map with the tags as keys and the frequency as a value" @spec tags_with_count :: map def tags_with_count do @tags_with_count end end end end