So, after a very long blogging hiatus, I’m coming back, with a completely different approach. Ruby Gems!!
Over the last few years I’ve been working on some very very interesting projects, and as part of those I developed a lot of functionality that is quite general purpose, and that I’ve been extracting into gems, for my own future self, of course, but I’m also blogging about them in the hopes that they will help you.
On today’s installment: SmsSafe!
If you’ve every sent emails with Rails, you’ve probably encountered MailSafe, that beautiful gem that allows you to test your app safely, knowing you will never email actual customers with crap, test data, or worse. Both Watu and Msty had this need to send SMS, and being used to MailSafe, I felt very unhappy without having a similar safety net, especially on Msty, where accidentally sending out SMS during a load test of the system could easily mean a few thousand dollars in carrier fees.
Seeing this, I cut my Gem-making teeth by blatantly stealing stemps’s idea, and building what is essentially MailSafe for SMS.
The idea is quite simple. You add the gem to your Gemfile, and then add this to your config:
SmsSafe.configure do |config| config.internal_phone_numbers = ['+12223334444', '+447111222222'] config.intercept_mechanism = :redirect config.redirect_target = '+12223334444' end SmsSafe.hook!(:action_texter) # or :twilio or :nexmo
The way this works, just like MailSafe, is you set a whitelist criteria, to which SMS can go, and for everything else, a redirection strategy and address. Anything that isn’t going to the whitelist, you can choose to ignore (simply drop it), redirect to a different number (probably your own), or redirect to e-mail instead, for a less annoying debugging alternative.
The usual cases:
- For development, you’ll whitelist nothing and then redirect everything to your phone / email. That way, when you are testing workflows that send SMS, you do get them.
- For staging, you can whitelist the numbers of everyone in the dev team… Or you can have a testing phone in the office that receives all SMS…
- The whitelist can be either a string, an array of strings, a regex, and most interestly a Proc, which lets you do custom logic. For example, on our “beta testing” environment, we wanted our testers to be able to sign up (for which they needed a verification SMS), but not be able to SMS other people. This simple Proc solved that, using the “reference” field to filter based on the message “type”. You can also use a Proc for the redirection address, so you can vary who ends up receiving the SMS / email using any logic you may need.
SmsSafe.configure do |config| config.internal_phone_numbers = Proc.new do |message| # Return true if it can go out, false if it needs to be intercepted message.original_message.reference.start_with?("phoneverif") end config.intercept_mechanism = :discard end
- For load / stress testing, you’ll turn off SMS sending completely. However, this will throw off your timings, since SMS sending is most certainly not instantaneous. To compensate for this, you can set a :discard_delay property to be the average response time of your SMS provider, which will give you a more realistic benchmark.
SmsSafe works by hooking into either the ActionTexter, Twilio or Nexmo gems. However, only ActionTexter has hooks that allow for interception (that I suggested via Pull Request for this very gem, and they graciously accepted). ActionTexter also provides functionality for switching to a “test” SMS provider in your test environment, allowing you to inspect the messages that would’ve been sent, so I strongly recommend you use it in your project.
Twilio and Nexmo are hooked via monkeypatching, unfortunately. If you use any other SMS gem or provider, and you’d like to use SmsSafe, please either submit a PR, or just let me know and I’ll try to add it, I’d love to see adoption for SmsSafe!