Topical.Topic (topical v0.2.2)

This module provides functions for instantiating and manipulating topic state.

The state of a topic is composed of a value, observed by subscribed clients, and also hidden internal state, which can be used to share state between calls. The value must only be manipulated by the helper functions (which track the individual updates). The hidden state can be modified directly.

This module also contains a macro - use-ing it sets up a topic server:

defmodule MyApp.Topics.List do
  use Topical.Topic, route: ["lists", :list_id]

  # Initialise the topic
  def init(params) do
    list_id = Keyword.fetch!(params, :list_id)

    value = %{items: %{}, order: []} # exposed 'value' of the topic
    state = %{list_id: list_id} # hidden server state
    topic = Topic.new(value, state)

    {:ok, topic}
  end

  # Optionally, handle subscribe
  def handle_subscribe(topic, _context) do
    {:ok, topic}
  end

  # Optionally, handle unsubscribe
  def handle_unsubscribe(topic, _context) do
    {:ok, topic}
  end

  # Optionally, handle capture
  def handle_capture(topic, _context) do
    {:ok, topic}
  end

  # Optionally, handle execution of an action
  def handle_execute("add_item", {text}, topic, _context) do
    id = Integer.to_string(:erlang.system_time())

    # Update the topic by putting the item in 'items', and appending the id to 'order'
    topic =
      topic
      |> Topic.set([:items, id], %{text: text, done: false})
      |> Topic.insert([:order], id)

    # Return the result (the 'id'), and the updated topic
    {:ok, id, topic}
  end

  # Optionally, handle a notification (an action without a result)
  def handle_notify("update_text", {id, text}, topic, _context) do
    topic  = Topic.set(topic, [:items, id, :text], text)
    {:ok, topic}
  end

  # Optionally, handle Erlang messages
  def handle_info({:done, id}, topic) do
    topic  = Topic.set(topic, [:items, id, :done], true)
    {:ok, topic}
  end

  # Optionally, handle the topic being terminated (e.g., once clients have disconnected)
  def terminate(_reason, topic) do
    # ...
  end
end

Link to this section Summary

Functions

Updates the topic be deleting count values from the array at the path, from the index.

Updates the topic by inserting the specified values into the array at the path, at the index (or append them to the end, if no index is specified).

Updates the opic by merging value into path.

Instantiates a new instance of a topic.

Updates the topic by setting the value at the path.

Updates the topic by unsetting the key of the object at the path.

Link to this section Functions

Link to this function

delete(topic, path, index, count \\ 1)

Updates the topic be deleting count values from the array at the path, from the index.

%{foo: %{bar: [1, 2, 3, 4]}}
|> Topic.new()
|> Topic.delete([:foo, :bar], 2)
|> Map.fetch!(:value)
#=> %{foo: %{bar: [1, 2, 4]}}
Link to this function

insert(topic, path, index \\ nil, value)

Updates the topic by inserting the specified values into the array at the path, at the index (or append them to the end, if no index is specified).

If value is a list, all the items will be added (to add a list as a single item, wrap it in a list).

%{foo: %{bar: [1, 4]}}
|> Topic.new()
|> Topic.insert([:foo, :bar], 1, [2, 3])
|> Map.fetch!(:value)
#=> %{foo: %{bar: [1, 2, 3, 4]}}
Link to this function

merge(topic, path, value)

Updates the opic by merging value into path.

%{foo: %{bar: %{a: 1, b: 2}}}
|> Topic.new()
|> Topic.merge([:foo, :bar], %{b: 3, c: 4})
|> Map.fetch!(:value)
#=> %{foo: %{bar: %{a: 1, b: 3, c: 4}}}
Link to this function

new(value, state \\ nil)

Instantiates a new instance of a topic.

value is the initial value of the client-visible state. state is the 'hidden' internal state.

Link to this function

set(topic, path, value)

Updates the topic by setting the value at the path.

%{foo: %{bar: 2}}
|> Topic.new()
|> Topic.set([:foo, :bar], 3)
|> Map.fetch!(:value)
#=> %{foo: %{bar: 3}}
Link to this function

unset(topic, path, key)

Updates the topic by unsetting the key of the object at the path.

%{foo: %{bar: 2}}
|> Topic.new()
|> Topic.unset([:foo], :bar)
|> Map.fetch!(:value)
#=> %{foo: %{}}