Protoplasm makes is easy to define an RPC server/client which is backed by protobuf through Beefcake.

Defining your service endpoints

The current service model is very simple. You can send only one type of protobuf object (the request object). This object must have an enum and a series of optional command fields to allow it to send commands. Here is an example of how to create a request object type:

class Command
  include Beefcake::Message
  module Type
    PING   = 1
    UPCASE = 2
  required :type,           Type,          1
  optional :ping_command,   PingCommand,   2
  optional :upcase_command, UpcaseCommand, 3

In this case, your request object would be able to accept one, and only one subcommand object. Those types are PingCommand, UpcaseCommand and EvenCommand.

So, in order to mark this Command class as your request class, you'd do the following:

module Types
  include Protoplasm::Types

  # .. your actual classes would go here

  request_class Command
  request_type_field :type

Defining your response objects

Every subcommand can choose to relay back no objects, one object, or stream any number of objects. Those objects must all be of the same type.

To define which objects you expect back, you must add the following.

module Types
  rpc_map Command::Type::PING,   :ping_command,   nil
  rpc_map Command::Type::UPCASE, :upcase_command, UpcaseResponse
  rpc_map Command::Type::EVEN,   :even_command,   EvenResponse,  :streaming => true

In this case, this would define the ping command as returning no object, the upcase command returns a single object, of type UpcaseResponse, and the even command return any number of EvenResponse objects.

Server implementation

Currently there is a single server implementation EMServer, which defines a non-blocking EventMachine based server. To create an EMServer, you subclass Protoplasm::EMServer and setup handlers for each of your command types. For example, a worker server could look like this:

class Server < Protoplasm::EMServer
  def process_ping_command(ping_command)
    # do nothing

  def process_upcase_command(upcase_command)
    send_response(:response => cmd.word.upcase)

  def process_even_command(even_command)
    ( do |num|
      send_response(:num => num) if num % 2 == 0

This server then could be started with Server.start(3000) which would start on port 3000 and process requests.


Currently there is a single client implementation: BlockingClient. It defines a blocking TCPSocket based client. To create a client for this example, you would do the following.

class Client < Protoplasm::BlockingClient
  def initialize(host, port)
    super(Types, host, port)

  def ping

  def upcase(word)
    send_request(:upcase_command, :word => word).response

  def evens(top)
    send_request(:even_command, :top => top) { |resp| yield resp.num }

Look at the full example under test/test_helper.rb.