Skip to content

MillionIntegrals/stone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stone

Build Status Build Status

I have started this project while learning elixir macros by trying to replicate functionality from sasa1977/exactor. The source code wasn't very clear to me, so I went on and tried to reimplement myself the same features, while at the same time trying to maintain code simpliciy and clarity.

This project tries to reduce boilerplate when writing Elixir GenServers by making use of language metaprogramming capabilities.

Online documentation can be found here.

Installation

If available in Hex, the package can be installed as:

  1. Add stone to your list of dependencies in mix.exs:
```elixir
def deps do
  [{:stone, "~> 0.2.0"}]
end
```
  1. Ensure stone is started before your application:
```elixir
def application do
  [applications: [:stone]]
end
```

Functionality

This project helps remove boilerplate common when implementing GenServer behaviour in Elixir. In particular, it can be useful in following situations:

  • start function just packs all arguments into a tuple which it forwards to init/1 via GenServer.start
  • Calls and casts interface functions just forward all arguments to the server process via GenServer.call and GenServer.cast.

For other cases, you may need to use plain GenServer functions (which can be used together with Stone macros). Stone is not meant to fully replace GenServer. It just tries to reduce boilerplate in most common cases.

Usage Examples

Let's take a look at the following server definition:

defmodule CounterAgent do
  use Stone.GenServer

  defstart start_link(val \\ 0) do
    initial_state(val)
  end

  defcall get(), state: state do
    reply(state)
  end

  defcall inc(), state: state do
    reply_and_set(state, state+1)
  end

  defcall add(x), state: state do
    reply_and_set(state + x, state + x)
  end

  defcast set(value) do
    noreply_and_set(value)
  end
end

Above code defines a simple GenServer that maintains a counter, and exposes a convenient interface to be used by other processes. Without using a library, this code would look like that:

defmodule CounterAgent do
  use GenServer
  
  def start_link(val \\ 0, opts \\ []) do
    GenServer.start_link(CounterAgent, {val}, opts)
  end
  
  def init({val}) do
    {:ok, val}
  end
  
  def get(pid) do
    GenServer.call(pid, {:get})
  end
  
  def handle_call({:get}, _from, state) do
    {:reply, state, state}
  end
  
  def inc(pid) do
    GenServer.call(pid, {:inc})
  end
  
  def handle_call({:inc}, _from, state) do
    {:reply, state, state+1}
  end
  
  def set(pid, value \\ 0) do
    GenServer.cast(pid, {:set, value})
  end
  
  def handle_cast({:set, value}, _from, _state) do
    {:noreply, value}
  end
end

About

Metaprogramming utilities for the Elixir language.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages