View Source Using with NimblePublisher

NimblePublisher is a library that uses the filesystem for storage and builds content at compile-time.

When used with Phoenix, it offers a powerful Markdown based platform for building personal websites and blogs.

start-with-nimblepublisher

Start with NimblePublisher

You can follow along in the example from the docs to get a simple site up and running.

add-some-links-to-your-post-s

Add some links to your post(s)

The example blog post from the link above needs a link, for testing our webmention sending:

# /posts/2020/04-17-hello-world.md
%{
  title: "Hello world!",
  author: "José Valim",
  tags: ~w(hello),
  description: "Let's learn how to say hello world"
}
---
This is the post.

This is a [link to the webmention testing site](https://webmention.rocks/test/1)

With that in place, lets configure libmention.

configure-libmention

Configure libmention

Once the example is up and running, you'll want to add libmention to your supervision tree.

Start by defining your options

libmention_opts = [
    outgoing: [
        proxy: [port: 8082], # only for local dev
        user_agent: "mywebsite-libmention"
    ]
]

See Libmention.Supervisor for all configuration options

Now add libmention AFTER the web server in your supervision tree

    children = [
        ...
        YourWeb.Endpoint,
        {Libmention.Supervisor, libmention_opts}
    ]

    opts = [strategy: :one_for_one, name: App.Supervisor]
    Supervisor.start_link(children, opts)

send-webmentions

Send Webmentions

We need to tell libmention about our posts, lets create a very simple process.

defmodule MyApp.WebMentionSender do
  use GenServer, restart: :temporary

  def start_link(), do: GenServer.start_link(__MODULE__, :ok)

  @impl true
  def init(:ok) do
    pages = MyApp.Blog.all_posts()
    {:ok, %{pages: pages, done: []}, {:continue, :send}}
  end

  @impl true
  def handle_continue(:send, state) do
    for page <- state.pages do
      source_url = Routes.blog_path(Endpoint, :show, page.id)
      Libmention.Supervisor.send(source_url, page.body)
    end

    {:noreply, state}
  end

  # When  the `send/2` function is done, a message will be sent
  # back to the parent process (this process) of :done or {:done, queue_url}
  @impl true
  def handle_info(:done, state) do
    state = %{state | done: [:done | state.done]}
    if Enum.count(state.done) == Enum.count(state.pages) do
      {:stop, :normal, state}
    else
      {:noreply, state}
    end
  end

  def handle_info({:done, url}, state) do
    state = %{state | done: [{:done, url} | state.done]}

    if Enum.count(state.done) == Enum.count(state.pages) do
      {:stop, :normal, state}
    else
      {:noreply, state}
    end
  end

  @impl true
  def terminate(_reason, _state) do
    :ok
  end
end

Great, now update the supervision tree to include this process

    children = [
        ...
        YourWeb.Endpoint,
        {Libmention.Supervisor, libmention_opts},
        MyApp.WebmentionSender
    ]

Start up your server and browse to http://localhost:8082/sent.

Since we are running in dev mode with the proxy active, our webmention didn't actually get sent anywhere. Instead, the proxy shows what would have been sent in production mode.

To test this out fully, you'll need to deploy to production and remove the proxy config for the prod build.

whats-next

Whats next?

The previous configuration will work, but when the server is stopped and restarted, it will send your webmentions all over again.

This is because we are storing the results of your webmentions in an ets table by default. To persist this across restarts, you'll want to look at the guide on getting the persistence layer to work with Ecto.