Elixir и Ruby — не ссорьтесь. Взаимодействуйте! (ExPort/ErlPort)

от автора

Наконец-то свершилось, я больше не разрабатываю приложения на Rails. Теперь Elixir — мой основной инструмент, и я рад этому.
Тем не менее всё ещё существует множество наработок в Ruby-мире, аналогов которых пока нет в Elixir. Чтобы обойти эту проблему, я решил выяснить могут ли оба мира взаимодействовать друг с другом. И ответ — да! Они могут.
Я попробую описать несколько способов как это сделать.

Чтобы не смешивать всё в одну кучу, материал будет разбит на несколько статей, каждая из которых будет описывать только один способ. Первая — про ExPort / ErlPort.
Примечание: ExPort также позволяет взаимодействовать с Python.

Этот способ запускает интерпретатор Ruby и предоставляет вспомогательный API для преобразования типов.

Итак, давайте создадим простое приложение на Elixir, использующее Ruby-код.

$ mix new elixir_ruby_app $ cd elixir_ruby_app

Добавьте пакет export в списки deps и applications в mix.exs

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

Скачайте зависимости

$ mix deps.get

Готово. Теперь давайте создадим директорию для Ruby-скриптов. Я выбрал priv, посколько она будет включена в релизную сборку, генерируемую exrm или distilery.

$ mkdir -p priv/ruby

Создайте скрипт fancy.rb в этой директории со следующим содержимым:

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

Код, вызывающий функцию из Ruby, выглядит так:

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

Настало время волшебства. Запустите iex и вызовите функцию:

$ 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}

Бац! Мы получили результат из Ruby. Вы можете использовать любые Ruby gems, которые захотите. Например, я использую гем icalendar для разбора ical-файлов.

Но мы ещё не закончили! Как насчёт вызова Elixir-функций из Ruby?
Давайте попробуем! Создайте файл fancy.ex:

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

Хорошо, мы передумали и теперь хотим суммировать числа в Elixir, но внутри Ruby. Безумная идея, но почему бы и нет? Изменим наш файл fancy.rb, чтобы он вызывал функцию из Elixir:

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

Запустим снова iex и посмотрим, что произойдёт.

$ 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}

Наша безумная идея сработала без проблем. Прекрасно.

Существуют и другие способы вызывать Ruby из Elixir и наоборот. Но о них мы поговорим в следующих статьях.

ссылка на оригинал статьи https://habrahabr.ru/post/316084/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *