12 December 2016 - 2 minutes read
The third method that allows us to communicate Ruby and Elixir is Erlectricity. It’s only a Ruby gem that you have to open an Erlang Port yourself, also you have to take care of data conversion (on Elixir side). Erlport using same trick underneath. Regardless I want do describe how to connect to Ruby with Erlectricity. Let’s do it!
Install erlectricity gem
gem install erlectricity $
Generate new elixir project and directory for ruby script
mix new erlectricity_app
$ cd erlectricity_app
$ mkdir priv
$ mkdir priv/ruby $
In this example, we also try, to sum up two integers in ruby.
'rubygems'
require 'erlectricity'
require
do |f|
receive :sum_two_integers, Integer, Integer]) do |a, b|
f.when([:result, [:ok, a+b]])
f.send!([
f.receive_loopend
end
As you can see, Ruby DSL is more like the Elixir. We’re creating a receive loop, match incoming messages, and if matched, send a message back.
I this example, we’re waiting for the message like this {:sum_two_integers, 1, 2}
and send back message like {:result, {:ok, 3}}
.
Moving to Elixir part, we have to create a function that will open an Erlang Port to ruby process and send the message to this process.
defmodule ErlectricityApp do
@ruby_echo Application.app_dir(:erlectricity_app, "priv/ruby/sum.rb")
@command "ruby #{@ruby_echo}"
def sum_two_integers_in_ruby(one, another) do
= Port.open({:spawn, @command}, [{:packet, 4}, :nouse_stdio, :exit_status, :binary])
pid
= {:sum_two_integers, one, another} |> encode_data
encoded_msg
|> Port.command(encoded_msg)
pid
receive do
:data, data}} ->
{_, {case data |> decode_data do
:result, result} -> result
{-> {:error, "Unknown message"}
_ end
end
end
defp encode_data(data) do
|> :erlang.term_to_binary
data end
defp decode_data(data) do
|> :erlang.binary_to_term
data end
end
What exactly going on here ? We need to know where is our ruby script. @ruby_echo
stores that data. Next one is @command
, this one stores command that will start or ruby process. Which is ruby with the path to the ruby file.
Our function first launches an Erlang port. Spawn a process with parameters.More about these parameters you can read here.
Because we’re using port protocol, we must encode our data. encode_data
function will do it for us.
Finally, we can send a message to our ruby process and wait for a response. Yes, all things are happening asynchronously.
When we receive a message from Ruby process, we need to decode them withdecode*data function, match if it is a {:result, *}
message, and return the result.
Fire up iex
and check if it works
$ iex -S mix
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
Compiling 1 file (.ex)
Interactive Elixir (1.3.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> ErlectricityApp.sum_two_integers_in_ruby(1,2)
{:ok, 3}
iex(2)> ErlectricityApp.sum_two_integers_in_ruby(5,2)
{:ok, 7}
iex(3)>
Work as expected. We’ve made it. Simple right ?