Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/fishcakez/core

Library for selective receive OTP processes
https://github.com/fishcakez/core

Last synced: 29 days ago
JSON representation

Library for selective receive OTP processes

Awesome Lists containing this project

README

        

# Core
Library for implementing OTP processes natively in Elixir.

Provides utility functions and macros to implement 100% OTP compliant
processes with 100% compatibility with all Erlang/OTP modules and tools.

# Installing
```
git clone https://github.com/fishcakez/core.git
cd core
mix do deps.get, docs, compile
```

# Hello World
Start a process that prints "Hello World" to `:stdio`.
```elixir
defmodule HelloWorld do

use Core

def start_link(), do: Core.start_link(__MODULE__, nil)

def init(_parent, _debug, _args) do
IO.puts("Hello World")
# Core.init_ack/0 will cause start_link/0 to return { :ok, self() }. If this
# function is never called start_link will block until this process exits.
Core.init_ack()
exit(:normal)
end

end
```

# Features
* Asynchronous and synchronous process initiation (with name registration).
* Automatic logging of un-rescued exceptions.
* System calls that work with any OTP compliant process.
* Receive macro to handle system messages.
* Supports progressive enhancement of OTP features: system message
automatically handled until you want to change the default behaviour.

# Basic Ping Server
Starts a process that can be pinged.
```elixir
defmodule PingPong do

use Core

@spec ping(Core.t) :: :pong
def ping(process), do: Core.call(process, __MODULE__, :ping, 5000)

@spec count(Core.t) :: non_neg_integer
def count(process), do: Core.call(process, __MODULE__, :count, 5000)

@spec close(Core.t) :: :ok
def close(process), do: Core.call(process, __MODULE__, :close, 5000)

@spec start_link() :: { :ok, pid }
def start_link() do
Core.start_link(__MODULE__, nil)
end

# Core api

def init(_parent, _debug, _args) do
Core.init_ack()
loop(0)
end

## Internal

defp loop(count) do
receive do
{ __MODULE__, from, :ping } ->
Core.reply(from, :pong)
loop(count + 1)
{ __MODULE__, from, :count } ->
Core.reply(from, count)
loop(count)
{ __MODULE__, from, :close } ->
Core.reply(from, :ok)
terminate(:normal)
end
end

defp terminate(reason) do
exit(reason)
end

end
```

# Advanced Ping Server
Starts a process that can be pinged, live debugged and live code
upgraded.

For example `Core.Sys.set_state(pid, 0)` will reset the `count` to `0`.
```elixir

defmodule PingPong do

use Core.Sys

@spec ping(Core.t) :: :pong
def ping(process), do: Core.call(process, __MODULE__, :ping, 5000)

@spec count(Core.t) :: non_neg_integer
def count(process), do: Core.call(process, __MODULE__, :count, 5000)

@spec close(Core.t) :: :ok
def close(process), do: Core.call(process, __MODULE__, :close, 5000)

# die/1 will print alot of information because the exit reason is abnormal.
@spec die(Core.t) :: :ok
def die(process), do: Core.call(process, __MODULE__, :die, 5000)

@spec start_link() :: { :ok, pid }
def start_link() do
Core.start_link(nil, __MODULE__, nil,
[{ :debug, [{ :log, 10 }, { :stats, true }] }])
end

## Core api

def init(parent, debug, _args) do
Core.init_ack()
loop(0, parent, debug)
end

## Core.Sys (minimal) api

def system_continue(count, parent, debug), do: loop(count, parent, debug)

def system_terminate(count, parent, debug, reason) do
terminate(count, parent, debug, reason)
end

## Internal

defp loop(count, parent, debug) do
Core.Sys.receive(__MODULE__, count, parent, debug) do
{ __MODULE__, from, :ping } ->
# It is not required to record events using `Core.Debug.event/1` but is
# a useful debug feature that is compiled to a no-op in production.
debug = Core.Debug.event(debug, { :in, :ping, elem(from, 0) })
Core.reply(from, :pong)
debug = Core.Debug.event(debug, { :out, :pong, elem(from, 0) })
count = count + 1
debug = Core.Debug.event(debug, { :count, count })
loop(count, parent, debug)
{ __MODULE__, from, :count } ->
debug = Core.Debug.event(debug, { :in, :count, elem(from, 0) })
Core.reply(from, count)
debug = Core.Debug.event(debug, { :out, count, elem(from, 0) })
loop(count, parent, debug)
{ __MODULE__, from, :close } ->
debug = Core.Debug.event(debug, { :in, :close, elem(from, 0) })
Core.reply(from, :ok)
debug = Core.Debug.event(debug, { :out, :ok, elem(from, 0) })
terminate(count, parent, debug, :normal)
{ __MODULE__, from, :die } ->
debug = Core.Debug.event(debug, { :in, :die, elem(from, 0) })
Core.reply(from, :ok)
debug = Core.Debug.event(debug, { :out, :ok, elem(from, 0) })
terminate(count, parent, debug, :die)
end
end

defp terminate(count, parent, debug, reason) do
event = { :EXIT, reason }
debug = Core.Debug.event(debug, event)
Core.stop(__MODULE__, count, parent, debug, reason, event)
end

end
```