Russian translation on Novemberain.com
While experimenting with Erluby (Ruby in Erlang) I got a feeling that I do not want to write specs in Erlang. RSpec is finally much better than missingbdd/erlang and I pesonally think that Ruby is a more expressive language.
That’s why I’ve though about some kind of bridge between erlang and ruby. I’ve googled for it and found Rebar and even its “secret” sources. How it works? It’s basically a JSON server implemented in Erlang. That’s basically nice, though it seems to be a deep allpha version and it does not support some Erlang-specific types like pids.
And I’ve decided to think more about how to make it better. I’ve resulted in throwing Rebar off and toying with the following idea: Ruby sends Erlang code to Erlang server and Erlang server returns Ruby code.
I’ve hacked Rebar runner a bit and here is what I’ve got:
-module(erluby_bridge).
-export([start/0, handle/1, loop/1]).
%% parse
eval_erlang_expr(Expr) ->
{ok, Tokens, _} = erl_scan:string(Expr),
{ok, [Form]} = erl_parse:parse_exprs(Tokens),
{value, Result, _} = erl_eval:expr(Form,[]),
{ok, Result}.
encode_to_ruby(E) when is_list(E) -> list_to_binary("'" ++ E ++ "'");
encode_to_ruby(E) when is_binary(E) -> encode_to_ruby(binary_to_list(E));
encode_to_ruby(E) when is_integer(E) -> list_to_binary(integer_to_list(E));
encode_to_ruby(E) when is_float(E) -> list_to_binary(float_to_list(E));
encode_to_ruby(E) when is_pid(E) -> list_to_binary("ErlPid.new('" ++ pid_to_list(E) ++ "')").
%% server
start() ->
{ok, LSock} = gen_tcp:listen(9900, [binary, {packet, 0}, {active, false}]),
Pid = spawn(erluby_bridge,loop,[LSock]),
register(erluby_bridge, Pid),
Pid.
loop(LSock) ->
{ok, Sock} = gen_tcp:accept(LSock),
spawn(erluby_bridge, handle, [Sock]),
loop(LSock).
handle(Sock) ->
% read the request from the socket
{ok, Bin} = gen_tcp:recv(Sock, 0),
% call the function
try
{ok, Return} = eval_erlang_expr(binary_to_list(Bin)),
% send the response
gen_tcp:send(Sock, encode_to_ruby(Return))
catch
error:Err ->
Stack = lists:flatten(io_lib:fwrite("~p", [{Err, erlang:get_stacktrace()}])),
gen_tcp:send(Sock, encode_to_ruby(Stack));
_:Ouch ->
io:format("~p~n!!!",[Ouch])
end,
ok = gen_tcp:close(Sock).
and on Ruby side:
require 'rubygems'
gem 'activesupport'
require 'active_support'
class Object
def to_erl
to_s
end
end
class String
def to_erl
'"' + to_s + '"'
end
end
class ErlPid
def initialize(binary)
@binary = binary
end
def to_erl
'list_to_pid("' + @binary + '")'
end
def to_s
@binary
end
end
class Symbol
def to_erl
"'#{to_s}'"
end
end
class Erluby
def initialize(host, port=9900, mod="erlang")
@host, @port, @mod = host, port, mod
end
def __invoke__(sym,*args)
sym = sym.to_s
if args.empty? and sym.camelize == sym
Erluby.new(@host,@port, sym.tableize.singularize)
else
erl_expr = "#{@mod}:#{sym}(#{args.collect(&:to_erl).join(',')})."
sock = TCPSocket.new(@host,@port)
sock.write(erl_expr)
res = sock.gets
eval(res)
end
end
def __send_message__(pid,msg)
self.Erlang.__invoke__("send",pid,msg)
end
def method_missing(sym,*args)
__invoke__(sym,*args)
end
end
All this stuff is far away from being polished (it’s very hackish) but it allows me to play with Erlang from Ruby. Something like:
@erluby = Erluby.new("127.0.0.1",9900)
puts @erluby.Base64.encode("havanagila")
puts @erluby.self
@erluby.__send_message__(:main,3) # unless whereis(main) == undefined




