Skip to content
Exos is a simple Port Wrapper : a GenServer which forwards cast and call to a linked Port.
Elixir
Latest commit 06b61cd Nov 16, 2015 @awetzel Soft constraint on Elixir 1.0 version
fix #1
fix #2
Failed to load latest commit information.
config initial commit Sep 16, 2014
lib v1.0.0 Jan 2, 2015
test v1.0.0 Jan 2, 2015
.gitignore initial commit Sep 16, 2014
README.md v1.0.0 Jan 2, 2015
mix.exs Soft constraint on Elixir 1.0 version Nov 16, 2015

README.md

Exos

Exos is a simple Port Wrapper : a GenServer which forwards cast and call to a linked Port. Requests and responses are converted using binary erlang term external representation.

You can use it to create a GenServer for Python, Clojure, NodeJS with :

Launching a Clojure/Python/NodeJS GenServer and use it in Elixir

Usage : Exos.Proc.start_link (see function documentation), then the resulting process is a GenServer where cast and call are binary encoded through stdio to the underlying process. If the GenServer receive messages outside of a call, a GenEvent can be attached to receive these messages as events.

See test/port_example.exs for a reference implementation of a server that can be launched in a port with Exos.Proc, and test/exos_test.exs for its use. clojure/python/node_erlastic projects can be used to launch a java/python/javascript GenServer.

See above an example of an account manager server developped in python/nodejs/clojure.

defmodule Account do
  def cmd do
    case Application.get_env(:account_impl) do
      :python-> "venv/bin/python -u account.py"
      :node-> "node account.js"
      :clojure-> "java -cp 'target/*' clojure.main account.clj"
    end
  end
  def start_link(ini), do: Exos.Proc.start_link(cmd,ini,[cd: "#{:code.priv_dir(:myproj)}/account"],name: __MODULE__)
  def add(v), do: GenServer.cast(__MODULE__,{:add,v})
  def rem(v), do: GenServer.cast(__MODULE__,{:rem,v})
  def get, do: GenServer.call(__MODULE__,:get,:infinity)
end

defmodule MyProj.App do
  use Application
  def start(_,_), do: MyProj.App.Sup.start_link

  defmodule Sup do
    use Supervisor
    def start_link, do: Supervisor.start_link(__MODULE__,[])
    def init([]), do: supervise([
      worker(Account,0)
    ], strategy: :one_for_one)
  end
end

vim mix.exs

def application do
  [mod: { MyProj.App, [] }]
end

Finally just implement your account server in any language as describe below, and use it as a standard GenServer.

iex -S mix

Account.add(5)
Account.rem(1)
4 == Account.get

Account Server Implementation in clojure

mix new myproj
cd myproj ; mkdir -p priv/account; cd priv/account
vim project.clj
(defproject account "0.0.1" 
  :dependencies [[clojure-erlastic "0.2.3"]
                 [org.clojure/core.match "0.2.1"]])
lein uberjar
vim account.clj
(require '[clojure-erlastic.core :refer [run-server]])
(use '[clojure.core.match :only (match)])
(run-server
  (fn [term count] (match term
    [:add n] [:noreply (+ count n)]
    [:rem n] [:noreply (- count n)]
    :get [:reply count count])))

Account Server Implementation in Python >3.4

mix new myproj
cd myproj ; mkdir -p priv/account; cd priv/account
echo "git://github.com/awetzel/python-erlastic.git#egg=erlastic" > requirements.txt
pyvenv venv
./venv/bin/pip install -r requirements.txt
vim account.py
mailbox,port = port_connection()
account = next(mailbox) #first msg is initial state
for req in mailbox:
  if req == "get": port.send(account)
  else:
    (op,amount) = req
    account = (account+amount) if op=="add" else (account-amount)

Account Server Implementation in NodeJS

mix new myproj
cd myproj ; mkdir -p priv/account; cd priv/account
npm init
npm install node_erlastic --save
vim account.js
require('node_erlastic').server(function(term,from,current_amount,done){
  if (term == "get") return done("reply",current_amount);
  if (term[0] == "add") return done("noreply",current_amount+term[1]);
  if (term[0] == "rem") return done("noreply",current_amount-term[1]);
  throw new Error("unexpected request")
});
Something went wrong with that request. Please try again.