Writing Ruby/Stomp Clients with SMQueue

January 01, 2009 · 3 min read

SMQueue makes writing Ruby clients for message queues almost trivially easy. It has adaptors for Spread, Stomp, and Stdio — which is handy, because that message queue I set up a few weeks back speaks Stomp, and I'm rather fond of Ruby.

Installing SMQueue

The upstream SMQueue repository doesn't have a way to produce a gem yet, so there are two options: drop it into vendor/gems/smqueue in your project, or build a gem from my fork. I went with the latter.

Clone my repository — you'll find a gemspec ready to go. The whole process looks like this:

git clone http://barkingiguana.com/~craig/smqueue.git
cd smqueue
gem build smqueue.gemspec
sudo gem install ./smqueue-0.1.0.gem

I'm told that when SMQueue does get an official gem release it'll start at 0.2.0, so having 0.1.0 installed won't cause any clashes.

Note: I've removed the Spread adaptor from my branch because I don't have a working Spread client on my system and SMQueue won't load without one. I'm sure that'll be sorted in a future release.

Assumptions

For this article I'm assuming you have a working Ruby 1.8.6 install and a local ActiveMQ instance with the Stomp connector enabled. Adjust the code accordingly if your setup differs.

A Simple Producer

Let's start with a contrived example: put an ascending number onto a queue roughly every second. A good source for ascending numbers is the current time as seconds since the epoch — easy to get in Ruby:

>> Time.now.to_i
=> 1230602445
>> Time.now.to_i
=> 1230602446
>> Time.now.to_i
=> 1230602447

Wrap it in a loop with a one-second sleep and you've got a steady stream:

>> loop do
?>   puts Time.now.to_i
>>   sleep 1
>> end
1230602557
1230602558
1230602559

Easy enough on STDOUT, but how do we get these into a queue? Bring in SMQueue, create a client, and push the numbers on:

require 'rubygems'
require 'smqueue'

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

loop do
  number = Time.now.to_i
  puts "Sending #{number}"
  queue.puts number.to_yaml
  sleep 1
end

Paste this into a terminal to kick off the producer. You should see a steady stream of output — about one message per second.

cat > producer.rb < "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

loop do
  number = Time.now.to_i
  puts "Sending #{number}"
  queue.puts number.to_yaml
  sleep 1
end
EOF
ruby producer.rb

A Simple Consumer

With the producer running, let's write a consumer that takes each message and converts it back into a human-readable time. It's a pointless task, but it shows just how little code is needed.

require 'rubygems'
require 'smqueue'
require 'yaml'

queue = SMQueue(
  :name => "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

queue.get do |message|
  number = YAML.parse(message.body).transform
  time = Time.at(number)
  puts "Got #{number} which is #{time}"
end

Let's walk through the important bits.

We tell the queue we want to receive messages:

queue.get do |message|

The producer serialised each number as YAML, so we parse and transform it back:

number = YAML.parse(message.body).transform

Then we convert the number to a time and print both:

time = Time.at(number)
puts "Got #{number} which is #{time}"

Run this to start the consumer:

cat > consumer.rb < "/queue/numbers.ascending",
  :host => "localhost",
  :adapter => :StompAdapter
)

queue.get do |message|
  number = YAML.parse(message.body).transform
  time = Time.at(number)
  puts "Got #{number} which is #{time}"
end
EOF
ruby consumer.rb

For each message the producer creates, you should see your consumer print a line to the screen. That's all there is to it.