Employee Directory with Ruby and Sinatra

This Sinatra application is an Employee Directory that can be used from any SMS client to find an employee's contact information. To build this app, we will need to.

  • Create an Employee model.
  • Receive an incoming SMS.
  • Search for an Employee by name.
  • Respond with an outgoing MMS.

Create an Employee Model

The first thing we need is a database of employees. We will be using DataMapper for this. It will allow us to define our database structure without having to worry with database specifics.

Our employee entity has a few fields for contact information, including their name, e-mail, phone number, and a public URL containing an image of them.

Loading Code Samples...
Language
require 'data_mapper'

class Employee
  include DataMapper::Resource

  property :id, Serial
  property :name, String
  property :image_url, String, length: 200
  property :email, String
  property :phone_number, String
end
models/employee.rb
Employee Model

models/employee.rb

Now, let's build the logic to search for employees.

Search for an Employee by Name

The Searcher module allows us to search the database for employees either by name or by their unique database identifier.

When searching using the employee name as parameter, we'll return a list of employees whose name might match a search query. If we pass a number as parameter we'll use it to search the employee by its id.

Loading Code Samples...
Language
module Searcher
  def self.search(employee_reference)
    if employee_reference =~ /^\d*$/
      [Employee.get(employee_reference)]
    else
      Employee.all(:name.like => "%#{employee_reference}%")
    end
  end
end
lib/searcher.rb
Employee Search Service

lib/searcher.rb

We'll use this search functionality when responding to an SMS from a user, which we'll look at next.

Receive an incoming SMS

When your number receives an SMS message, Twilio will send an HTTP POST request to our application. This will be handled by the Lookup action in EmployeeController.

We check for numeric input (more on that later) or perform a lookup for the desired employee. The results are packaged up as a XML and sent back to Twilio and, in turn, the original sender of the SMS.

Loading Code Samples...
Language
require_relative '../lib/message_creator'

module Routes
  module Employee
    def self.registered(app)
      app.post '/employee' do
        content_type 'application/xml'

        employees = Searcher.search(params[:Body])
        message_creator = MessageCreator.new(employees)
        case employees.size
        when 0 then message_creator.employee_not_found
        when 1 then message_creator.employee_details
        else message_creator.employees_options
        end
      end
    end
  end
end
routes/employee.rb
Receive an Incoming SMS

routes/employee.rb

Let's take a closer look at building the response.

Respond with Single Match for an Employee Name

Let's say the service finds a single employee matching the text message. In this case, we simply write out a response that contains the employee's contact information, including a photo, making our response a MMS message.

Loading Code Samples...
Language
class MessageCreator
  def initialize(employees)
    @employees = employees
  end

  def employee_not_found
    Twilio::TwiML::Response.new do |response|
      response.Message 'not found'
    end.to_xml
  end

  def employees_options
    Twilio::TwiML::Response.new do |response|
      response.Message employees_labels.join(' ')
    end.to_xml
  end

  def employee_details
    employee = employees.first

    Twilio::TwiML::Response.new do |response|
      employee_info = "#{employee.id}-#{employee.name}" \
      " #{employee.email}" \
      " #{employee.phone_number}"

      response.Message do |message|
        message.Body employee_info
        message.Media employee.image_url
      end
    end.to_xml
  end

  private

  attr_reader :employees

  def employees_labels
    employees.map do |employee|
      "#{employee.id}-#{employee.name}"
    end
  end
end
lib/message_creator.rb
Create TwiML response

lib/message_creator.rb

A single matching employee isn't the only scenario, however. Next, we will learn how to handle multiple employee matches.

What If No Employee or Multiple Employees Match?

If we don't find any employees, we can simply return a "Not found" message.

What about multiple matches? For this case, we want to return a list of the matching employees' names along with an ID number the end user can use to make their selection. For example, if someone searched for "Peter" they might get something like:

1-Peter Parker 2-Peter Quill
Loading Code Samples...
Language
class MessageCreator
  def initialize(employees)
    @employees = employees
  end

  def employee_not_found
    Twilio::TwiML::Response.new do |response|
      response.Message 'not found'
    end.to_xml
  end

  def employees_options
    Twilio::TwiML::Response.new do |response|
      response.Message employees_labels.join(' ')
    end.to_xml
  end

  def employee_details
    employee = employees.first

    Twilio::TwiML::Response.new do |response|
      employee_info = "#{employee.id}-#{employee.name}" \
      " #{employee.email}" \
      " #{employee.phone_number}"

      response.Message do |message|
        message.Body employee_info
        message.Media employee.image_url
      end
    end.to_xml
  end

  private

  attr_reader :employees

  def employees_labels
    employees.map do |employee|
      "#{employee.id}-#{employee.name}"
    end
  end
end
lib/message_creator.rb
Get TwiML for Multiple Results

lib/message_creator.rb

Let's see how these options are stored next.

Return Employee's Contact Information by Number Choice

When searching for an employee, we check whether the body of the text input is a number. If that's the case, we use it to retrieve the employee by its ID. Otherwise, we use it to do a search by employee's name.

Loading Code Samples...
Language
module Searcher
  def self.search(employee_reference)
    if employee_reference =~ /^\d*$/
      [Employee.get(employee_reference)]
    else
      Employee.all(:name.like => "%#{employee_reference}%")
    end
  end
end
lib/searcher.rb
Employee Search Service

lib/searcher.rb

That's it! We've built a working Employee Directory.

winning

Where to Next?

Take a look at the code on GitHub to run the application yourself. There you will find the complete solution and instructions for getting up and running.

If you're a ruby developer working with Twilio, you might want to check out these other tutorials.

Automated Survey

SMS and MMS Marketing Notifications

Did this help?

Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Tweet @twilio to let us know what you think!

Agustin Camino
David Prothero
Jose Oliveros

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
require 'data_mapper'

class Employee
  include DataMapper::Resource

  property :id, Serial
  property :name, String
  property :image_url, String, length: 200
  property :email, String
  property :phone_number, String
end
module Searcher
  def self.search(employee_reference)
    if employee_reference =~ /^\d*$/
      [Employee.get(employee_reference)]
    else
      Employee.all(:name.like => "%#{employee_reference}%")
    end
  end
end
require_relative '../lib/message_creator'

module Routes
  module Employee
    def self.registered(app)
      app.post '/employee' do
        content_type 'application/xml'

        employees = Searcher.search(params[:Body])
        message_creator = MessageCreator.new(employees)
        case employees.size
        when 0 then message_creator.employee_not_found
        when 1 then message_creator.employee_details
        else message_creator.employees_options
        end
      end
    end
  end
end
class MessageCreator
  def initialize(employees)
    @employees = employees
  end

  def employee_not_found
    Twilio::TwiML::Response.new do |response|
      response.Message 'not found'
    end.to_xml
  end

  def employees_options
    Twilio::TwiML::Response.new do |response|
      response.Message employees_labels.join(' ')
    end.to_xml
  end

  def employee_details
    employee = employees.first

    Twilio::TwiML::Response.new do |response|
      employee_info = "#{employee.id}-#{employee.name}" \
      " #{employee.email}" \
      " #{employee.phone_number}"

      response.Message do |message|
        message.Body employee_info
        message.Media employee.image_url
      end
    end.to_xml
  end

  private

  attr_reader :employees

  def employees_labels
    employees.map do |employee|
      "#{employee.id}-#{employee.name}"
    end
  end
end
class MessageCreator
  def initialize(employees)
    @employees = employees
  end

  def employee_not_found
    Twilio::TwiML::Response.new do |response|
      response.Message 'not found'
    end.to_xml
  end

  def employees_options
    Twilio::TwiML::Response.new do |response|
      response.Message employees_labels.join(' ')
    end.to_xml
  end

  def employee_details
    employee = employees.first

    Twilio::TwiML::Response.new do |response|
      employee_info = "#{employee.id}-#{employee.name}" \
      " #{employee.email}" \
      " #{employee.phone_number}"

      response.Message do |message|
        message.Body employee_info
        message.Media employee.image_url
      end
    end.to_xml
  end

  private

  attr_reader :employees

  def employees_labels
    employees.map do |employee|
      "#{employee.id}-#{employee.name}"
    end
  end
end
module Searcher
  def self.search(employee_reference)
    if employee_reference =~ /^\d*$/
      [Employee.get(employee_reference)]
    else
      Employee.all(:name.like => "%#{employee_reference}%")
    end
  end
end