Ruby code in Elixir project

Did you know that you can fire up ruby code inside an Elixir project?

I haven’t done any benchmarks, but I have a feeling it would be too slow for production, however, coding and trying out crazy stuff is fun, right?

Let’s jump into it by creating a new project:

~/$ mix new ruby_elixir
* creating README.md
* creating .gitignore
* .....

and cd into our project folder.

We are going to use ExPort - a wrapper for Erlport, a library for Erlang which helps connect Erlang to Python and Ruby. Now, I’m a Rubyist, so this blog post will be written in Ruby and Elixir.

To install the Export package, we’ll need to add it to our dependencies list:

# mix.exs
defp deps do
[{:export, “~> 0.0.7”},
{:erlport, github: “hdima/erlport”, manager: :make}]
end

We’ll then run mix deps.get:

~/ruby_elixir$ mix deps.get
* Getting erlport (https://github.com/hdima/erlport.git)
.......
Using locally cached package

So, in our lib folder, we’ll create two new folders - one named ruby, and one named ruby_elixir. Within the ruby_elixir folder, we’ll create a file called ruby_call.ex, and write the following into it:

# lib/ruby_elixir/ruby_call.ex
defmodule RubyElixir.RubyCall do
use Export.Ruby
  def ruby_call do
{:ok, ruby} = Ruby.start(ruby_lib: Path.expand(“lib/ruby”))
end
end

Cool. So first we’re using the use macro to import the ExPort’s Ruby library, then we’ll tell our ruby_call/0 function where our Ruby folder is located.

In our ruby folder, we’ll create another file called ruby.rb, and write the following into it:

# lib/ruby/ruby.rb
def hello_world
puts “Hello from Ruby”
end

Now, back to our ruby_call.ex file:

# lib/ruby_elixir/ruby_call.ex
......
def ruby_call do
{:ok, ruby} = Ruby.start(ruby_lib: Path.expand(“lib/ruby”))
  ruby
|> Ruby.call(“ruby”, “hello_world”, [])
end

Ruby.call takes 4 parameters. The ruby folder we got from the tuple, the filename, the ruby method, and the parameters. Since our ruby method doesn’t have any parameters, we should be good to go. Let’s fire up iEX and try it out:

~/ruby_elixir$ iex -S mix
iex(1)> RubyElixir.RubyCall.ruby_call
Hello from Ruby
:undefined

Sweet. It worked! Let’s try with some parameters:

# lib/ruby/ruby.rb
......
def hello(name)
puts “Hello #{name}”
end
--------------------------------------------------------------------
# lib/ruby_elixir/ruby_call.ex
......
def ruby_call(name) do
{:ok, ruby} = Ruby.start(ruby_lib: Path.expand(“lib/ruby”))
ruby
|> Ruby.call(“ruby”, “hello”, [name])
end

So we gave both our ruby method and our elixir function a new parameter name. Let’s see what happens if we run it:

iex> RubyElixir.RubyCall.ruby_call(“Stephan”)
Hello Stephan
:undefined

Pretty awesome. Say we have a killer Ruby gem that we want to include in our Elixir project. Is it possible to do more “sophisticated” things with this? Let’s give it a shot..

I really like Prawn, a very nice PDF gem in Ruby. Now, to use Ruby gems, we need a Gemfile in our root folder. We’ll create one, and write the following in it:

# Gemfile
source ‘https://rubygems.org'
gem ‘prawn’

Now, in our terminal, we’ll run bundle to install our gem:

~/ruby_elixir$ bundle
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Resolving dependencies…
Using pdf-core 0.6.1
......

Sweet! Looks like the gem is installed. Let’s see if we’re able to generate a pdf file with it. We’ll write up a new method in our ruby file:

# lib/ruby/ruby.rb
require "prawn"
......
def generate_pdf
Prawn::Document.generate("prawn_elixir_pdf.pdf") do
text "PDF generated with Ruby, Elixir and Prawn"
end
end

And we’ll change our elixir function accordingly:

# lib/ruby_elixir/ruby_call.ex
......
def ruby_call do
{:ok, ruby} = Ruby.start(ruby_lib: Path.expand(“lib/ruby”))
  ruby
|> Ruby.call(“ruby”, “generate_pdf”, [])
end

Let’s try and run it:

~/ruby_elixir$ iex -S mix
iex> RubyElixir.RubyCall.ruby_call
:undefined

That didn’t tell us much, however, it didn’t blow up, which is always a great thing. Now, if we now take a look in our root folder, we’ve got a new file! prawn_elixir_pdf.pdf. If we open it up, we can see the content of our freshly generated pdf — PDF generated with Ruby, Elixir and Prawn.

Until next time
Stephan Bakkelund Valois