В недавнем проекте одним из технических требований была интеграция службы SMS с приложением Rails, чтобы пользователи могли проверять свои учетные записи с помощью своего мобильного устройства, а также разрешать приложению отправлять SMS-сообщения таким пользователям.
Оказывается, добавить такие возможности чрезвычайно легко, особенно при использовании такого сервиса, как Twilio . На мой взгляд, это одна из лучших услуг на рынке сегодня, и ее простота не имеет себе равных.
Эта статья проведет вас через шаги, необходимые для отправки и получения текстовых сообщений через приложение Rails. Более конкретно, он будет охватывать:
- Записаться на Twilio
- Использование песочницы Twilio
- Проверка номера телефона с помощью Twilio
- Отправка и получение SMS-сообщений через приложение Rails
- Создание базового приложения
- Соединяя основные части вместе
- Интеграция приложения с Twilio
- Выставление вашего локального хоста в Интернет
- Написание юнит-тестов и имитация внешних звонков
В конце у нас должно быть работающее приложение, которое может отправлять и получать текстовые сообщения. Наслаждайтесь!
Записаться на Twilio
Процесс регистрации в Twilio действительно прост: вам просто нужно указать свое имя, адрес электронной почты и пароль. Как только вы закончите, вы будете перенаправлены на их панель управления.
Использование песочницы Twilio
Теперь, когда мы зарегистрировались, давайте посмотрим, что произойдет, когда мы отправим простое «привет» текстовое сообщение на номер телефона Twilio. Мы получаем следующее сообщение об ошибке:
Хорошо, нам нужно включить PIN-код с панели инструментов, давайте продолжим и сделаем это. После того, как сообщение успешно отправлено в формате PIN ‘hello’ , мы получаем следующий автоматический ответ:
Большой! Мы успешно взаимодействовали с Песочницей Twilio.
Проверка номера телефона с помощью Twilio
Находясь в режиме песочницы, вам нужно добавить и проверить номера телефонов, которые вы хотите включить для получения текстовых сообщений программно. Это обязательный шаг, и вы не получите никаких сообщений из приложения Rails, если не завершите этот процесс.
После того, как вы добавите номер телефона и нажмете кнопку «Позвонить по этому номеру», система автоматически сделает телефонный звонок на указанный номер. Вам необходимо ответить на звонок и ввести код, который отображается на экране. После этого вам должно быть хорошо идти.
Отправка и получение SMS-сообщений через приложение Rails
Основное применение
В этом разделе я собираюсь начать с нового приложения Rails на основе Ruby 1.9.3 и Rails 3.2.5 в среде Mac OSX. Вы можете использовать существующее приложение, если оно у вас уже есть.
Во-первых, давайте создадим новое приложение rails. Я пропускаю звездочки и javascript просто для простоты (и потому что нам не нужно для нашего примера приложения):
rails new sms_app —skip-sprockets —skip-javascript |
Затем мы создаем ресурс User, который будет использоваться в качестве точки входа для создания новых пользователей и отправляем им начальное текстовое сообщение для подтверждения учетной записи:
rails g resource user name:string email:string phone:string verified:boolean —skip-assets |
Теперь перенесите базу данных и добавьте следующие проверки в модель User:
class User < ActiveRecord::Base | |
attr_accessible :email, :name, :phone | |
validates :name, presence: true | |
validates :email, :phone, presence: true, uniqueness: true | |
end |
Для последнего шага нам нужно создать контроллер, который будет использоваться для проверки учетной записи пользователя. Вот где наше приложение будет получать обратные вызовы от Twilio:
rails g controller verifications —skip-assets |
Основная структура готова. Следующим шагом будет соединить части вместе.
Соединяя основные части вместе
Теперь, когда наша структура скелета готова, давайте начнем собирать части. Первое, что мы собираемся сделать, это отредактировать config / rout.rb и добавить следующие маршруты:
resources :users | |
resource :verifications | |
root to: ‘users#new’ |
Затем отредактируйте app / controllers / users_controller.rb и добавьте следующие методы:
class UsersController < ApplicationController | |
def new | |
@user = User.new | |
end | |
def create | |
@user = User.new(params[:user]) | |
if @user.save | |
render text: «Thank you! You will receive an SMS shortly with verification instructions.» | |
else | |
render :new | |
end | |
end | |
end |
Для следующего шага нам нужно создать файл представления для Users # new в app / views / users, чтобы мы могли зарегистрировать пользователей для нашего замечательного сервиса:
<h1>User Registration</h1> | |
<%= form_for @user do |f| %> | |
<% if @user.errors.any? %> | |
<ul> | |
<% @user.errors.full_messages.each do |msg| %> | |
<li><%= msg %></li> | |
<% end %> | |
</ul> | |
<% end %> | |
<p><%= f.label :name %><br> | |
<%= f.text_field :name%></p> | |
<p><%= f.label :email %><br> | |
<%= f.text_field :email%></p> | |
<p><%= f.label :phone %><br> | |
<%= f.text_field :phone%></p> | |
<%= f.submit %> | |
<% end %> |
Наконец, откройте app / controller / verifications_controller.rb и добавьте метод создания заполнителя:
class VerificationsController < ApplicationController | |
def create | |
end | |
end |
Теперь у нас есть основное рабочее приложение. Запустите сервер Rails ( rails ) и направьте ваш браузер по адресу http: // localhost: 3000 . Убедитесь, что вы удалили public / index.html, иначе вы получите индексный файл Rails по умолчанию. Вы должны увидеть что-то вроде этого:
Интеграция нашего приложения с Twilio
Здесь начинается самое интересное. Сначала добавьте следующее в ваш Gemfile и запустите пакетную установку :
gem ‘twilio-ruby’ |
Далее мы собираемся создать файл конфигурации, который будет содержать информацию, необходимую для использования Twilio API. Создайте файл config / twilio.yml и добавьте следующее:
development: | |
from: ‘your-from‘ | |
sid: ‘your-sid‘ | |
token: ‘your-token‘ |
У вас должна быть вся информация, необходимая для файла конфигурации, если вы правильно зарегистрировались в Twilio. А если нет, то обязательно следуйте инструкциям в начале этой статьи.
Наличие отдельной записи для каждой среды действительно полезно и позволяет вам иметь различные конфигурации для каждой среды. Вы бы не хотели использовать свою производственную учетную запись для разработки и свою учетную запись для производства, не так ли?
Для следующего шага нам нужно загрузить эту конфигурацию и сделать ее доступной для всего приложения. Есть несколько разных способов сделать это, но в этом примере я собираюсь использовать константу. Создайте файл config / initializers / twilio.rb и добавьте следующее:
path = File.join(Rails.root, «config/twilio.yml») | |
TWILIO_CONFIG = YAML.load(File.read(path))[Rails.env] || {‘sid’ => », ‘from’ => », ‘token’ => »} |
Обратите внимание, что по умолчанию мы используем предварительно заполненный Hash, если не можем найти текущую среду. Это необходимо для предотвращения ошибок при доступе к значениям для несуществующей конфигурации.
Ok! Теперь мы готовы отправить нашим пользователям текстовое сообщение с информацией о том, как подтвердить свою учетную запись. Готов?
Измените UsersController # create, включив в него следующее:
class UsersController < ApplicationController | |
def new | |
@user = User.new | |
end | |
def create | |
@user = User.new(params[:user]) | |
if @user.save | |
render text: «Thank you! You will receive an SMS shortly with verification instructions.» | |
# Instantiate a Twilio client | |
client = Twilio::REST::Client.new(TWILIO_CONFIG[‘sid’], TWILIO_CONFIG[‘token’]) | |
# Create and send an SMS message | |
client.account.sms.messages.create( | |
from: TWILIO_CONFIG[‘from’], | |
to: @user.phone, | |
body: «Thanks for signing up. To verify your account, please reply HELLO to this message.» | |
) | |
else | |
render :new | |
end | |
end | |
end |
Затем вернитесь в браузер и откройте приложение Rails. После того, как вы зарегистрируетесь по номеру своего мобильного телефона, вы получите текстовое сообщение, подобное этому:
Это довольно круто, не правда ли?
Для следующего шага нам нужно изменить Verification # create, чтобы иметь возможность получать обратные вызовы от Twilio и проверять пользователя, отправляющего нам текстовое сообщение:
class VerificationsController < ApplicationController | |
before_filter :get_user | |
def create | |
@user.update_attribute(:verified, true) | |
# You could send another message acknowledging the verificaton | |
head :ok | |
end | |
private | |
def get_user | |
unless @user = User.find_by_phone(params[‘From’]) | |
head :not_found | |
end | |
end | |
end |
Когда Twilio отправляет обратный вызов в наше приложение, оно включает в себя около шестнадцати параметров. Для этого примера мы заботимся только о From . Вот остальные параметры, если вам интересно:
{ | |
«AccountSid»=>«<string>», | |
«Body»=>«the message content», | |
«ToZip»=>«<string>», | |
«FromState»=>«<string>», | |
«ToCity»=>«<string>», | |
«SmsSid»=>«<string>», | |
«ToState»=>«<string>», | |
«To»=>«<string>», | |
«ToCountry»=>«<string>», | |
«FromCountry»=>«<string>», | |
«SmsMessageSid»=>«<string>», | |
«ApiVersion»=>«<string>», | |
«FromCity»=>«<string>», | |
«SmsStatus»=>«<string>», | |
«From»=>«<string>», | |
«FromZip»=>«<string>» | |
} |
Выставление вашего локального хоста в Интернет
Чтобы получать обратные вызовы от Twilio в нашем локальном Rails-приложении, нам нужно выставить наш локальный сервер для внешнего «мира». Для этого есть три простых варианта:
Статический IP-адрес: если у вас уже есть статический IP-адрес у вашего интернет-провайдера, это не составляет труда. Просто настройте маршрутизатор для перенаправления запросов с внешнего порта 3000 на ваш внутренний сервер через порт 3000 и используйте свой внешний IP в качестве URL-адреса SMS в Twilio.
Showoff.io: согласно их веб-сайту , это «самый простой способ поделиться локальным хостом через Интернет». И это довольно просто на самом деле. Просто установите их гем и запустите их двоичный файл с внутренним портом, которым вы хотите поделиться, и он сгенерирует короткий URL, который вы можете использовать извне. Вы можете бесплатно пользоваться услугой в течение пяти минут или приобрести один из платных планов. На момент написания этой статьи планы составляют 1 доллар за однодневный пропуск или 5 долларов без ограничений.
Пример использования:
# Manual install | |
gem install showoff—io | |
# Or via Bundle | |
gem ‘showoff-io’, group: :development | |
bundle install | |
# Sharing your localhost on port 3000 | |
show 3000 |
Tunnlr: Подобно Showoff.io , это служба пересылки, которая позволяет вам использовать локальный сервер для внешних пользователей. Их текущий гем tunnlr_connector (1.0.3) работает только с Rails, но его легко расширить для работы с другими фреймворками. На момент написания этой статьи вы можете бесплатно пользоваться этой услугой в течение 30 дней, после чего она стоит 5 долларов в месяц. Одна вещь, которую вы должны сделать, это зарегистрироваться в службе, прежде чем вы сможете использовать драгоценный камень. Он задаст вам вопросы о вашей учетной записи в процессе настройки.
Пример использования:
# Manual install | |
gem install tunnlr_connector | |
# Or via Bundle | |
gem ‘tunnlr_connector’, group: :development | |
bundle install | |
# Configure for the first time | |
rake tunnlr:configure | |
# Sharing your localhost | |
rake tunnlr:start |
Из трех приведенных выше вариантов я лично использую Tunnlr, и это то, что мы будем использовать в этом примере. Это чрезвычайно полезно при работе в разработке и взаимодействии с песочницей Twilio.
После запуска Tunnlr скопируйте созданный URL-адрес и вставьте его в поле «SMS-URL» на панели инструментов Twilio. Не забудьте добавить / подтверждение в конец URL:
Мы все готовы сейчас. Ответ на сообщение о подтверждении, которое мы получили ранее, обновит наш статус пользователя до подтвержденного.
Не забывайте всегда указывать PIN-код с панели мониторинга Twilio к сообщениям, которые вы отправляете в режиме песочницы, в противном случае Twilio ответит сообщением об ошибке. Давай и попробуй.
Написание юнит-тестов и имитация внешних звонков
Последняя часть — это написание тестов для нашего приложения и насмешка по внешним вызовам API Twilio. Я должен упомянуть, что я собираюсь рассказать только о функциональном тесте для UsersController, чтобы продемонстрировать, как работает mocking . Я оставляю другие тесты в качестве упражнения для читателя.
Во время тестов вы должны избегать вызовов внешних служб и отправки реальных текстовых сообщений. Вот где насмешливые объекты пригодятся. В этой статье я буду использовать RR , потрясающий «тестовый двойной» фреймворк.
В вашем Gemfile добавьте следующее и запустите пакетную установку :
group :test do | |
gem ‘rr’ | |
end |
Затем откройте test / test_helper.rb и добавьте следующее, что позволит нам использовать RR для насмешки вызовов API Twilio:
class ActiveSupport::TestCase | |
include RR::Adapters::TestUnit | |
end |
Теперь откройте test / functions / users_controller_test.rb и добавьте следующее:
require ‘test_helper’ | |
class UsersControllerTest < ActionController::TestCase | |
test «#new» do | |
get :new | |
assert_response :success | |
assert_template :new | |
end | |
test «#create succeeds» do | |
assert_difference ‘User.count’ do | |
post :create, user: {name: ‘John Doe’, email: ‘[email protected]’, phone: ‘555-444-3434’} | |
assert_response :ok | |
end | |
end | |
test «#create fails on validation» do | |
assert_no_difference ‘User.count’ do | |
post :create, user: {name: ‘Joe’} | |
assert_response :ok | |
assert_template :new | |
assert !assigns(:user).valid? | |
end | |
end | |
end |
Если вы запустите этот тест, он потерпит неудачу. Не потому, что тест написан неправильно, а потому, что REST-клиент Twilio не может выполнить запрос к указанному ресурсу. Это происходит главным образом из-за того, что наш код пытается отправить реальное текстовое сообщение в тестовом режиме.
Пусть начнется насмешка!
Первое, что нам нужно сделать, это создать ложный класс, который содержит метод method_missing, который возвращает экземпляр self . Это позволяет нам смоделировать цепочку методов в коде Twilio client.account.sms.messages.create () :
class MockedTwilioClient | |
def method_missing(*args) | |
self | |
end | |
end |
Следующим шагом является использование метода mock для записи клиентского объекта Twilio и возврата экземпляра нашего класса MockedTwilioClient :
mock(Twilio::REST::Client).new(»,»){ MockedTwilioClient.new } |
Наконец, собрав все вместе, наш тест должен выглядеть так:
require ‘test_helper’ | |
class MockedTwilioClient | |
def method_missing(*args) | |
self | |
end | |
end | |
class UsersControllerTest < ActionController::TestCase | |
test «#new» do | |
get :new | |
assert_response :success | |
assert_template :new | |
end | |
test «#create succeeds» do | |
mock(Twilio::REST::Client).new(»,»){ MockedTwilioClient.new } | |
assert_difference ‘User.count’ do | |
post :create, user: {name: ‘John Doe’, email: ‘[email protected]’, phone: ‘555-444-3434’} | |
assert_response :ok | |
end | |
end | |
test «#create fails on validation» do | |
assert_no_difference ‘User.count’ do | |
post :create, user: {name: ‘Joe’} | |
assert_response :ok | |
assert_template :new | |
assert !assigns(:user).valid? | |
end | |
end | |
end |
Теперь, когда вы запускаете тесты, он не только проходит, но и проверяет внешние вызовы API Twilio из нашего UsersController .
Вывод
В этой статье показано, как просто добавить возможности SMS в приложение Rails, и у вас должно быть достаточно знаний, чтобы применить полученные знания в своих собственных проектах. В нем также описаны шаги, которые необходимо предпринять, чтобы вывести свой локальный сервер Rails в Интернет; и как можно применять объектный макет, чтобы избежать вызова внешних сервисов во время теста.
Для упрощения примера были сделаны несколько ярлыков, и вы должны использовать их по своему усмотрению. Пример:
- Лучшие проверки, лучшие тесты
- В UsersController # create код, отправляющий текстовое сообщение, следует переместить в модель User в действие after_create и, возможно, даже выполнить как фоновое задание или отправить его в очередь для последующей обработки.
- … вы поняли идею.
Ресурсы
Twilio:
- Сайт: www.twilio.com
- Драгоценный камень: twilio-ruby
Showoff.io:
- Сайт: www.showoff.io
- Драгоценный камень: showoff-io
Tunnlr:
- Сайт: www.tunnlr.com
- Gem: tunnlr_connector
RR:
- Сайт: www.github.com/btakita/rr
- Драгоценный камень: рр