Skip to content

Commit

Permalink
Refactored - Hid the EM calls
Browse files Browse the repository at this point in the history
  • Loading branch information
davebryson committed May 22, 2009
1 parent 608e4cb commit 8873f04
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 112 deletions.
122 changes: 39 additions & 83 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ h2. rinterface: Ruby to Erlang client

rinterface is a pure Ruby client that can send RPC calls to an Erlang node. It's very much a work in progress.


h3. Requirements

* Erlang 12B-5
Expand All @@ -12,114 +11,71 @@ h3. Requirements

h3. Try it out

1. Open a terminal, Cd into the test directory and run:
1. Open a terminal, Cd into the test directory and run:
'make'
'make run'
this will start the erlang node named 'math'

This will start the erlang node named 'math'

2. Open another terminal, and run: 'ruby sample.rb'
2. Open another terminal, and run: 'ruby sample.rb'

h3. How to use?

See 'sample.rb' for an example:

In your Ruby code, make a call to the Erlang node like this:
<pre>
<code>
EM.run do
# Connect to epmd to get the port of 'math'. 'math' is the -sname of the erlang node
epmd = EpmdConnection.lookup_node("math")
epmd.callback do |port|
puts "got the port #{port}"

# make the rpc call to 'math' on port for mod 'math_server' on fun 'add' with args
node = Node.rpc_call("math",port.to_i,"math_server","add",[10,20])
node.callback{ |result|
puts "Sum is: #{result}"
EM.stop
}

node.errback{ |err|
puts "Error: #{err}"
EM.stop
}
end

epmd.errback do |err|
puts "Error: #{err}"
EM.stop
end
end
</code>
</pre>
# Erlang::Node(nodename,module,function,args) => [:ok,Response] | [:badprc,Reason]

r = Erlang::Node.rpc("math","math_server","add",[10,20])

h3. So you wanna call Erlang from your Rails app...

Here's a quick and simple example. Make sure you put the rinterface lib into RAILS_ROOT/lib and start the math_server in 'test'

First we'll wrap rinterface in a class to call from Rails:
if r[0] == :badrpc
puts "Got and Error. Reason #{r[1]}"
else
puts "Success: #{r[1]}"
end
</code>
</pre>

models/math_service.rb
Where:
* 'math' is the node name (the -sname of the Erlang node)
* 'math_server' is the name of the module
* 'add' is the funtion to call
* args is an array of arguments to pass to the function

The result will always be an Array of the form:
<pre>
<code>
require 'lib/rinterface'

class MathService
include Erlang
attr_accessor :result
[:ok,Response] where Response is the result from the Erlang

or

def self.sum(args)
s = self.new
s.call_erlang(args)
s.result
end

def initialize
@result = 0
end

def call_erlang(args)
EM.run do
# Connect to epmd to get the port of 'math'.
# 'math' is the -sname of the erlang node
epmd = EpmdConnection.lookup_node("math")
epmd.callback do |port|

# make the rpc call to 'math' on port for mod 'math_server' on
# fun 'add' with args
node = Node.rpc_call("math",port.to_i,"math_server","add",args)
node.callback{ |result|
@result = result
EM.stop
}

node.errback{ |err|
EM.stop
}
end

epmd.errback do |err|
EM.stop
end
end
end

end
[:badrpc,Reason] where Reason is the 'why' it failed
</code>
</pre>

Now here's the controller:
h3. So you wanna call Erlang from your Rails app...

controllers/math_controller.rb
Here's a quick and simple example. Make sure you put the rinterface lib into RAILS_ROOT/lib and start the math_server in 'test'

In the controller:

controllers/math_controller.rb
<pre>
<code>
require 'lib/rinterface'

class MathController < ApplicationController
def index
a = params[:a]
b = params[:b]
@result = MathService.sum([a.to_i,b.to_i])
r = Erlang::Node.rpc("math","math_server","add",[a.to_i,b.to_i])

if r[0] == :badrpc
@result = "Error"
else
@result = r[1]
end

end
end
</code>
Expand Down
71 changes: 68 additions & 3 deletions lib/erlang/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,68 @@
# - makes the RPC call
#
module Erlang
class Node < EM::Connection
class Node
attr_reader :result

def initialize
@result = nil
end

def self.rpc(node,mod,fun,args)
n = self.new
setup = proc{ n.do_connect(node,mod,fun,args) }

if EM.reactor_running?
setup.call
else
EM.run(&setup)
end
n.result
end

def do_connect(node,mod,fun,args)
epmd = EpmdConnection.lookup_node(node)
epmd.callback do |port|
conn = NodeConnection.rpc_call(node,port,mod,fun,args) do |c|
c.destnode = node
c.mod = mod
c.fun = fun
c.args = args
c.port = port
c.myname = build_nodename
c.cookie = get_cookie
end
conn.callback do |r|
# Check for bad rpc response
# this is where I miss the patten matching in Erlang
if r.is_a?(Array)
if !r.empty? && r[0] == :badrpc
@result = [:badrpc,r[1]]
else
@result = [:ok,r]
end
else
@result = [:ok,r]
end
EM.stop
end
conn.errback do |err|
# never called??
@result = [:badrpc,err]
EM.stop
end
end
epmd.errback do |err|
# return bad RPC no port found (0)
@result = [:badrpc,"no port found for service"]
EM.stop
end
end

end


class NodeConnection < EM::Connection
include EM::Deferrable

attr_accessor :host,:myname,:destnode,:port,:cookie,:mod,:fun,:args
Expand All @@ -27,6 +88,7 @@ def self.rpc_call(node,port,mod,fun,args)
end
end


# Get the Cookie from the home directory
def self.get_cookie
# ... I did it all for the cookie, come on the cookie ...
Expand Down Expand Up @@ -67,11 +129,13 @@ def handle_any_response
decoder.read_any
# read the message
result = decoder.read_any
puts "Raw Response: #{result.inspect}"
#puts "Raw Response: #{result.inspect}"
set_deferred_success result[1]
else
# This seems to never happen...always 'p'
result = decoder.read_any
set_deferred_failure result
end
set_deferred_success result[1]
end

def send_name
Expand Down Expand Up @@ -140,6 +204,7 @@ def make_challenge(her_challenge)
encoder.out.string
end

# Handshake complete...send the RPC
def receive_challenge_ack(packet_size,decoder)
@responder = :handle_any_response
call_remote
Expand Down
40 changes: 14 additions & 26 deletions sample.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
require 'lib/rinterface'


# Example of connecting to an Erlang node and making an RPC call
include Erlang
# Try different responses...

EM.run do
# Connect to epmd to get the port of 'math'. 'math' is the -sname of the erlang node
epmd = EpmdConnection.lookup_node("math")
epmd.callback do |port|
puts "got the port #{port}"

# make the rpc call to 'math' on port for mod 'math_server' on fun 'add' with args
node = Node.rpc_call("math",port.to_i,"math_server","add",[10,20])
node.callback{ |result|
puts "Sum is: #{result}"
EM.stop
}

node.errback{ |err|
puts "Error: #{err}"
EM.stop
}
end

epmd.errback do |err|
puts "Error: #{err}"
EM.stop
end
end
# Bad rpc. Try to call the wrong service
r = Erlang::Node.rpc("math","matx_server","add",[10,20])
puts "Got: #{r.inspect}"

puts "--------"
# No Port for Service. Can't find a port for 'ath'
r = Erlang::Node.rpc("ath","matx_server","add",[10,20])
puts "Got: #{r.inspect}"

puts "--------"
# Good call
r = Erlang::Node.rpc("math","math_server","add",[10,20])
puts "Got: #{r.inspect}"

0 comments on commit 8873f04

Please sign in to comment.