Home > Articles > Programming > Ruby

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Blastoff: Hello, World!

Distribunaut uses DRb and Rinda to do most of its heavy lifting. The good news is that because you have already learned all about DRb and Rinda, you can easily jump into experimenting with Distribunaut.

As you'll remember from our look at Rinda, we need to start a RingServer before we can run any code. Distribunaut ships with a convenient binary to help make starting, stopping, and restarting a RingServer easy:

$ distribunaut_ring_server start

If you wanted to stop the RingServer, you would do so with the following command:

$ distribunaut_ring_server stop

You can probably guess how to restart the server. You should restart the RingServer between all these examples, just so things don't go a bit funny on you:

$ distribunaut_ring_server restart

So, with a RingServer running nicely as a daemon in the background, let's kick off things with a simple "Hello World" application. Let's start with a server. Keep in mind that, as we talked about earlier in the book, when we are using DRb and Rinda, applications can act as both a server and a client. So when we use the term "server" here, we are merely using it to describe a bit of code that serves up some content. So what does our HelloWorld class look like with Distribunaut? Let's see:

require 'rubygems'
require 'distribunaut'

configatron.distribunaut.app_name = :hello_world_app

class HelloWorld
  include Distribunaut::Distributable

  def say_hi
    "Hello, World!"
  end

end

DRb.thread.join

First we require rubygems and then the Distribunaut library itself. After that we hit the first of two lines that make Distribunaut special.

Each Distribunaut "application" needs a unique name. When we talk about applications within Distribunaut, we are actually talking about a Ruby VM/process that contains one or more Distribunaut classes. The name of that application should be unique to avoid confusion. We will look at what can happen with redundant application, and class, names a bit later in this chapter.

To manage its configurations, Distribunaut uses the Configatron3 library. We set the application as follows:

configatron.distribunaut.app_name = :hello_world_app

This needs to happen only once per Ruby VM. If you set it multiple times, strange things can happen, so be careful. In our sample code we are setting the application name to :hello_world_app. We could just as easily set it to something like :server1 if we wanted to make it more generic for other Distribunaut classes we were planning on running in the same Ruby VM.

After we have set up our application name, the only other thing we have to do is include the Distribunaut::Distributable module in our HelloWorld class. Then we are ready to try to get a "Hello, World!" remotely.

Before we get to our client code, let's take a quick look at what the preceding HelloWorld class would've looked like had we used raw DRb and Rinda:

require 'rinda/ring'

class HelloWorld
  include DRbUndumped

  def say_hi
    "Hello, World!"
  end

end

DRb.start_service
ring_server = Rinda::RingFinger.primary
ring_server.write([:hello_world_service, :HelloWorld,
                   HelloWorld.new, 'I like to say hi!'],
                  Rinda::SimpleRenewer.new)

DRb.thread.join

Although the HelloWorld class part of it is relatively the same, much more noise is required at the end to get our HelloWorld instance into the RingServer. At this point it is also worth pointing out that in the Rinda version of HelloWorld we had to create a new instance of the class. This means that we can't call any class methods that HelloWorld may have. This includes the ability to call the new method and get a new instance of the HelloWorld class. We are stuck with that instance only. We did not do anything of the sort with the Distribunaut version of the class. In fact, you probably have noticed that we didn't make any calls to get it into the RingServer. We'll talk about why that is shortly. First, let's look at our client code:

require 'rubygems'
require 'distribunaut'

hw = Distribunaut::Distributed::HelloWorld.new
puts hw.say_hi

If we were to run this code, we should see the following output:

Hello, World!

What just happened there? Where did the Distribunaut::Distributed::HelloWorld class come from? How did it know to print "Hello, World!" when we called the say_hi method? All great questions.

The Distribunaut::Distributed module is "special." When you preface a constant such as HelloWorld in that module, it queries the RingServer and attempts to find a service that matches that constant. So, in our case it searched the RingServer for a service called HelloWorld. It found the HelloWorld class we created earlier and returned a reference to it. With that reference we could call the new method on that class, which returned a new instance of the HelloWorld class. And then we could call the say_hi method.

So if we didn't explicitly place our HelloWorld class in the RingServer, how did we access it? And how were we able to call a class method on it, when we know that you have to put instances only into a RingServer? The same answer applies to both questions. When we included the Distribunaut::Distributable module into the HelloWorld class, it created a Singleton wrapper class on-the-fly that then proxies all methods called on that proxy class onto the original HelloWorld class. With that we can put the Singleton instance into the RingServer. Then we can call class methods, which allows us to do things like call the new and get back a new instance of the class.

Having all of this happen automatically also helps clean up the usual supporting code you need to write to both set an instance into the RingServer and retrieve that instance later. Just look at what a plain-vanilla DRb and Rinda implementation of the client would look like:

require 'rinda/ring'

DRb.start_service
ring_server = Rinda::RingFinger.primary

service = ring_server.read([:hello_world_service,
                            nil, nil, nil])
server = service[2]

puts server.say_hi

This is not only more code, but also uglier code.

  • + Share This
  • 🔖 Save To Your Account