Building a SMS Module in Rails
This guide will explain how to create a Ruby module and include it in the lib directory so it can be used throughout the application to send SMS messages.
Guide Tasks
  • Read Tutorial

This guide will explain how to create a Ruby module and include it in the lib directory so it can be used throughout the application to send SMS messages.

In professional rails development there arises times where features need to be built that are outside the scope of typical MVC and RESTful functionality (models, controllers, views, etc). In cases like ours' where we need to build in the ability to send SMS messages it would be a bad idea to integrate that code in the Notification model because remember that classes should only entail the behavior of a single feature and the ability to send SMS messages is outside the scope of what a Notification should manage.

There are a few places where we could place the code that will handle sending SMS messages, including:

  1. In the models/ directory using a plain Ruby class - however this isn't ideal because that seems confusing, our SMS sending tool isn't going to be an application model, so it wouldn't make sense to put it in the models/ directory.

  2. As a concern - Rails gives the ability include concerns, located in the models/concerns/ directory, however I typically only like to use create a Concern when it's related to data since, once again, it's located in the models/ directory.

  3. As a code library in the lib/ directory - this is how I typically like to integrate code that is outside the purview of a typical rails feature. This is where we'll be placing our SMS tool. This has the added benefit of organizing our module so that it could be refactored out into its own RubyGem at some point if we feel like that's necessary.

Configuring Rails to load modules from the lib directory

Before we can create our module and call it from within the application we need to let Rails know that it needs to look in the lib directory and load the files there since it doesn't do that by default. Open up the application.rb file and update it so it looks like the code here:

# config/application.rb
# I removed the comments to save space

require File.expand_path('../boot', __FILE__)

require "rails"

require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "sprockets/railtie"

Bundler.require(*Rails.groups)

module MessageService
  class Application < Rails::Application
    config.autoload_paths << Rails.root.join("lib") # this is the key code to add
    config.active_record.raise_in_transactional_callbacks = true
  end
end

The configuration option of config.autoload_paths << Rails.root.join("lib") simply tells the application to load the files from the lib directory.

With that setup now we can create our module, let's create the file and place it in the lib directory:

touch lib/sms_tool.rb

We're not going to connect to the Twilio API yet since we're going to be good coders and follow TDD practices to build a stub that will mimic the exact behavior that we should get from the API. Inside of our newly created file we can add in the following code:

# lib/sms_tool.rb

module SmsTool
  def self.send_sms(num, msg, app)
    puts "Sending message..."
    puts "#{msg} to #{num} from #{app}"
  end
end

I put some console output that will get printed out when we run our tests and will let us ensure that we can successfully communicate with our new module.

Now in the NotificationsController let's first import the module and then we can call the method from within the create method.

# app/controllers/notifications_controller.rb

class NotificationsController < ApplicationController
  include SmsTool

  def create
    @notification = Notification.new(notification_params)

    respond_to do |format|
      if @notification.save
        SmsTool.send_sms("5555555555", "A Message", "My App")
        format.json { render action: 'show', status: :created, location: @notification}
      else
        format.json { render json: @notification.errors, status: :unprocessable_entity }
      end
    end
  end

  def show
  end

  private

    def notification_params
      params.require(:notification).permit(:phone, :body, :source_app)
    end
end

Now if you run rspec you'll see that our tests are passing and you'll also see that our message gets printed to the console, so our module is being called properly and the placeholder method is working properly.

large

With our SMS module in place we can start the process of connecting to the Twilio API, in the next guide we'll walk through how to create RSpec stubs to mimic calling the API.

Resources