Blather is an easy to use XMPP client library written in Ruby. It seems to be actively developed and supported, as opposed to its competition XMPP4R library. I am using it in an application that has thousands of users interacting with it in real time.
Refer to https://github.com/sprsquish/blather for more details about the library.
This post demonstrates one way of writing concise tests for Blather based applications using RSpec.
There are different ways of writing Blather applications as shown in http://rubydoc.info/github/sprsquish/blather/master/Blather/DSL.
I have chosen the following approach of subclassing the Blather client, as it led to readable clients and simple tests.
#!/usr/bin/env ruby require "blather/client/client" class OurClient < Blather::Client def initialize super register_handler :subscription, :request? do |stanza| on_subscription(stanza) end register_handler :message, :chat?, :body do |stanza| on_message(stanza) end end def on_subscription(stanza) write stanza.approve! # Approve the subscription request. write stanza.request! # Request subscription in turn. end def on_message(stanza) # Echo the received message back to the sender. write Blather::Stanza::Message.new stanza.from, "You sent: #{stanza.body}" end end if __FILE__ == $0 trap(:INT) { EM.stop } trap(:TERM) { EM.stop } client = OurClient.setup "@", "", "", 5222 EM.run { client.run } end
The application above demonstrates handling of subscription requests (invitations to add as a friend) and incoming messages. These are the two major interactions that a typical XMPP client engages in.
On receiving an invitation, our client simply accepts the invitation and sends a similar request to the sender. At the end of it, both the invitee and our client are subscribed to each other.
On receiving a message, our client simply echoes the message back, as an acknowledgement of the message.
Writing specs for this client is as simple:
require "spec_helper" require "our_client" describe OurClient do let(:client) { OurClient.new } before { client.stub!(:write) } describe "#initialize" do it "registers a handler for subscriptions." do stanza = Blather::Stanza::Presence::Subscription.new(random_email,
:subscribe) client.should_receive(:on_subscription).with(stanza) client.send :call_handler_for, :subscription, stanza end it "registers a handler for messages." do stanza = Blather::Stanza::Message.new(random_email, random_string) client.should_receive(:on_message).with(stanza) client.send :call_handler_for, :message, stanza end end describe "#on_subscription" do it "approves the subscription request." do stanza = Blather::Stanza::Presence::Subscription.new(random_email,
:subscribe) client.should_receive(:write).with(stanza.approve!) client.on_subscription(stanza) end it "sends a subscription request." do stanza = Blather::Stanza::Presence::Subscription.new(random_email,
:subscribe) client.should_receive(:write).with(stanza.request!) client.on_subscription(stanza) end end describe "#on_message" do it "echoes the response back." do body = random_string sender = random_email stanza = Blather::Stanza::Message.new(random_email, body) stanza.stub!(:from).and_return(sender) client.should_receive(:write) do |message| message.to.should == sender message.body.should == "You sent: #{body}" end client.on_message(stanza) end end end