Using RethinkDB in Rails with ActionCable


Let’s check how we can implement RethinkDB with ActionCable into Ruby on Rails application.

You can find all code in rethinkdb_example.

First of all, let’s create sample app with rails command:

rails new rethinkdb_example

And setup ActionCable route in routes.rb:

Rails.application.routes.draw do
  mount ActionCable.server => '/cable'
end

We need to generate channel for active users:

rails g channel active_users

Let’s generate controller and set it to the root in routes.rb

rails g controller tasks index

Now, moving to RethinkDB.

RethinkDB is a document oriented data store much like MongoDB, but with some key advantages.

RethinkDB makes building and scaling realtime apps dramatically easier. Get started by installing the server, and jump into our getting started guide to start building your first app in minutes.

You can read more here.

Let’s integrate RethinkDB into our rails application.

We need two gems.

gem 'nobrainer'
gem 'nobrainer_streams' 

Run bundle and installer script, to have your application use NoBrainer instead of ActiveRecord.

bundle && rails g nobrainer:install

Now, if you generate model User with rails g model User it will look like:

class User
  include NoBrainer::Document
  include NoBrainer::Document::Timestamps
end

Now, let’s move to our ActiveUsersChannel. Here we have subscribed and unsubscribed methods.

They’re just calling then somebody open or close connection.

class ActiveUsersChannel < ApplicationCable::Channel
  include NoBrainer::Streams

  def subscribed
    @user = User.create
    stream_from User.all, include_initial: true
  end

  def unsubscribed
    @user.destroy
  end
end

Now, let’s change our tasks/index.html.erb we add here special div with active_users_list ID.

<section id="active_users">
  <h2>Active users</h2>
  <ul id="active_users_list">
  </ul>
</section>

Now let’s take a look into generated active_users.coffee file:

App.active_users = App.cable.subscriptions.create "ActiveUsersChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    # Called when there's incoming data on the websocket for this channel

A little change here, for now just keep received event:

App.active_users = App.cable.subscriptions.create "ActiveUsersChannel",
  received: (data) ->
    if data.old_val && !data.new_val
      App.spreadsheet.remove_user(data.old_val)
    else if data.new_val
      App.spreadsheet.new_user(data.new_val)

Just update our ul by ID and add new data:

App.spreadsheet =
  active_users: {}

  new_user: (user) ->
    @active_users[user.id] = user
    @render_active_users()

  remove_user: (user) ->
    delete @active_users[user.id]
    @render_active_users()

  render_active_users: () ->
    $('#active_users_list').html(
      ("<li>#{user.id}</li>" for id,user of @active_users).join("")
    )

Now you can open browser window, then open second one, so you can see how new user will be added to the list. Or deleted - if you close browser.

DB