In a recent project, one of the technical requirements was to integrate an SMS service to a Rails application so that users could validate their accounts using their mobile device and also allow the application to send SMS messages to such users.
As it turns out, it is extremely easy to add such capabilities specially when using a service such as Twilio. In my opinion, it is one of the best services in the market today and its simplicity is unmatchable.
This article will guide you through the steps necessary to send and receive text messages through a Rails application. More specifically, it will cover:
- Signing up for Twilio
- Using Twilio’s Sandbox
- Verifying a phone number with Twilio
- Sending and Receiving SMS messages through a Rails application
- Creating a basic application
- Plugging the basic pieces together
- Integrating the app with Twilio
- Exposing your localhost to the Internet
- Writing unit tests and mocking external calls
At the end we should have a functioning application that is able to send and receive text messages. Enjoy!
Signing up for Twilio
The process to sign up for Twilio is really simple: you just need to provide your name, email and a password. Once you are done you will be redirected to their dashboard.
Using Twilio’s Sandbox
Now that we have signed up, let’s see what happens when we send a simple ‘hello’ text message to Twilio’s phone number. We get the following error message:
Alright, we need to include the PIN from the dashboard, let’s go ahead and do that. After the message is successfully sent with the format PIN ‘hello’, we get the following automated reply:
Great! We’ve successfully interacted with the Twilio Sandbox.
Verifying a phone number with Twilio
While in Sandbox mode you need to add and verify phone numbers that you want to enable to receive text messages programatically. This is a mandatory step and you will not receive any messages from the Rails application unless you complete this process.
Once you add a phone number and click the ‘Call this number’ button the system will place an automated phone call to the given number. You need to answer the call and enter the code that is displayed on screen. After that you should be good to go.
Sending and Receiving SMS messages through a Rails application
For this section I am going to start with a new Rails application based on Ruby 1.9.3 and Rails 3.2.5 on a Mac OSX environment. You are free to use an existing application if you already have one.
Next, we generate a User resource that will be used as the entry point to create new users and send them the initial account verification text message:
Now, migrate your database and add the following validations to the User model:
For the last step we need to create a controller that will be used to verify a user’s account. This is where our application will be receiving the callbacks from Twilio:
The basic structure is done. The next step will be to to plug the pieces together.
Plugging the basic pieces together
Now that our skeleton structure is ready, let’s start putting the pieces together. The first thing we’re going to do is edit config/routes.rb and add the following routes:
Next, edit app/controllers/users_controller.rb and add the following methods:
For the next step we need to create a view file for Users#new under app/views/users so that we can have users registering for our awesome service:
Finally, open app/controller/verifications_controller.rb and add a placeholder create method:
We now have a basic working application. Start the Rails server (rails s) and direct your browser to http://localhost:3000. Make sure you delete public/index.html otherwise you will get Rails’ default index file. You should see something like this:
Integrating our app with Twilio
This is where the fun part begins. First, add the following to your Gemfile and run bundle install:
Next, we are going to create a configuration file that will hold the information needed to use the Twilio API. Create the file config/twilio.yml and add the following:
You should have all the information needed for the configuration file if you signed up for Twilio correctly. And if not, make sure you follow the steps in the beginning of this article.
Having a separate entry for each environment is really helpful and allows you to have different configuration for each environment. You wouldn’t want to use your production account for development and your development account for production, would you?
For the next step we need to load this configuration and make it available app-wide. There are several different ways that we can do this but for this example I am going to use a constant. Create the file config/initializers/twilio.rb and add the following:
Notice that we’re defaulting to a pre-filled Hash if we can’t find the current environment. This is to prevent errors when accessing values for a configuration that does not exist.
Ok! We’re now ready to send our users a text message with information on how to validate their account. Ready?
Modify UsersController#create to include the following:
Next, go back to your browser and open the Rails application. After you register with your cell phone number you will receive a text message similar this:
That’s pretty cool, isn’t it?
For the next step we need to modify Verifications#create to be able to receive the callbacks from Twilio and verify the user that is sending us the text message:
When Twilio posts the callback to our application it includes about sixteen parameters. For this example we only care about From. Here are the rest of the parameters in case you’re curious:
Exposing your localhost to the Internet
In order to receive the callbacks from Twilio in our local Rails application we need to expose our local server to the outside “world”. There are three easy options for this:
Static IP address: If you already have an static IP address with your ISP, this is a no-brainer. Just setup your router to redirect requests from the external port 3000 to your internal server on port 3000, and use your external IP as the SMS URL in Twilio.
Showoff.io: According to their website, it’s “the easiest way to share localhost over the web”. And it is quite simple actually. Just install their gem and execute their binary with the internal port that you would like to share, and it will generate a short url that you can use externally. You can use the service for free for five minutes or purchase one of the paid plans. As of the writing of this article, the plans are $1 for a day pass, or $5 unlimited.
Tunnlr: Similar to Showoff.io, it’s a forwarding service that allows you to share your local server to outside users. Their current gem tunnlr_connector (1.0.3) only works with Rails but it can easily be extended to work with other frameworks. As of the writing of this article, you can use the service for free for 30 days and it costs $5/month thereafter. One thing you’re required to do is signup for the service before you can use the gem. It will ask you questions about your account during the configuration process.
From the three options above, I personally use Tunnlr and this is what we will use for this example. It’s extremely useful while working in development and interacting with the Twilio Sandbox.
After starting Tunnlr, copy the generated URL and paste it in the “SMS URL” field in Twilio’s Dashboard. Don’t forget to append /verification to the end of the URL:
We are all set now. Replying to the verification message that we received earlier will update our user status to verified.
You need to remember to always include the PIN from Twilio’s dashboard to the messages you send while in Sandbox mode, otherwise Twilio will reply with an error message. Go ahead and give it a shot.
Writing unit tests and mocking external calls
The final part is writing tests for our application and mocking external calls to the Twilio API. I must mention that I am only going to cover the functional test for UsersController to demonstrate how mocking works. I leave the other tests as an exercise for the reader.
During tests you should by all means avoid calling external services and sending real text messages. That’s where mocking objects come in handy. For this article, I will be using RR, an awesome “test double” framework.
In your Gemfile, add the following and run bundle install:
Next, open test/test_helper.rb and add the following, which will allow us to use RR to mock the calls to the Twilio API:
Now, open test/functional/users_controller_test.rb and add the following:
If you run this test, it will fail. Not because the test isn’t written properly but because Twilio’s REST client can’t complete the request to the specified resource. This is mainly due to our code trying to send a real text message while in test mode.
Let the mocking begin!
The first thing we need to do is create a mocked class that contains a method_missing method that returns an instance of self. This allows us to mock the method chaining in Twilio’s code client.account.sms.messages.create():
The next step is to use RR‘s mock method to mock Twilio’s client object and return an instance of our MockedTwilioClient class:
Finally, putting it all together, our test should look like this:
When you run the tests now it will not only pass but also mock the external calls to the Twilio API from our UsersController.
This article demonstrated how simple it is to add SMS capabilities to a Rails application and you should have enough knowledge to apply what you learned into your own projects. It also covered the steps you need to take to expose your local Rails server to the Internet; and how object mocking can be applied to avoid calling external services during test.
A few shortcuts were taken in order to keep the example simple and you should use them at your best judgement. Example:
- Better validations, better tests
- In UsersController#create, the code sending the text message should be moved to the User model to an after_create action, and possibly even executed as a background job or send it to a queue to be processed later.
- … you get the idea.
- Site: www.twilio.com
- Gem: twilio-ruby
- Site: www.showoff.io
- Gem: showoff-io
- Site: www.tunnlr.com
- Gem: tunnlr_connector
- Site: www.github.com/btakita/rr
- Gem: rr