Current section

Files

Jump to
grapple lib grapple.ex
Raw

lib/grapple.ex

defmodule Grapple do
@moduledoc """
This is the main module for Grapple. It defines the functions and macros that
are available for adding, removing, and viewing topics and hooks, and
broadcasting hooks.
"""
use Application
def start(_type, _args) do
Grapple.Supervisor.start_link([])
end
# Topics
@doc """
Adds a new topic. Topic must be an atom. Returns a `Grapple.Server.Topic`
struct which has a `name` and a `sup` (Supervisor pid).
iex> {:ok, topic = %Grapple.Server.Topic{}} = Grapple.add_topic(:pokemon)
iex> topic.name
:pokemon
"""
def add_topic(topic) when is_atom(topic) do
Grapple.Server.add_topic(topic)
end
@doc """
Removes a topic.
Returns `:ok`.
"""
def remove_topic(topic) when is_atom(topic) do
Grapple.Server.remove_topic(topic)
end
@doc """
Lists all topics.
iex> {:ok, pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, gyms} = Grapple.add_topic(:gyms)
iex> [gyms, pokemon] == Grapple.get_topics
true
"""
def get_topics do
Grapple.Server.get_topics()
end
@doc """
Clears all topics.
Returns `:ok`.
"""
def clear_topics do
Grapple.Server.clear_topics()
end
# Hooks
@doc """
Subscribes a hook to a topic. The first argument must be an atom
representing an existing topic and the second must be a valid `Hook`
struct.
Returns the `pid` of the Hook process that was created.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api"})
iex> is_pid(pid)
true
"""
def subscribe(topic, %Grapple.Hook{} = webhook) when is_atom(topic) do
Grapple.Server.subscribe(topic, webhook)
end
@doc """
Sends HTTP requests for all hooks subscribed to the given topic.
Returns `:ok`.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, _pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api"})
iex> Grapple.broadcast(:pokemon)
:ok
"""
def broadcast(topic) when is_atom(topic) do
Grapple.Server.broadcast(topic)
end
@doc """
Like broadcast/1, but will send all hooks for a topic with the given `body`
instead of what was originally defined as the `body` on the `Hook`.
"""
def broadcast(topic, body) when is_atom(topic) do
Grapple.Server.broadcast(topic, body)
end
@doc """
Returns a list of all hooks subscribed to a topic.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, _pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api"})
iex> [{_pid, hook}] = Grapple.get_hooks(:pokemon)
iex> hook
%Grapple.Hook{body: %{}, headers: [], life: nil, method: "GET", owner: nil,
query: %{}, ref: nil, url: "my-api"}
"""
def get_hooks(topic) when is_atom(topic) do
Grapple.Server.get_hooks(topic)
end
@doc """
Returns a list of responses for each hook on the given topic.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, _pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api"})
iex> [{_pid, responses}] = Grapple.get_responses(:pokemon)
iex> responses
[]
"""
def get_responses(topic) when is_atom(topic) do
Grapple.Server.get_responses(topic)
end
@doc """
Removes a subscribed hook given a topic and the hook `pid`.
Returns `:ok`.
"""
def remove_hook(hook) when is_pid(hook) do
Grapple.Server.remove_hook(hook)
end
@doc """
Starts polling for a hook if the hook already has a specified interval (milliseconds).
Use this function if the specified hook already has an interval, but
polling is not currently running.
Returns `:ok` if successful or `{:error, msg}` otherwise.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api"})
iex> Grapple.start_polling(pid)
{:error, "No interval specified, use `start_polling/2`."}
"""
def start_polling(hook) when is_pid(hook) do
Grapple.Server.start_polling(hook)
end
@doc """
Starts polling for a hook with the given interval (milliseconds).
Returns `:ok` if successful.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api"})
iex> Grapple.start_polling(pid, 3000)
:ok
"""
def start_polling(hook, interval) when is_pid(hook) and is_integer(interval) do
Grapple.Server.start_polling(hook, interval)
end
@doc """
Stops a hook from polling.
Returns `:ok`.
iex> {:ok, _pokemon} = Grapple.add_topic(:pokemon)
iex> {:ok, pid} = Grapple.subscribe(:pokemon, %Grapple.Hook{url: "my-api", interval: 3000})
iex> Grapple.stop_polling(pid)
:ok
"""
def stop_polling(hook) when is_pid(hook) do
Grapple.Server.stop_polling(hook)
end
# Macros
@doc """
Allows modules to `use` Grapple.Hook in them
"""
defmacro __using__(_opts) do
quote do
import Grapple
end
end
@doc """
Allows users to define hookable functions that automatically publish
to subscribers whenever they are invoked.
"""
defmacro defhook({name, _, _} = func, do: block) do
quote do
def unquote(func) do
topic = unquote(name)
result = unquote(block)
broadcast(topic, result)
result
end
end
end
end