Awesome
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
.
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.
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
.
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