Live - чат и ActionCable

В этой заметке раскажу как интегрировать, зарелизиный совсем недавно, ActionCable в ваш Ruby on Rails проект на примере Live - чата. Я не стану сильно углубляться в то, как работает сам ActionCable, просто приведу небольшой пример применения.


Подготовка

Прежде всего добавим в Gemfile actioncable и puma:

gem 'actioncable', github: 'rails/actioncable'
gem 'puma'

ActionCable будет запускаться отдельным от основного приложения процессом. По этому мы будем использовать многопоточный web-сервер Puma.


Архитекутра чата

На главной странице пользователь будет вводить свой ник, после чего мы запишем его в куки и средиректим на страницу чата.

Добавим пару роутов в config/routes.rb:

resources :messages, only: [:index, :create]
resources :sessions, only: [:new, :create]

Создадим контроллеры с экшенами.
Запись ника в куки:

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    cookies.signed[:username] = params[:session][:username]
    redirect_to messages_path
  end
end

Формы авторизации пользователя app/views/sessions/new.html.slim:

= form_for :session, url: sessions_path do |f|
  p
    = f.label :username, 'Введите Ваш ник'
  p
    = f.text_field :username
  p
    = f.submit 'Ок'

В самом чате пока просто будем отправлять 200 в ответ на напечатанное сообщение:

# app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def create
    head :ok
  end
end

Представление app/views/messages/index.html.slim:

p
  = cookies.signed[:username]
p
  #messages
p
  = form_for :message, url: messages_path, remote: true, id: 'messages-form' do |f|
    p
      = f.label :body, 'Введите сообщение:'
    p
      = f.text_field :body
    p
      = f.submit 'Отправить'

Сообщение будет отправляться AJAX'ом на сервер.

 

Настройка ActionCalbe

Создадим три класса, которые будут ответственны за интеграцию с ActionCable.

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
  end
end
 
# app/channels/application_cable/channel.rb
module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end
 
# app/channels/messages_channel.rb
class MessagesChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'messages'
  end
end

Не забудьте добавить созданные директории в autoload_paths. Теперь, все, кто подписан на канал MessagesChannel, смогут получать сообщения по соответствующему потоку, определённому в методе subscribed, т.е. - messages.

ActionCable обеспечивает обмен сообщениями через Redis. Будем считать, что он у Вас уже установлен. Нам понадобиться настроить связь с ним в config/redis/cable.yml:

development: &dev
  :url: redis://localhost:6379
  :host: localhost
  :port: 6379
  :timeout: 1
  :inline: true
test: *dev
production: *dev

Теперь настроим Puma:

# cable/config.ru
require ::File.expand_path('../../config/environment',  __FILE__)
Rails.application.eager_load!
 
require 'action_cable/process/logging'
 
run ActionCable.server

Запускать пуму будем на порту 34523, почему бы и нет...

# /bin/bash
bundle exec puma -p 34523 cable/config.ru

Теперь пума запускается коммандой ./bin/cable.

 

Отправка сообщений в общий чат

Немного подправим app/controllers/messages_controller.rb:

class MessagesController < ApplicationController
  def create
    ActionCable.server.broadcast 'messages',
      message: params[:message][:body],
      username: cookies.signed[:username]
 
    head :ok
  end
end

Самое важное - подписка на канал. Создадим два coffee файла (не забудьте прореквайрить их):

#app/assets/javascripts/channels/messages.coffee
 
App.messages = App.cable.subscriptions.create 'MessagesChannel',
  received: (data) ->
    $('#messages').append("<p><b>[#{data.username}]:</b> #{data.message}</p>")
 

 
#app/assets/javascripts/channels/index.coffee
 
#= require cable
#= require_self
#= require_tree .
 
@App = {}
App.cable = Cable.createConsumer('ws://127.0.0.1:34523')

 

Вот и всё.
Как всегда, небольшой проект - https://github.com/lon10/live-chat


12.08.2015
Обсуждение недоступно