I am trying to build a chat feature for my rails application. I am using ActionController::Live
, Puma
, Resque
, Redis
for this. So basically in this case, redis subscribe
method is running in background using resque
.
So far what i have done is whenever a user enters a text in below form field i.e. chat box
<%= form_tag chat_box_publish_path, method: :get do %>
<%= text_field_tag :query, params[:query], class: "form-control", id: "chatSearchBox",
placeholder: 'Search' %>
<% end %>
..the request is coming to Publish
method in ChatBoxController
.
def publish
$redis.publish("chat_message:1:1", "#{params[:query]}")
respond_to do |format|
format.js {render nothing: true}
end
end
..now i have a below background Resque
job running with below code for testing purposes. So whenever a chat message is posted, its printing the data
which is fine. But how can i add ActionController::Live
feature to the background job ? or how do i go about this implementation ? Need help with this design.
class ChatBoxInitiator
@queue = :chat_box_initiator
private
def self.perform
$redis.subscribe('chat_message:1:1') do |on|
on.message do |event, data|
puts "====#{data}"
return data
end
end
end
end
and i want to show the Server Sent Events(SSE)
along with ActionController::Live
for notifications in Users/show
page
Create a redis.rb
initializer file in the config/initializers
directory, globalizing an instance of redis
. It's also a good idea to set up a heartbeat
thread (Anything from 5 seconds to 5 minutes is okay, depending on your requirements):
$redis = Redis.new
heartbeat_thread = Thread.new do
while true
$redis.publish("heartbeat","thump")
sleep 15.seconds
end
end
at_exit do
heartbeat_thread.kill
$redis.quit
end
You need to add two methods to your ChatController
, pub
and sub
. The role of pub
is to publish chat events and messages to redis
, and sub
to subscribe to these events. It should look something like this:
class ChatController < ApplicationController
include ActionController::Live
skip_before_filter :verify_authenticity_token
def index
end
def pub
$redis.publish 'chat_event', params[:chat_data].to_json
render json: {}, status: 200
end
def sub
response.headers["Content-Type"] = "text/event-stream"
redis = Redis.new
redis.subscribe(['chat_event', 'heartbeat']) do |on|
on.message do |event, data|
response.stream.write "event: #{event}\ndata: #{data}\n\n"
end
end
rescue IOError
logger.info "Stream Closed"
ensure
redis.quit
response.stream.close
end
end
In your routes
, make pub a POST
and sub a GET
, and match the path to something like /chat/publish
and /chat/subscribe
.
Assuming your actual webpage for the chat app is at /chat
, you need to write some Javascript to actually send and receive chat messages.
For ease of understanding, let's suppose your webpage only has a textbox and a button. Hitting the button should publish the content of the textbox to the chat stream, we can do that using AJAX:
$('button#send').click (e) ->
e.preventDefault()
$.ajax '/chat/publish',
type: 'POST'
data:
chat_data: {
message: $("input#message").val()
timestamp: $.now()
error: (jqXHR, textStatus, errorThrown) ->
console.log "Failed: " + textStatus
success: (data, textStatus, jqXHR) ->
console.log "Success: " + textStatus
Now, you need to be able to subscribe and receive the chat messages as well. You need to use EventSource
for this. Using EventSource, open a channel for SSE so that you can receive events, and use that data to update the view. In this example, we will only log them to the javascript console.
The code should look something like this:
$(document).ready ->
source = new EventSource('/chat/subscribe')
source.addEventListener 'chat_event', (e) ->
console.log(e.data)
Note: Place both of the code blocks above in your controllername.coffee
file, for this example it should be chat.js.coffee
in your app/assets/javascript
directory. You also need to make sure it's being loaded in the asset pipeline. require
it in your application.js
file (if you aren't already calling require tree .
).
In your development environment, you'll have to enable parallel requests by adding these two lines to your config/environments/development.rb
:
config.preload_frameworks = true
config.allow_concurrency = true
Now fire up your browser, browse to /chat
and see the magic. When you type a message and click the button, the message will be received by all instances of that webpage.
Well this is how you make a basic chat application in rails
using ActionController::Live
and Redis
. The final code would obviously be very different depending on your requirements but this should get you started.
Some more resources you should check out:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With