From 8f59e921d28214e5448604b08a0cfa8c5fc2c33b Mon Sep 17 00:00:00 2001 From: Arnaud Wetzel Date: Tue, 16 Sep 2014 12:25:42 +0200 Subject: [PATCH] initial commit --- .gitignore | 13 +++++++ README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++ config/config.exs | 24 +++++++++++++ lib/exos.ex | 24 +++++++++++++ mix.exs | 14 ++++++++ test/exos_test.exs | 7 ++++ test/test_helper.exs | 1 + 7 files changed, 166 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config/config.exs create mode 100644 lib/exos.ex create mode 100644 mix.exs create mode 100644 test/exos_test.exs create mode 100644 test/test_helper.exs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb247a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/_build +/deps +*.errlog +erl_crash.dump +*.ez +*_data +*.config +*.swp +.DS_Store +.idea +*gz +*nugs + diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e477ce --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +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 [clojure-erlastic](http://github.com/awetzel/clojure-erlastic), +[python-erlastic](http://github.com/awetzel/python-erlastic), etc. + +## Exemple usage : a clojure calculator ## + +Using [clojure-erlastic](http://github.com/awetzel/clojure-erlastic), your can easily create +a port and communicate with it. + +> mix new myproj + +> cd myproj ; mkdir -p priv/calculator + +> vim project.clj + +```clojure +(defproject calculator "0.0.1" + :dependencies [[clojure-erlastic "0.2.3"] + [org.clojure/core.match "0.2.1"]]) +``` + +> lein uberjar + +> vim calculator.clj + +```clojure +(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]))) +``` + +Then in your project, you can use Exos.Proc GenServer as a proxy to the clojure +port. + +```elixir +defmodule Calculator do + def start_link(ini), do: GenServer.start_link(Exos.Proc,{"#{:code.priv_dir(:myproj)}/calculator","java -cp 'target/*' clojure.main calculator.clj",ini},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(Calculator,0) + ], strategy: :one_for_one) + end +end +``` + +> vim mix.exs + +```elixir +def application do + [mod: { MyProj.App, [] }] +end +``` + +Then you can use the calculator + +> iex -S mix + +```elixir +Calculator.add(5) +Calculator.rem(1) +4 == Calculator.get +``` diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..6dfa82f --- /dev/null +++ b/config/config.exs @@ -0,0 +1,24 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +# This configuration is loaded before any dependency and is restricted +# to this project. If another project depends on this project, this +# file won't be loaded nor affect the parent project. For this reason, +# if you want to provide default values for your application for third- +# party users, it should be done in your mix.exs file. + +# Sample configuration: +# +# config :logger, :console, +# level: :info, +# format: "$date $time [$level] $metadata$message\n", +# metadata: [:user_id] + +# It is also possible to import configuration files, relative to this +# directory. For example, you can emulate configuration per environment +# by uncommenting the line below and defining dev.exs, test.exs and such. +# Configuration from the imported file will override the ones defined +# here (which is why it is important to import them last). +# +# import_config "#{Mix.env}.exs" diff --git a/lib/exos.ex b/lib/exos.ex new file mode 100644 index 0000000..65d81d6 --- /dev/null +++ b/lib/exos.ex @@ -0,0 +1,24 @@ +defmodule Exos.Proc do + use GenServer + + def init({cd,cmd,initarg}) do + port = Port.open({:spawn,'#{cmd}'}, [:binary,:exit_status, packet: 4, cd: '#{cd}']) + send(port,{self,{:command,:erlang.term_to_binary(initarg)}}) + {:ok,port} + end + + def handle_info({port,{:exit_status,0}},port), do: {:stop,:normal,port} + def handle_info({port,{:exit_status,_}},port), do: {:stop,:port_terminated,port} + def handle_info(_,port), do: {:noreply,port} + + def handle_cast(term,port) do + send(port,{self,{:command,:erlang.term_to_binary(term)}}) + {:noreply,port} + end + + def handle_call(term,_,port) do + send(port,{self,{:command,:erlang.term_to_binary(term)}}) + result = receive do {^port,{:data,b}}->:erlang.binary_to_term(b) end + {:reply,result,port} + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..6a8464c --- /dev/null +++ b/mix.exs @@ -0,0 +1,14 @@ +defmodule Exos.Mixfile do + use Mix.Project + + def project do + [app: :exos, + version: "0.0.1", + elixir: "~> 1.0.0", + deps: []] + end + + def application do + [applications: [:logger]] + end +end diff --git a/test/exos_test.exs b/test/exos_test.exs new file mode 100644 index 0000000..26387ba --- /dev/null +++ b/test/exos_test.exs @@ -0,0 +1,7 @@ +defmodule ExosTest do + use ExUnit.Case + + test "the truth" do + assert 1 + 1 == 2 + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start()