Elixir, Ruby, don’t fight. Talk… with Export/Erlport

22 November 2016 ・ Reading time: 2 minutes

There are still a lot of things in Ruby world, that don’t exist in Elixir yet. To resolve that problem. I’ve done a research. Tried to figure it out if both worlds can talk to each other. And the answer is yes. They can. I’ll try to describe few ways how to do it.

I decided to split things up, each article describes one method, the first one is about Export / Erlport.

This method launches Ruby interpreter and provides some API to convert types between each other.

So create a simple Elixir application that uses a Ruby gem.

$ mix new elixir_ruby_app
$ cd elixir_ruby_app

Add export package to deps and applications in mix.exs

def application do
  [applications: [:export]]
end

def deps do
  [
    {:export, ~> 0.0.7},
    {:erlport, github: hdima/erlport, manager: :make}
  ]
end

And download dependencies

$ mix deps.get

Done. Moving further, create a directory for ruby scripts. I choose priv because it will be included in release package generated by exrm or distilery.

$ mkdir priv
$ mkdir priv/ruby

Create our fancy.rb ruby script in that directory.

def sum_two_integers(one, another)
  result = one + another
  Tuple.new(
    [:ok, result]
  )
end

Function that invoke our ruby code looks like this:

defmodule ElixirRubyApp do
  @ruby_dir Application.app_dir(:elixir_ruby_app, "priv/ruby")

  use Export.Ruby

  def sum_two_integers_in_ruby(one, another) do
    {:ok, ruby} = Ruby.start(ruby_lib: @ruby_dir)

    ruby
    |> Ruby.call(sum_two_integers(one, another), from_file: "fancy")
  end
end

It’s time to magic happen, run iex and run function.

$ iex -S mix
erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.3.3) — press Ctrl+C to exit (type h() ENTER for help)
iex(1)> ElixirRubyApp.sum_two_integers_in_ruby(1,2)
{:ok, 3}
iex(2)>

Bang! Got a result from Ruby. You can use now all rubygems that you want. For example I’m using icalendar gem to parse ical files.

But we’re not finished yet! What about calling elixir functions from ruby?

Let’s try! Add fancy.ex file:

defmodule ElixirRubyApp.Fancy do
  def sum_two_integers_in_elixir(one, another) do
    result = one + another
    {:ok, result}
  end
end

OK, We’re changed our mind, now we want to sum these integers in Elixir, but within Ruby. Crazy idea, but why not? Change our fancy.rb file to call a function from Elixir.

include ErlPort::Erlang

def sum_two_integers(one, another)
  call('Elixir.ElixirRubyApp.Fancy'.to_sym, :sum_two_integers_in_elixir, [one, another])
end

Again, run iex, and see what will happen.

$ iex -S mix
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.3.3) — press Ctrl+C to exit (type h() ENTER for help)
iex(1)> ElixirRubyApp.sum_two_integers_in_ruby(14,2)
{:ok, 16}
iex(2)>

Double bang! Our crazy idea just work. Beautiful. We’re done. As you may notice, export also work with python.