Integrating Cizen with Phoenix
By: Yuki Kodama
Tags: Cizen, Tutorial, Elixir, Phoenix Framework

Do you know Cizen? It’s an Elixir library that helps you to develop applications that consist of automata who communicate by passing events to each other. The author of Cizen has published an article titled “Asynchronous Request and Connection Pooling in Elixir with Cizen”.

But, wait. Is Cizen a library for specific purposes? Absolutely NOT! Just like a meaning of Cizen (“自然” in Japanese, “Nature” in English), it can be utilized every scene in application development. In this article, I’d like to introduce an integration of Cizen and Phoenix Framework though building a tiny web application “Hello World” (github repo). 🚀

I assume you have already installed requirements below.

Elixir 1.7.3 (Erlang/OTP 21)

Phoenix 1.4.0

If not, please install Elixir and Phoenix before started. To upgrade Phoenix from 1.3.x to 1.4.0, don’t forget to uninstall project generator before install.

Hello World 🌐

Before diving into the Cizen world, I’d like to explain what I want to achieve with this example. “Hello World” is a simple web application that accepts HTTP GET request and returns an HTML response including a text “Hello World”. Uh…where is Cizen? Don’t worry, Cizen has a responsibility of a core application; this means that a content of the HTTP response is provided from a Cizen’s automaton.

Overview of Hello World application

Let’s create “Hello World” application with Phoenix’s project generator. In this application, we don’t use database backends (ecto) and JavaScript assets (webpack since 1.4.0, formerly brunch). Therefore we drop them on scaffolding.

mix phx.new hello_world --no-webpack --no-ecto

Run dev server and open in your browser to check it works fine.

cd hello_world

mix phx.server

Congrats! 🎉

To add Cizen to this project, open mix.exs file with your favorite editor and add {:cizen, "~> 0.12.5"} to dependencies section.

defp deps do

[

{:cizen, "~> 0.12.5"},

{:phoenix, "~> 1.4.0"},

{:phoenix_pubsub, "~> 1.1"},

{:phoenix_html, "~> 2.11"},

{:phoenix_live_reload, "~> 1.2", only: :dev},

{:gettext, "~> 0.11"},

{:jason, "~> 1.0"},

{:plug_cowboy, "~> 2.0"}

]

end

Save the file, run mix deps.get to install new dependencies, and restart Phoenix dev server. To simplify the HTML template, remove unnecessary part of layout/app.html.eex and page/index.html.eex .

app.html.eex

index.html.eex

<h1>Hello World</h1>

Let’s dive into Cizen part. An automaton is a primitive entity of the world. In a Cizen way of design, we think what kind of automata is needed and how the automata behave through receiving and dispatching events.

Here is a specification for our automaton.

The name is GreetingAutomaton

Receives Greeting event

event Dispatches Reply event with a text message

That’s all! It’s time to write code. 😁 First, we define the Greeting event.

defmodule HelloWorld.Greeting do

defstruct []

end

Cizen’s events are not special, it’s just an Elixir struct. Because of the Greeting event doesn’t have a payload, defstruct declaration leaves blank.

In contrast with the Greeting event, the definition of Reply event is a bit different. To represent communications such as a pair of request and response, Cizen provides a feature named “Requestive Events”. Use Cizen.Reuqest behavior and define the Reply response event using defresponse macro. Notice that message field is also defined in it.

defmodule HelloWorld.Greeting do

defstruct [] use Cizen.Request

defresponse Reply, :greeting_id do

defstruct [:greeting_id, :message]

end

end

message field is used for containing a text message, but umm…what is greeting_id field? Can we remove it? No. It’s important for the feature because Cizen needs to pick a response event up from tons of unrelated events in the world.

Define GreetingAutomaton that responds the Greeting event and returns the Reply response event with a message.

alias Cizen.Effects.{Receive, Subscribe, Dispatch}

alias Cizen.{Event, Filter} defmodule HelloWorld.GreetingAutomaton do

use Cizen.Automaton defstruct [] @impl true

def spawn(id, _) do

# Subscribe "Greeting" event

perform id, %Subscribe{

event_filter: Filter.new(

fn %Event{body: %HelloWorld.Greeting{}} -> true end

)

} # No state in this automaton

:loop

end @impl true

def yield(id, :loop) do

# Wait for "Greeting" event

event = perform id, %Receive{} # Respond to "Greeting" event

perform id, %Dispatch{

body: %HelloWorld.Greeting.Reply{

greeting_id: event.id,

message: "Hello Cizen"

}

}

:loop

end

end

Oops…lots of code. Don’t be afraid, let’s take a look at the detail.

To define Cizen’s automata, use Cizen.Automaton behavior and implement some lifecycle callbacks; spawn/2 and yield/2 . spawn/2 is called on start. It’s useful for an initialization such as subscribing events and determining an initial state of an automaton. By the way, defstruct of an automaton is used for passing parameters to automaton on launch.

Cizen’s automata need to interact with others via performing “Effects”. To subscribe Greeting events, we perform Subscribe effect with a filter that passes only Greeting events. yield/2 is called after returning an initial state from spawn/2 callback. Thanks to Elixir’s awesome pattern matching, we can also define multiple yield/2 based on the state (second argument of yield/2 ).

yield/2 behaves like a general event loop; waits until arriving an event, then does something, yields new state, and loops infinitely. GreetingAutomaton receives Greeting event by performing Receive effect, and then dispatches Greeting.Reply response event that contains greeting_id (comes from Greeting event) and a message “Hello Cizen” by performing Dispatch effect.

Place above code in lib/hello_world/hello.ex (source code). Don’t forget adding a bootstrap code to lib/hello_world/application.ex (source code) in order to start the GreetingAutomaton as part of a supervision tree.

defmodule HelloWorld.Application do

@moduledoc false use Application def start(_type, _args) do

children = [

HelloWorldWeb.Endpoint,

%{

id: HelloWorld.GreetingAutomaton,

start: {Cizen.Saga, :start_link,

[%HelloWorld.GreetingAutomaton{}]}

}

] opts = [strategy: :one_for_one, name: HelloWorld.Supervisor]

Supervisor.start_link(children, opts)

end [...]

end

By the way, where Greeting event comes from? It’s Phoenix world. Let’s connect the both worlds. Open controllers/page_controller.ex file (source code).

alias Cizen.Effects.Request defmodule HelloWorldWeb.PageController do

use HelloWorldWeb, :controller

use Cizen.Effectful def index(conn, _params) do

%{body: %{message: message}} = handle fn id ->

perform id, %Request{body: %HelloWorld.Greeting{}}

end render(conn, "index.html", message: message)

end

end

Cizen.Effectful is useful to create a block which performs effects. In the block of handle , perform Request effect with Greeting event and block until receiving Reply response event from GreetingAutomaton . This process runs every time Phoenix gets a request. To render a greeting message, update templates/page/index.html.eex file.

<h1><%= @message %></h1>

Finally, we get a greeting from GreetingAutomaton in the Cizen world.

As a conclusion, summarize what I wanted to present in this article.

Automata drive only with Events

drive only with Automata interact with others through performing Effects

Use Cizen.Effectful to interact with the Cizen world

Thanks for reading. Enjoy Cizen! 🎉