ActionCable

Real-time web applications for fun and profit

@sophiedeziel

Requirements

Basic concepts

Config

Creating a game

Profit

In this talk, we will you will learn amazing things:

Read the code at your ease:

http://slides.com/sophiedeziel/actioncables/


# You can read the code in the 
# slides at your ease.
#
#
# This will remain accessible 
# after the talk so you can 
# come back to it.

Talk.new('ActionCables').start(Time.now)

Requirements

Basic concepts

Config

Creating a game

Profit

What is a WebSocket?

  • Standard (RFC 6455)
  • Full-Duplex 
  • Used aside of HTTP
  • Supports encrypted and unencrypted connections

Wow! is it well supported?

Yes.

There are existing solutions for rails

  • websocket_rails gem
  • faye_rails gem
  • Pusher
  • Pubsubhubbub
  • Firebase
  • Still in alpha version
  • Announced for Rails 5
  • But we can try it!

ActionCables is coming soon...

Important concepts

Your Rails server

# Some code
# ...

render text: 'Hello World!'

User

Your web application

Your Action Cable server

Important concepts

  • Action Cable server
  • Connection Instance
  • WebSocket connection
  • Consumer
  • Channel
  • Subscription
  • Streaming
  • Broadcasting

Requirements

Basic concepts

Config

Creating a game

Profit

  • Ruby >= 2.2.0
  • Works on Rails 4.2
  • Redis
  • multi-threaded web server (ex: Puma)

Recipe:

Gemfile

source 'https://rubygems.org'

# Default rails gems
gem 'rails', '4.2.2'
gem 'sqlite3'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'

gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0', group: :doc

# For Action Cables
gem 'actioncable', git: 'https://github.com/rails/actioncable'
gem 'puma'

group :development, :test do
  gem 'byebug'
  gem 'web-console', '~> 2.0'
  gem 'spring'
end

Requirements

Basic concepts

Config

Creating a game

Profit

config/redis/cable.yml

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

cable/config.ru

require ::File.expand_path('../../config/environment',  __FILE__)
Rails.application.eager_load!

require 'action_cable/process/logging'

run ActionCable.server

Start the WebSocket server

> bundle exec puma -p 28080 cable/config.ru

Or

bin/cable

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

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

Hosting on heroku

Two solutions:

Hosting on a virtual machine using NGinx

upstream app {
    server unix:/home/sophie/actioncable-game/shared/sockets/puma.sock fail_timeout=0;
}

server {
    listen 80;
    server_name localhost;

    root /home/sophie/actioncable-game/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
    server localhost:28080;
}

server {
    listen 8020;
    location / {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

Requirements

Basic concepts

Config

Creating a game

Profit

Creating a channel

class ApplicationChannel < ApplicationCable::Channel

end

class GameChannel < ApplicationChannel
  def subscribed
  end

  def unsubscribed
  end

  def my_action
  end

  def my_other_action
  end
end

Sending data from the client side (easy stuff..)

# app/assets/javascript/application_cable.coffee
@App = {}
App.cable = Cable.createConsumer 'ws://game.sophiedeziel.com:8020'


# app/assets/javascripts/cable/subscriptions/game.coffee
App.game = App.cable.subscriptions.create "GameChannel",
  connected: -> 
    # some code when connected
  my_method: (data) ->
        @perform 'my_action', data


# Anywhere in your application
App.game.my_method({ player_name: 'Chuck Norris', score: 'over 9000' })
  

Broadcasting from the server (wow! This is also easy)

class GameChannel < ApplicationChannel
  periodically :poke_users, every: 1.second

  def subscribed
    stream_from "game_channel"
  end

  private

  def poke_users
    ActionCable.server.broadcast "game_channel", { message: 'Poke!', from: 'Server' }
  end
end


# From anywhere in your app:
ActionCable.server.broadcast "game_channel", { message: 'Poke!', from: 'Chuck Norris' }


#app/assets/javascripts/cable/subscriptions/game.coffee
App.game = App.cable.subscriptions.create "GameChannel",
  received: (data) ->
    if data['message'] == 'Poke!' && data['Chuck Norris']
      players.kill()
    else
      players.send_message(data)

http://game.sophiedeziel.com

Requirements

Basic concepts

Config

Creating a game

Profit

  1. Build an awesome app
  2. Monetize it or open-source it.

Conclusion

  • Still tiny compared to the other solutions
  • There are some rough edges
  • Fun and easy to play with
  • I spent 10x time debugging my javascript game than experimenting with Action Cable

Please, ask some questions

Sources & Links

ActionCable, Real-time applications for fun and profit

By Sophie Déziel

ActionCable, Real-time applications for fun and profit

I gave that talk on September 15th 2015 at Montreal.rb meetup.

  • 1,619