Yeah, that's right, we released a new version of N2O for Elixir. As this is june and N2O is 6 years old it is 6.6.6 :-) Generally speaking, it was already so long time ago, I just dropped Elixir support for a focusing on something meaningful at least at Erlang side.

By full N2O Elixir support I mean Erlang records auto import with module usage, and proxying, recoloring calls from :n2o.sid() to N2O.sid(), etc. Also we assume mix.exs contains application entry point definition and other vital stuff but no more! As N2O came from the land of keeping ass clean, we encourage only vital macros at the top of the Sample.Index page:

defmodule Sample.Index do use N2O, with: [:kvx, :n2o, :nitro]

The minimal sys.config in Elixir config.exs syntax will have only proper KVX setup for custom DB layer (using kvx_st as a custom stream backend implementation for RocksDB), NITRO protocol and Sample.Routes.

config :n2o, protocols: [:n2o_nitro], routes: Sample.Routes config :kvx, dba: :kvx_rocks, dba_st: :kvx_st, schema: [:kvx,:kvx_stream]

We are using plain binary pattern match as a fastest way of routing, but you may use any code at this level:

defmodule Sample.Routes do import N2O def finish(state, cx), do: {:ok, state, cx} def init(state, ctx) do %{path: p} = cx(ctx,:req) {:ok, state, cx(ctx, path: p, module: route_prefix(p))} end def route_prefix(<<"/ws/", p::binary>>), do: route(p) def route_prefix(<<"/", p::binary>>), do: route(p) def route_prefix(p), do: route(p) def route(<<>>), do: Sample.Login def route(<<"app/index", _::binary>>), do: Sample.Index def route(<<"app/login", _::binary>>), do: Sample.Login def route(_), do: Sample.Login

The actual page that contains direct embedding of N2O JavaScript SDK from CDN is following and is included as a static file for cowboy serving (could also be obtained from CDN):


<header> <h2 id=heading>room</h2> <button id=logout>logout</button> </header> <main> <form> <textarea id=message rows='4' autofocus placeholder="Just type what you think about this"> </textarea> <button id=upload>Browse</button> <button id=send>chat</button> </form> <history id="history"> </history> </main>

JavaScript CDN ws.n2o.dev:

<script src='https://ws.n2o.dev/priv/utf8.js'></script> <script src='https://ws.n2o.dev/priv/bert.js'></script> <script src='https://ws.n2o.dev/priv/heart.js'></script> <script src='https://ws.n2o.dev/priv/ieee754.js'></script> <script src='https://ws.n2o.dev/priv/n2o.js'></script> <script>host = location.hostname === 'sample.n2o.dev' ? 'ns.synrc.com' : location.hostname; port = 8001;</script> <script src='https://ws.n2o.dev/priv/ftp.js'></script> <script src='https://ws.n2o.dev/priv/nitro.js'></script> <script>$io.do = function(r) { console.log(r); };</script> <script>protos = [$bert]; N2O_start();</script>


The code that mounts JavaScript postback events that delivers source DOM information to server is here. On connect page should sent 'N2O' init marker that calls the :init case of event function in Sample.index module:

def event(:init) do room = N2O.session(:room) KVX.save(KVX.writer(NITRO.to_binary(room))) N2O.reg({:topic,room}) N2O.reg(:n2o.sid()) NITRO.clear (:history) NITRO.update(:upload, upload()) NITRO.update(:heading, h2(id: :heading, body: room)) NITRO.update(:logout, button(id: :logout, postback: :logout, body: "Logout")) NITRO.update(:send, button(id: :send, body: "Chat", postback: :chat, source: [:message])) Enum.each KVX.all(room), fn {:msg,_,u,m} -> event({:client,{u,m}}) end end

It contains KVX cursor initialization for that particular room feed and also proceed GPROC/SYN subscriptions for two topics: {:topic, room} and :n2o.sid() --- authorized WebSocket process. Note that in case of updat DOM elements they must already be present in page otherwise you will get an error exception. At the botton of :init Nitrogen event you can see database retrival of recorded messages. All of them are being passed trough {:client,_} event.


def event({:client,{usr,msg}}) do NITRO.wire(jq(target: :message,method: [:focus,:select])) NITRO.insert_top(:history, message(body: [author(body: usr), msg])) end

In this event it simply adds the DOM element to the :history div.


The main event of the Sample.Index application module is the :chat. It retrives room and user names from session and message from client's sent event, than stores it in the KVX and notify the render.

def event(:chat) do room = N2O.session(:room) usr = N2O.user() msg = NITRO.q(:message) KVX.save(KVX.add(writer(KVX.writer(room), args: {:msg,KVX.seq([],[]),usr,msg}))) N2O.send({:topic,room},client(data: {usr,msg})) end

Next version of MAD will be able to produce Elixir skeleton code. The sources of working SAMPLE ported to Elixir is here:


Brought to you by:

am-kantox — Aleksei Matiushkin
bushyn — Arseniy Bushyn
lessless — Yevhenii Kurtov
5HT — Namdak Tonpa