- 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:
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 themodels/
directory.As a concern - Rails gives the ability include concerns, located in the
models/concerns/
directory, however I typically only like to use create aConcern
when it's related to data since, once again, it's located in themodels/
directory.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.
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.