README.md

# PolyPost

A publishing engine with markdown and code highlighting support.

## Features

* Loads files directly from configured paths
* Stores content in single process-owned ETS tables
* Supports markdown with structured metadata in JSON
* Supports multiple directories with markdown files that can be specified as different resources
* Supports code highlighting in `code` blocks using [makeup](https://github.com/elixir-makeup/makeup)
* Update content during runtime by calling:
  * `PolyPost.build_and_store!/1`
  * `PolyPost.build_and_store_all!/0`

## Installation

You can add `poly_post` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [{:poly_post, "~> 0.1"}]
end
```

In any of the `config/{config,dev,prod,test}.exs` files
you can configure each resources for your content:

```elixir
config :poly_post, :resources,
  content: [
    articles: {Article, {:path, "/path/to/my/markdown/*.md")}}
  ]
```

## Basic Usage

### Loading and Storing Content

With a file called `my_article1.md` in the configured directory:

```markdown
{
  "title": "My Article #1",
  "author": "Me"
}
---
## My Article 1

This is my first article
```

You can create an `Article` module to load your content by
implementing the `PolyPost.Resource.build/3` callback:

```elixir
defmodule Article do
  @behaviour PolyPost.Resource

  @enforce_keys [:key, :title, :author, :body]
  defstruct [:key, :title, :author, :body]

  # Callbacks

  @impl PolyPost.Resource
  def build(reference, metadata, body) do
    %__MODULE__{
      key: reference,
      title: get_in(metadata, ["title"]),
      author: get_in(metadata, ["author"]),
      body: body
    }
  end
end
```

The only requirement is that the struct or map **MUST** contain a key
called `key` that uniquely identifies this content. It **MUST** be a
`String`.

When I call `PolyPost.build_and_store_all!/0`, it will:

1. Load and parse all the markdown files and their metadata
2. Replace all `code` blocks with highlighted versions if a highlighter is .
2. Call `Article.build/3` with the `reference` (filename), `metadata` and `content`
3. Then it stores it in a corresponding `PolyPost.Depot` process.

### Using Makeup to Style Code Blocks

If you wish to use [makeup](https://github.com/elixir-makeup/makeup) to style your `code` blocks, you must
specify the needed dependencies in your `mix.exs` file.

For example, if you wanted to highlight Elixir, Erlang and HTML in your
project, then I would specify the following:

```elixir
defp deps do
  [
    {:makeup, "~> 1.1"},
    {:makeup_elixir, ">= 0.0.0"},
    {:makeup_erlang, ">= 0.0.0"},
    {:makeup_html, ">= 0.0.0"}
  ]
end
```

Then you can use tags in your markdown code blocks like so and it will
automatically highlight them:

````markdown
```elixir
def start_link(arg) do
  GenServer.start_link(__MODULE__, arg, name: __MODULE__)
end
```
````

### Retrieving Content

You can retrieve content using the functions on the `PolyPost.Depot`
module to access the associated ETS table that stores your data:

1. `find/2` - find a specific content by `key` for the resource
2. `get_all/1` - gets all content for a resource

For example:

```elixir
PolyPost.Depot.find(:articles, "my_article1.md")
=> %Article{...}
```

and

```elixir
PolyPost.Depot.get_all(:articles)
=> [%Article{...}]
```

## Differences from NimblePublisher

This library was heavily inspired by [NimblePublisher](https://github.com/dashbitco/nimble_publisher), but
it **IS** different.

* Metadata in markdown files are specified in JSON instead of Elixir
* Designed to be updated at runtime via calling refresh methods (`PolyPost.build_and_store!/1` or `PolyPost.build_and_store_all!/0`)
* Must be configured through `Application` config using `:poly_post`
* Stores content in ETS instead of compiling directly into modules

## License

This software is licensed under the [Apache-2.0 License](LICENSE).