Skip to content

Instantly share code, notes, and snippets.

@kirpachov
Last active February 22, 2022 16:02
Show Gist options
  • Select an option

  • Save kirpachov/2e87b57eb1c7dc67481a939f7a9aa4c9 to your computer and use it in GitHub Desktop.

Select an option

Save kirpachov/2e87b57eb1c7dc67481a939f7a9aa4c9 to your computer and use it in GitHub Desktop.

Rails notifications with websockets and ActionCable

Backend

  1. Create file connection.rb, notification_channel.rb, channel.rb
  2. Install redis

Frontend (angular)

  1. Install deps with
  • npm i --save actioncable
  • npm i --save-dev @types/actioncable
  1. Create service notifications.service.ts as below

Backend files

Gemfile.rb

# location: Gemfile.rb
gem 'redis'

NotificationChannel

# location: rails/app/channels/notification_channel.rb
class NotificationChannel < ApplicationCable::Channel
  def broadcast(channel, payload)
    NotificationChannel.broadcast_to(channel, payload)
  end

  def subscribed
    stream_from "notifications"
    stream_for current_user
  end

  def receive(data)
    puts "Received: #{data.inspect}"
    ActionCable.server.broadcast 'notifications', { title: 'Data from server to client' }
    # NotificationChannel.broadcast_to(current_user, payload) # From console/model/controller
  end
end

Connection

# location: rails/app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user
   
    def connect
      puts "request.params => #{request.params.inspect}"
      reject_unauthorized_connection if request.params[:token].blank?
      self.current_user = request.params[:token]
    end

    private

    def report_error(e)
      puts e.to_s + "\n" + e.backtrace.join("\n")
    end
  end
end

Channel

#location: rails/app/channels/application_cable/channel.rb
module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end

Routes

  mount ActionCable.server => '/cable'

Cable.yml

# location: config/cable.yml
development:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { 'redis://localhost:6379/1' } %>
  channel_prefix: websocket

Development config

# location: app/channels/notification_channel.rb
  config.action_cable.disable_request_forgery_protection = true

  # config.logger = Logger.new($stdout)
  config.action_cable.url = "ws://localhost:8080/cable"

... you should be fine :)

/**
This is just a example, you can implement this as you wish
*/
import { Injectable, NgZone } from '@angular/core';
import * as ActionCable from 'actioncable';
@Injectable({
providedIn: 'root'
})
export class ActionCableService {
private consumer: ActionCable.Cable;
constructor() {
this.consumer = ActionCable.createConsumer('ws://localhost:3000/')
}
subscribeMe(token: any) {
this.consumer = ActionCable.createConsumer(`ws://localhost:3334/cable?token=${token}`);
console.log("Trying connection");
this.consumer.subscriptions.create("NotificationChannel", {
connected() {
console.log("Subscription is ready for use");
},
disconnected() {
console.log("Service terminated by WB server");
},
received(data: any) {
console.log("This is the data received: ", data);
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment