confessions of a ruby programmer

29 August 2007 updated 15 February 2009
Sending SMS messages from your Rails application

Something I’ve seen come up on the Ruby/Rails mailing lists/google groups from time to time is how to send SMS messages from your Rails app. In this article, I’ll introduce you to my Ruby Clickatell library and how to use it to send SMS messages from your Rails application in no time.

When it comes to sending an SMS using Ruby there are two approaches you can take. The first – and more complicated – is to use your own hardware and something like the ruby-sms library to communicate with the hardware. This set-up is time consuming, a pain to maintain and probably not very scalable.

The second option is to take the much easier route and use an existing SMS gateway service. There are many SMS gateways out there that offer APIs (ranging from HTTP/FTP based to email and COM-based); one such provider is Clickatell who are one of the bigger providers out there with a range of services whose customers include Barclays Bank, the BBC and CNN.

Getting started with the Ruby Clickatell gem

Before you can start sending SMS messages from your Rails app, you’ll need a Clickatell account. You can sign up for an account (Clickatell Central API) on their website. Once you have signed up you can log into your account centre and add an HTTP service to your account – this will give you an API key that you will need to use in your code.

Once you’ve signed up, you need to install the Ruby Clickatell gem. I’ve just released version 0.4 and its fast approaching being stable for production use – all it needs is some users to test it out a bit more extensively. So install it and give it a go:

$ sudo gem install clickatell

The gem also comes with a handy command-line utility, “sms”, which can be used to send SMS messages directly from the command line. For more information, simply run:

$ sms --help

More information can be found on the website.

Integrating with Rails

For our basic example application, which will use REST-ful conventions, we will expose a single resource – sms – which we can POST to to send an SMS to a specified recipient. First of all, lets create a simple wrapper around the Clickatell API which will act as our “resource” that takes a hash containing our username/password/api-key and has an ActiveRecord-style create method.

require 'clickatell'

class SMS
  def initialize(config)
    @config = config
  end
  
  def create(recipient, message_text)
    api.send_message(recipient, message_text)
  end
  
  private
    def api
      @api ||= Clickatell::API.authenticate(
        @config[:api_key],
        @config[:username],
        @config[:password]
      )
    end
end

The above wrapper isn’t strictly necessary but it helps to keep our controller as skinny as possible.

For convenience, we’ll want to keep our Clickatell credentials in a YAML file in the config folder of our Rails app:

# config/clickatell.yml
api_key: abcdefghi123
username: joebloggs
password: secret

We’ll also want to access this config within our Rails app easily:

# config/environments/production.rb
CLICKATELL_CONFIG = YAML.load(File.open(File.join(RAILS_ROOT, 'config', 'clickatell.yml')))

h3. Bringing it altogether

Now we’ll want to configure our routing to expose our SMS resource:

ActionController::Routing::Routes.draw do |map|
  map.resource :sms
end

Next, we’ll create our form under app/views/sms/new.rhtml:

<% form_tag '/sms', :method => :post do -%>
  <label>Enter the recipients mobile number:</label>
  <%= text_field_tag "recipient" %>
  <label>Enter your message:</label>
  <%= text_area_tag "message_text" %>
  <%= submit_tag "Send SMS" %>
<% end %>

Finally, all we have to do is create our controller’s create method to handle the form submission.

class SmsController < ApplicationController
  def create
    sms = SMS.new(CLICKATELL_CONFIG)
    sms.create(params[:recipient], params[:message_text])
    flash[:notice] = "Message sent succesfully!"
    redirect_to :back
  rescue Clickatell::API::Error => e
    flash[:error] = "Clickatell API error: #{e.message}"
    redirect_to :back
  end
end

And thats all there is to it. Of course, in a real application you might want to think about things such as validation of required attributes and message length. I’ll leave that as an exercise to the reader.

For more information:

* Ruby Clickatell API Website * Ruby Clickatell Documentation * Rubyforge project page * Clickatell Gateway information