Search code examples
ruby-on-railsherokumessagingfaye

Using Faye on Heroku in production mode


Rails 4.1.1
Ruby 2.1.1 The access point is https://pacific-savannah-8641.herokuapp.com

Gemfile

source 'https://rubygems.org'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.1.1'
# Use mysql as the database for Active Record
gem 'mysql2'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.3'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.0.0'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer',  platforms: :ruby

# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', '~> 0.4.0',          group: :doc

# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use unicorn as the app server
# gem 'unicorn'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Use debugger
# gem 'debugger', group: [:development, :test]

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin]

gem 'slim-rails'
gem 'devise'
gem 'cancan'
gem 'rakismet'

gem 'carrierwave'
gem 'rmagick', :require => false
gem 'faker', '1.0.1'
gem 'jdbc-mysql', '5.1.28', :platform => :jruby
gem 'thinking-sphinx', '~> 3.1.0'
gem 'remotipart', '~> 1.2'
gem 'kaminari'
gem 'thin'
gem 'carrierwave-aws'

group :production do
  gem 'rails_12factor', '0.0.2'
end

gem 'foreman'
gem 'faye'

faye.ru

require 'faye'
Faye::WebSocket.load_adapter('thin')
faye_server = Faye::RackAdapter.new(:mount => '/faye', :timeout => 45)
run faye_server

application.html.slim

doctype html
html
  head
    title Help Desk App

    = stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true
    = javascript_include_tag "application", "data-turbolinks-track" => true
    = javascript_include_tag  "http://localhost:9292/faye.js"

    = csrf_meta_tags
    ....

Procfile

worker: bundle exec foreman start -f Procfile.workers

Procfile.workers

faye_worker: rackup faye.ru -s thin -E production

app/assets/javascripts/main.js

$(function() {
  var faye = new Faye.Client('http://localhost:9292/faye');
  faye.subscribe('/posts/new', function (data) {
      eval(data);
  });  
}

app/helpers/application_helper.rb

module ApplicationHelper
  def broadcast(channel, &block)
    message = {:channel => channel, :data => capture(&block)}
    uri = URI.parse("http://localhost:9292/faye")
    Net::HTTP.post_form(uri, :message => message.to_json)  
  end
end

heroku ps

drobazko@drobazko:~/www/help_desk$ heroku ps
=== run: one-off processes
run.2182 (1X): up 2015/02/11 08:07:57 (~ 27m ago): `foreman start`

=== web (1X): `bin/rails server -p $PORT -e $RAILS_ENV`
web.1: up 2015/02/11 08:35:44 (~ -40s ago)

=== worker (1X): `bundle exec foreman start -f Procfile.workers`
worker.1: up 2015/02/11 08:35:42 (~ -38s ago)
worker.2: up 2015/02/11 08:35:39 (~ -35s ago)
worker.3: up 2015/02/11 08:35:40 (~ -37s ago)
worker.4: up 2015/02/11 08:35:39 (~ -36s ago)

I couldn't find faye.js: Obviously, the reference http://localhost:9292/faye.js is not accessible.

I have tried to find faye.js without success:

https://pacific-savannah-8641.herokuapp.com/faye.js
https://pacific-savannah-8641.herokuapp.com/faye
http://pacific-savannah-8641.herokuapp.com/faye
http://pacific-savannah-8641.herokuapp.com/faye.js
...http://pacific-savannah-8641.herokuapp.com:9292...

Questions:

How should I replace the reference http://localhost:9292/faye in production on Heroku so that it works?


Solution

  • I have found the solution.
    To use Faye with Heroku without need to add extra dynos (i.e. for free) you should mount Faye to your applications as middleware.
    The key point is adding particular lines to application.rb:

    require File.expand_path('../boot', __FILE__)
    
    require 'rails/all'
    require 'net/http'
    
    Bundler.require(*Rails.groups)
    
    module Helpdesk
      class Application < Rails::Application
        config.middleware.delete Rack::Lock
        config.middleware.use FayeRails::Middleware, mount: '/faye', :timeout => 25
      end
    end
    

    Also, you should add faye-rails gem:

    gem 'faye-rails', '~> 2.0'
    

    application.js should look as:

    //= require jquery
    //= require jquery.turbolinks
    //= require jquery_ujs
    //= require jquery.remotipart
    //= require turbolinks
    //= require faye
    //= require_tree .
    

    Then, I have created apps/assets/javascripts/realtime.js.coffee to define how I will handle a realtime messages form server:

    window.client = new Faye.Client('/faye')
    
    jQuery ->
      client.subscribe '/comments', (payload) ->
        $('.ticket-' + payload.token + ' .no-posts').remove()
    
        $('<div></div>')
          .css({'backgroundColor' : '#ff9f5f'})
          .prependTo('.ticket-' + payload.token + ' .posts')
          .hide()
          .append(payload.message)
          .fadeIn('slow')
          .css({transition: 'background-color 3s ease-in-out', "background-color": "white"})
    

    Finally, here is how I publish a message to all subscribers app/views/posts/create.js.coffee:

    publisher = client.publish('/comments', {
      message: '<%= j render @post %>',
      token: '<%= @ticket.token %>'
    });
    

    Procfile should be with the content:

    web: bundle exec rails server thin -p $PORT -e $RACK_ENV
    

    You can check all code out on help_desk