We will be using puma instead of thin.
gem 'faye'
gem 'puma'
gem 'sync', '~> 0.3.0'
$ bundle
$ rails g sync:install
#= require sync
= javascript_include_tag Sync.adapter_javascript_url
# in config/sync.yml
development:
server: "http://localhost:9292/faye"
adapter_javascript_url: "http://localhost:9292/faye/faye.js"
auth_token: "badass"
adapter: "Faye"
async: true
test:
server: "http://localhost:9292/faye"
adapter_javascript_url: "http://localhost:9292/faye/faye.js"
adapter: "Faye"
auth_token: "secret"
async: false
production:
server: "http://example.com/faye"
adapter_javascript_url: "http://localhost:9292/faye/faye.js"
adapter: "Faye"
auth_token: "4f13884bb7e40b5573c2962c520d7b6a57a40f319fb686cb219d606e756ce118"
async: true
# Run with: rackup sync.ru -E production
require "bundler/setup"
require "yaml"
require "faye"
require "sync"
Faye::WebSocket.load_adapter 'puma'
Sync.load_config(
File.expand_path("../config/sync.yml", __FILE__),
ENV["RAILS_ENV"] || "development"
)
run Sync.pubsub_app
# in /config/initializers/sync.rb
Thread.new do
env = ENV["RAILS_ENV"] || "production"
# `ps -ef | grep "sync" | awk '{print $2}' | xargs kill`
puts "Booting Faye backend for Sync in #{env} environment..."
system("rackup sync.ru -E production")
end
$ rake assets:clobber
Sync is very particular about naming conventions, it is what allows it to do the magic.
Suppose you have a Comment model with a single :body
attribute and want an index view where a stream of latest comments would drop in. Assuming Rails-way, Comment model stores information in database table named comments.
You need a partial for comment rows in
# in /app/views/sync/<model_table_name>/_<model_name>_row.slim
/app/views/sync/comments/_comment_row.slim
The partial file name can be arbitrary, but it helps to keep with the naming convention.
And, of course, routes: resources :comments, only: :index
Key is to add the enable_sync
part. This will subscribe opening browsers to realtime messages.
class CommentsController < ApplicationController
enable_sync only: [:index, :create]
def index
end
def create
[...]
sync_new @comment, partial: 'comment_row'
end
end
Here sync_new
will catch new model creations and sync
will render existing model instances and listen for updates or destroys.
# in /views/comments/index.slim
h1 Comment stream
= sync_new partial: 'comment_row', resource: Comment.new, direction: :prepend
/ or :append to put at the bottom
= sync partial: 'comment_row', collection: Comment.all_comments
If your objects will never be edited, only added, you can load existing comments without the sync listening wrapper in a regular partial:
h1 Comment stream
= sync_new partial: 'comment_row', resource: Comment.new
/= render partial: 'comment_row', collection: Comment.all_comments
- Comment.all_comments.each do |comment|
= render partial: 'sync/comments/comment_row', locals: {comment: comment}
No problem here, you only need to keep realtime reloadable parts under /views/sync/<model_table_name>
Here we will simply display the comment's text body
/in /views/sync/comments/_comment_row.slim
p
= comment.body
class Comment < ActiveRecord::Base
include Sync::Actions # Enables syncing from console, jobs, callbacks etc.
sync :all
sync_scope :all_comments, -> { all.order(created_at: :desc) } # This is how sync-used scopes are defined
### Experimental create & update method overrides
def update *args
Sync::Model.enable { super }
end
def self.create(attributes = {}, &block) #(attributes = nil, options = {}, &block)
Sync::Model.enable { super }
end
def self.create!(attributes = {}, &block) #(attributes = nil, options = {}, &block)
Sync::Model.enable { super }
end
def save *args
Sync::Model.enable { super }
end
def save! *args
Sync::Model.enable { super }
end
With everything set up like this:
- run faye with
$ rackup sync.ru -E production
# NB, must run in production environment even in dev, otherwise lint errors - run
rails server
- run
rails c
- navigate to /comments and see nothing (since no comments yet exist)
- in console run
Comment.create!(body: "New comment at #{Time.now}")
- in console you should see "Rendered sync/comments/_comment_row.slim" somewhere after COMMIT
- in /comments you should see the new comment body text appeared.
- Horray, it is realtime!
Assuming your production environment uses SSL, faye server should as well.
Place javascript asset requirements from step 3 & 4 in correct namespace - if developing functionality for admin, require sync in admin.coffee and admin.slim respectively.
Sync partials are finnicky in respect to nesting, best make them individual paragraphs.