क्या आप कभी Facebook पर रहे हैं और पेज को रीफ्रेश किए बिना एक सूचना प्राप्त की है? इस तरह की रियल-टाइम कार्यक्षमता अधिकांश एप्लिकेशन पर JavaScript फ्रेमवर्क का उपयोग करके प्राप्त की जाती है, जैसे React के माध्यम से स्टेट मैनेजमेंट। इनमें से अधिकांश एप्लिकेशन सिंगल-पेज एप्लिकेशन के रूप में कार्य करते हैं, क्योंकि उपयोग के दौरान डेटा को रियल टाइम में अपडेट करने के लिए उन्हें पेज रीलोडिंग की आवश्यकता नहीं होती है। लंबे समय से, Rails एप्लिकेशन इस अर्थ में स्टेटलेस रहे हैं कि एप्लिकेशन की वर्तमान स्थिति प्राप्त करने के लिए आमतौर पर एक पेज रीलोड की आवश्यकता होती है। उदाहरण के लिए, यदि आप एक Rails ऐप पर थे जो थिएटर में उपलब्ध फिल्मों की सूची दिखाता था और एक एडमिन द्वारा एक फिल्म जोड़ी जाती है, तो नई जोड़ी गई फिल्म आपके डैशबोर्ड पर तब तक दिखाई नहीं देगी जब तक आप पेज को रीफ्रेश नहीं करते।
ActionCable क्यों?
ActionCable इस अंतर को पाटता है और Rails एप्लिकेशन में रियल-टाइम अपडेट और इस डायनामिक कार्यक्षमता को संभव बनाता है। यह WebSocket नामक एक संचार प्रोटोकॉल का उपयोग करता है ताकि एप्लिकेशन में स्टेट पेश किया जा सके जबकि अभी भी प्रदर्शनकारी और स्केलेबल हो। इसके साथ, उपयोगकर्ता अपने पेज को रीफ्रेश किए बिना अपने डैशबोर्ड पर अपडेटेड कंटेंट प्राप्त कर सकते हैं।
Turbo-Rails का जादू
TurboRails में टर्बो ड्राइव, टर्बो फ्रेम्स, और टर्बो स्ट्रीम्स शामिल हैं। जब एक टर्बो-फ्रेम में लिपटे पेज के एक हिस्से से एक अनुरोध भेजा जाता है, तो HTML प्रतिक्रिया उस फ्रेम को प्रतिस्थापित करती है जिससे यह निकला यदि उनके पास समान आईडी है। दूसरी ओर, टर्बो स्ट्रीम्स, वेब सॉकेट कनेक्शन पर इन आंशिक पेज अपडेट को सक्षम करते हैं। ActionCable एक चैनल से इन अपडेट्स को प्रसारित करता है, और टर्बो स्ट्रीम इस चैनल का सब्सक्राइबर बनाता है और अपडेट्स डिलीवर करता है। परिणामस्वरूप, मॉडल परिवर्तनों के जवाब में एसिंक्रोनस अपडेट्स सीधे बनाए जा सकते हैं।
हम क्या बनाने का इरादा रखते हैं
इस लेख का इरादा यह दिखाना है कि Turbo ActionCable के साथ पर्दे के पीछे कैसे काम करता है ताकि Rails 6 ऐप में रियल-टाइम अपडेट्स को प्रसारित और प्रदर्शित किया जा सके। इस प्रकार, हम एक चैट ऐप बनाएंगे जिसमें एक चैट रूम किसी भी उपयोगकर्ता द्वारा बनाया जा सकता है, और सभी उपयोगकर्ता उस रूम में संदेश भेज सकते हैं और रियल टाइम में अपडेट प्राप्त कर सकते हैं। हम उपयोगकर्ताओं को एक-दूसरे के साथ निजी तौर पर चैट करने में सक्षम भी बनाएंगे। हम ग्रुप चैट आमंत्रण लागू नहीं करेंगे; ये इस ब्लॉग पोस्ट के दायरे से बाहर हैं क्योंकि इसमें Turbo और ActionCable के बजाय केवल अतिरिक्त डेटाबेस डिजाइन शामिल हैं। हालांकि, इस पाठ के बाद, यदि आप आगे जाना चुनते हैं तो यह केक का एक टुकड़ा होना चाहिए।
यह कैसा दिखना चाहिए, या इसमें क्या शामिल होना चाहिए?
- एक इंडेक्स पेज जो सभी मौजूदा चैट रूम और उपयोगकर्ताओं को सूचीबद्ध करता है।
- यह पेज डायनामिक रूप से अपडेट होना चाहिए जब नए उपयोगकर्ता साइन अप करें या नए रूम बनाए जाएं।
- नए चैट रूम बनाने के लिए एक फॉर्म मौजूद होना चाहिए।
- किसी भी चैट रूम में होने पर संदेश बनाने के लिए एक मैसेज चैट बॉक्स।
शुरू करने के लिए 7 आसान चरण
इस ऐप में, हमें उपयोगकर्ताओं को केवल उनके अद्वितीय उपयोगकर्ता नामों का उपयोग करके लॉगिन करने की आवश्यकता है, जो सत्रों का उपयोग करके प्राप्त किया जाता है।
1. एक नया Rails ऐप बनाएं
rails new chatapp
cd chatapp
2. एक उपयोगकर्ता मॉडल बनाएं और इसे माइग्रेट करें
rails g model User username
rails db:migrate
फिर, हम उपयोगकर्ता नाम के लिए एक अद्वितीय वैलिडेशन जोड़ते हैं क्योंकि हम चाहते हैं कि सभी उपयोगकर्ता नाम उनके मालिकों के लिए अद्वितीय हों। हम अपनी उपयोगकर्ता सूची के लिए वर्तमान उपयोगकर्ता को छोड़कर सभी उपयोगकर्ताओं को लाने के लिए एक स्कोप भी बनाते हैं, क्योंकि हम नहीं चाहते कि कोई उपयोगकर्ता खुद से चैट करे :).
#app/models/user.rb
class User < ApplicationRecord
validates_uniqueness_of :username
scope :all_except, ->(user) { where.not(id: user) }
end
3. एक चैट रूम मॉडल बनाएं
एक चैट रूम का एक नाम होता है और यह एक निजी चैट रूम (दो उपयोगकर्ताओं के बीच निजी चैट के लिए) या सार्वजनिक (सभी के लिए उपलब्ध) हो सकता है। इसे इंगित करने के लिए, हम अपने रूम टेबल में एक is_private कॉलम जोड़ते हैं।
rails g model Room name:string is_private:boolean
इस फाइल को माइग्रेट करने से पहले, हम is_private कॉलम में एक डिफ़ॉल्ट मान जोड़ देंगे ताकि डिफ़ॉल्ट रूप से बनाए गए सभी रूम सार्वजनिक हों, जब तक कि अन्यथा न कहा जाए।
class CreateRooms < ActiveRecord::Migration[6.1]
def change
create_table :rooms do |t|
t.string :name
t.boolean :is_private, default: false
t.timestamps
end
end
end
इस चरण के बाद, rails db:migrate कमांड का उपयोग करके हमारी फाइल माइग्रेट करते हैं। नाम संपत्ति के लिए अद्वितीय वैलिडेशन और हमारी रूम सूची के लिए सभी सार्वजनिक रूम लाने के लिए एक स्कोप जोड़ना भी आवश्यक है।
#app/models/room.rb
class Room < ApplicationRecord
validates_uniqueness_of :name
scope :public_rooms, -> { where(is_private: false) }
end
4. स्टाइलिंग जोड़ें
इस ऐप में न्यूनतम स्टाइलिंग जोड़ने के लिए, हम अपने application.html.erb फाइल में Bootstrap CDN जोड़ देंगे
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
5. प्रमाणीकरण जोड़ें
ऐप में प्रमाणीकरण जोड़ने के लिए हर समय एक current_user वेरिएबल की आवश्यकता होगी। आइए अपने ऐप में बताई गई फाइलों में निम्नलिखित कोड जोड़कर ऑथ सक्षम करें।
#app/controllers/application_controller.rb
helper_method :current_user
def current_user
if session[:user_id]
@current_user = User.find(session[:user_id])
end
end
def log_in(user)
session[:user_id] = user.id
@current_user = user
redirect_to root_path
end
def logged_in?
!current_user.nil?
end
def log_out
session.delete(:user_id)
@current_user = nil
end
#app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.find_by(username: params[:session][:username])
if user
log_in(user)
else
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_path
end
end
#app/views/sessions/new.html.erb
<%= form_for (:session) do |f| %>
<%= f.label :username, 'Enter your username' %>
<%= f.text_field :username, autocomplete: 'off' %>
<%= f.submit 'Sign in' %>
<% end %>
Routes.rb फाइल में निम्नलिखित रूट्स जोड़ें।
#routes.rb
Rails.application.routes.draw do
get '/signin', to: 'sessions#new'
post '/signin', to: 'sessions#create'
delete '/signout', to: 'sessions#destroy'
end
6. कंट्रोलर बनाएं
rails g controller Rooms index का उपयोग करके RoomsController बनाएं और अपने इंडेक्स मेथड में उपयोगकर्ताओं और रूम्स की सूची के लिए वेरिएबल्स जोड़ें।
class RoomsController < ApplicationController
def index
@current_user = current_user
redirect_to '/signin' unless @current_user
@rooms = Room.public_rooms
@users = User.all_except(@current_user)
end
end
7. रूट्स सेटअप करें
routes.rb में रूम, यूजर और रूट रूट्स जोड़ें ताकि हमारा लैंडिंग पेज इंडेक्स पेज हो जो सभी रूम्स और उपयोगकर्ताओं को सूचीबद्ध करता है, और हम अपनी पसंद के किसी भी रूम में नेविगेट कर सकते हैं।
#routes.rb
resources :rooms
resources :users
root 'rooms#index'
व्यू सेटअप करना
Turbo के 'जादू' का हमारा पहला परिचय नए जोड़े गए रूम या नए साइन-अप उपयोगकर्ताओं की घटना में हमारे डैशबोर्ड पर रियल-टाइम अपडेट्स का स्वागत है। इसे प्राप्त करने के लिए, सबसे पहले, हम दो पार्शियल बनाते हैं: प्रत्येक रूम को प्रदर्शित करने के लिए _room.html.erb और प्रत्येक उपयोगकर्ता को प्रदर्शित करने के लिए _user.html.erb। हम इस सूची को index.html.erb फाइल में रेंडर करेंगे जो RoomsController बनाए जाने पर बनाई गई थी, क्योंकि यह हमारा लैंडिंग पेज है।
# app/views/rooms/_room.html.erb
<div>
<%= link_to room.name, room %>
</div>
# app/views/users/_user.html.erb
<div>
<%= link_to user.username, user %>
</div>
हम इन फाइलों को हमारे index.html.erb फाइल में रेंडर करने के लिए आगे बढ़ते हैं, उन्हें सीधे संदर्भित करके नहीं बल्कि उस कलेक्शन को लाने वाले वेरिएबल्स को रेंडर करके। याद रखें कि हमारे RoomsController में, वेरिएबल्स @users और @rooms पहले से ही परिभाषित किए गए हैं।
#app/views/rooms/index.html.erb
<div class="container">
<h5> Hi <%= @current_user.username %> </h5>
<h4> Users </h4>
<%= render @users %>
<h4> Rooms </h4>
<%= render @rooms %>
</div>
कंसोल में, निम्नलिखित कमांड चलाएं:
Room.create(name: 'music')
User.create(username: 'Drake')
User.create(username: 'Elon')
rails s का उपयोग करके अपने Rails सर्वर को चालू करें। आपको साइन इन करने के लिए प्रेरित किया जाएगा; ऊपर बनाए गए उपयोगकर्ताओं में से किसी के भी उपयोगकर्ता नाम के साथ ऐसा करें, और आपको अपना नया बनाया गया रूम और वह उपयोगकर्ता जिसे आपने साइन इन नहीं किया था, नीचे दी गई छवि में दिखाए अनुसार दिखाई देना चाहिए।

Turbo का परिचय
रियल-टाइम अपडेट्स प्राप्त करने के लिए, हमें Turbo इंस्टॉल करने की आवश्यकता है।
bundle add turbo-rails
rails turbo:install
यदि आपके पास Redis इंस्टॉल नहीं है तो निम्न कमांड चलाएं:
sudo apt install redis-server #यदि आपके पास अभी तक नहीं है तो redis इंस्टॉल करता है
redis-server #सर्वर शुरू करता है
import "@hotwired/turbo-rails" का उपयोग करके application.js फाइल में turbo-rails इम्पोर्ट करें
अगला, हम अपने मॉडल्स में विशिष्ट निर्देश जोड़ेंगे और उनसे पूछेंगे कि किसी विशेष चैनल पर किसी नए इंस्टेंस को प्रसारित करें। यह प्रसारण ActionCable द्वारा किया जाता है, जैसा कि हम शीघ्र ही देखेंगे।
#app/models/user.rb
class User < ApplicationRecord
validates_uniqueness_of :username
scope :all_except, ->(user) { where.not(id: user) }
after_create_commit { broadcast_append_to "users" }
end
यहां, हम उपयोगकर्ता मॉडल से "users" नामक चैनल पर प्रसारित करने के लिए कह रहे हैं जब भी उपयोगकर्ता का एक नया इंस्टेंस बनाया जाता है।
#app/models/room.rb
class Room < ApplicationRecord
validates_uniqueness_of :name
scope :public_rooms, -> { where(is_private: false) }
after_create_commit { broadcast_append_to "rooms" }
end
यहां, हम रूम मॉडल से प्रत्येक नए रूम इंस्टेंस बनाए जाने के बाद "rooms" नामक चैनल पर प्रसारित करने के लिए भी कह रहे हैं।
अपने कंसोल को शुरू करें यदि यह पहले से शुरू नहीं है, या यदि यह पहले से चल रहा है तो reload! कमांड का उपयोग करें। इनमें से किसी का भी नया इंस्टेंस बनाने के बाद, हम देखते हैं कि ActionCable निर्दिष्ट चैनल पर जोड़े गए इंस्टेंस को टर्बो स्ट्रीम के रूप में प्रसारित करता है, जिसमें इससे असाइन किया गया पार्शियल टेम्पलेट के रूप में उपयोग किया जाता है। एक नए जोड़े गए रूम के लिए, यह _room.html.erb पार्शियल को नवीनतम जोड़े गए इंस्टेंस से संबंधित मूल्यों के साथ प्रसारित करता है, जैसा कि नीचे दिखाया गया है।

समस्या, हालांकि, यह है कि प्रसारित टेम्पलेट डैशबोर्ड पर दिखाई नहीं देता है। ऐसा इसलिए है क्योंकि हमें अपने व्यू में प्रसारण का एक रिसीवर जोड़ने की आवश्यकता है ताकि ActionCable द्वारा जो कुछ भी प्रसारित किया जाता है उसे प्राप्त किया जा सके और जोड़ा जा सके। हम इसे turbo_stream_from टैग जोड़कर करते हैं, जिसमें उस चैनल को निर्दिष्ट किया जाता है जिससे हम प्रसारण प्राप्त करने की आशा करते हैं। जैसा कि ऊपर की छवि में देखा गया है, प्रसारित स्ट्रीम में एक टारगेट एट्रिब्यूट होता है, और यह उस कंटेनर की आईडी निर्दिष्ट करता है जिसमें स्ट्रीम जोड़ा जाएगा। इसका मतलब है कि प्रसारित टेम्प्लेट "rooms" आईडी वाले कंटेनर की खोज करेगा जिसे जोड़ा जाना है; इसलिए, हम अपने इंडेक्स फाइल में उक्त आईडी के साथ एक डिव शामिल करते हैं। इसे प्राप्त करने के लिए, हमारी index.html.erb फाइल में, हम <%= render @users %> को निम्न से प्रतिस्थापित करते हैं:
<%= turbo_stream_from "users" %>
<div id="users">
<%= render @users %>
</div>
और <%= render @rooms %> को
<%= turbo_stream_from "rooms" %>
<div id="rooms">
<%= render @rooms %>
</div>
इस क्षण, हम Turbo के जादू का अनुभव कर सकते हैं। हम अपने पेज को रीफ्रेश कर सकते हैं और अपने कंसोल से नए उपयोगकर्ताओं और रूम्स को जोड़ना शुरू कर सकते हैं और उन्हें रियल टाइम में हमारे पेज पर जोड़ा हुआ देख सकते हैं। यिप्पी!!!
कंसोल से नए रूम बनाने से थक गए? आइए एक फॉर्म जोड़ें जो उपयोगकर्ताओं को नए रूम बनाने में सक्षम बनाता है।
#app/views/layouts/_new_room_form.html.erb
<%= form_with(model: @room, remote: true, class: "d-flex" ) do |f| %>
<%= f.text_field :name, class: "form-control", autocomplete: 'off' %>
<%= f.submit data: { disable_with: false } %>
<% end %>
उपरोक्त फॉर्म में, @room का उपयोग किया जाता है, लेकिन इसे अभी तक हमारे कंट्रोलर में परिभाषित नहीं किया गया है; इस प्रकार, हम इसे परिभाषित करते हैं और इसे हमारे RoomsController के इंडेक्स मेथड में जोड़ते हैं।
@room = Room.new
जब क्रिएट बटन पर क्लिक किया जाता है, तो यह RoomsController में एक क्रिएट मेथड पर रूट करेगा, जो वर्तमान में मौजूद नहीं है; इसलिए, हमें इसे जोड़ने की आवश्यकता है।
#app/controllers/rooms_controller.rb
def create
@room = Room.create(name: params["room"]["name"])
end
हम इस फॉर्म को अपने इंडेक्स फाइल में इसके पार्शियल को रेंडर करके जोड़ सकते हैं:
<%= render partial: "layouts/new_room_form" %>
इसके अलावा, हम पेज को रूम्स और उपयोगकर्ताओं की सूची के लिए एक भाग और चैट के लिए दूसरे भाग में विभाजित करने के लिए कुछ Bootstrap क्लासेस जोड़ सकते हैं।
<div class="row">
<div class="col-md-2">
<h5> Hi <%= @current_user.username %> </h5>
<h4> Users </h4>
<div>
<%= turbo_stream_from "users" %>
<div id="users">
<%= render @users %>
</div>
</div>
<h4> Rooms </h4>
<%= render partial: "layouts/new_room_form" %>
<div>
<%= turbo_stream_from "rooms" %>
<div id="rooms">
<%= render @rooms %>
</div>
</div>
</div>
<div class="col-md-10 bg-dark">
The chat box stays here
</div>
</div>
रियल-टाइम में अपडेट किए गए जोड़े गए रूम्स की छवि
अब, नए रूम बनाने पर, हम देख सकते हैं कि ये रूम बनाए गए हैं, और पेज रियल-टाइम में अपडेट होता है। आपने शायद यह भी देखा होगा कि फॉर्म प्रत्येक सबमिशन के बाद क्लियर नहीं होता है; हम इस मुद्दे को बाद में Stimulus का उपयोग करके संभालेंगे।
ग्रुप चैट्स
ग्रुप चैट्स के लिए, हमें व्यक्तिगत रूम्स पर रूट करने में सक्षम होने की आवश्यकता है लेकिन उसी पेज पर बने रहें। हम ऐसा अपने RoomsController शो मेथड में इंडेक्स पेज के लिए आवश्यक सभी वेरिएबल्स जोड़कर और अभी भी इंडेक्स पेज को रेंडर करके करते हैं।
#app/controllers/rooms_controller.rb
def show
@current_user = current_user
@single_room = Room.find(params[:id])
@rooms = Room.public_rooms
@users = User.all_except(@current_user)
@room = Room.new
render "index"
end
शो मेथड में @single_room नामक एक अतिरिक्त वेरिएबल जोड़ा गया है। यह हमें उस विशेष रूम को देता है जिस पर रूट किया गया है; इसलिए, हम अपने इंडेक्स पेज पर एक सशर्त स्टेटमेंट जोड़ सकते हैं जो उस रूम का नाम दिखाता है जिस पर हमने क्लिक किया है जब एक रूम नाम पर क्लिक किया जाता है। यह col-md-10 क्लास नाम वाले डिव के भीतर जोड़ा जाता है, जैसा कि नीचे दिखाया गया है।
<div class="col-md-10 bg-dark text-light">
<% if @single_room %>
<h4 class="text-center">
<%= @single_room.name %>
</h4>
<% end %>
</div>
कई रूम्स में जाने को दिखाने वाली छवि
अब हम कुछ अधिक रसदार सामग्री, मैसेजिंग पर आगे बढ़ेंगे। हमें अपने चैट सेक्शन को 100vh की ऊंचाई देनी होगी ताकि यह पेज को भर दे और इसमें संदेश निर्माण के लिए एक चैट बॉक्स शामिल करें। चैटबॉक्स को एक मैसेज मॉडल की आवश्यकता होगी। इस मॉडल में एक उपयोगकर्ता रेफरेंस और एक रूम रेफरेंस होगा, क्योंकि एक मैसेज अपने निर्माता और उस रूम के बिना अस्तित्व में नहीं रह सकता जिसके लिए यह था।
rails g model Message user:references room:references content:text
rails db:migrate
हमें उपयोगकर्ता और रूम मॉडल में इस एसोसिएशन की पहचान करने की भी आवश्यकता है उनमें निम्नलिखित लाइन जोड़कर।
has_many :messages
आइए मैसेज निर्माण के लिए अपने पेज पर एक फॉर्म जोड़ें और इसमें स्टाइलिंग जोड़ें:
#app/views/layouts/_new_message_form.html.erb
<div class="form-group msg-form">
<%= form_with(model: [@single_room ,@message], remote: true, class: "d-flex" ) do |f| %>
<%= f.text_field :content, id: 'chat-text', class: "form-control msg-content", autocomplete: 'off' %>
<%= f.submit data: { disable_with: false }, class: "btn btn-primary" %>
<% end %>
</div>
#app/assets/stylesheets/rooms.scss
.msg-form {
position: fixed;
bottom: 0;
width: 90%
}
.col-md-10 {
height: 100vh;
overflow: scroll;
}
.msg-content {
width: 80%;
margin-right: 5px;
}
इस फॉर्म में एक @message वेरिएबल शामिल है; इसलिए, हमें इसे अपने कंट्रोलर में परिभाषित करने की आवश्यकता है। हम इसे हमारे RoomsController के शो मेथड में जोड़ते हैं।
@message = Message.new
हमारे routes.rb फाइल में, हम रूम रिसोर्स के भीतर मैसेज रिसोर्स जोड़ते हैं, क्योंकि यह उस रूम की आईडी को पैरामीटर्स से जोड़ता है जिससे मैसेज बनाया जा रहा है।
resources :rooms do
resources :messages
end
जब भी एक नया मैसेज बनाया जाता है, हम चाहते हैं कि यह उस रूम में प्रसारित हो जिसमें इसे बनाया गया था। ऐसा करने के लिए, हमें एक मैसेज पार्शियल की आवश्यकता है जो मैसेज को रेंडर करे। चूंकि यह वही है जो प्रसारित किया जाएगा, हमें उस विशेष रूम के लिए प्रसारित मैसेज को प्राप्त करने वाला एक turbo_stream और एक डिव भी चाहिए जो इन मैसेजेस को जोड़ने के लिए कंटेनर के रूप में काम करेगा। आइए न भूलें कि इस कंटेनर की आईडी प्रसारण के टारगेट के समान होनी चाहिए।
हम इसे अपने मैसेज मॉडल में जोड़ते हैं:
#app/models/message.rb
after_create_commit { broadcast_append_to self.room }
इस तरह, यह उस विशेष रूम में प्रसारित होता है जिसमें इसे बनाया गया था।
हम अपने इंडेक्स फाइल में स्ट्रीम, मैसेज कंटेनर, और मैसेज फॉर्म भी जोड़ते हैं:
#app/views/rooms/index.html.erb में @single_room कंडीशन के भीतर
<%= turbo_stream_from @single_room %>
<div id="messages">
</div>
<%= render partial: 'layouts/new_message_form' >
हम मैसेज पार्शियल बनाते हैं जो प्रसारित किया जाएगा, और इसमें, हम प्रेषक का उपयोगकर्ता नाम केवल तभी दिखाते हैं यदि रूम सार्वजनिक है।
#app/views/messages/_message.html.erb
<div>
<% unless message.room.is_private %>
<h6 class="name">
<%= message.user.username %>
</h6>
<% end %>
<%= message.content %>
</div>
कंसोल से, यदि हम एक मैसेज बनाते हैं, तो हम देख सकते हैं कि यह असाइन किए गए टेम्पलेट का उपयोग करके अपने रूम में प्रसारित होता है।

डैशबोर्ड से मैसेज निर्माण सक्षम करने के लिए, हमें MessagesController में क्रिएट मेथड जोड़ने की आवश्यकता है।
#app/controllers/messages_controller.rb
class MessagesController < ApplicationController
def create
@current_user = current_user
@message = @current_user.messages.create(content: msg_params[:content], room_id: params[:room_id])
end
private
def msg_params
params.require(:message).permit(:content)
end
end
यह वही है जो हमें मिलता है: 
जैसा कि हम ऊपर वीडियो में देख सकते हैं, मैसेजेस जोड़े जाते हैं, लेकिन यदि हम दूसरे चैट रूम में जाते हैं, तो ऐसा लगता है कि जब हम वापस लौटते हैं तो हम पिछले वाले में मैसेजेस खो देते हैं। ऐसा इसलिए है क्योंकि हम डिस्प्ले के लिए एक रूम से संबंधित मैसेजेस को ला नहीं रहे हैं। ऐसा करने के लिए, RoomsController शो मेथड में, हम एक वेरिएबल जोड़ते हैं जो एक रूम से संबंधित सभी मैसेजेस को लाता है, और इंडेक्स पेज पर, हम लाए गए मैसेजेस को रेंडर करते हैं। हम यह भी देख सकते हैं कि मैसेज फॉर्म एक मैसेज भेजे जाने के बाद क्लियर नहीं होता है; इसे नीचे Stimulus का उपयोग करके संभाला जाएगा।
#app/controllers/rooms_controller.rb के शो मेथड में
@messages = @single_room.messages
#'messages' आईडी वाले डिव के भीतर
<%= render @messages %>
अब, प्रवेश पर प्रत्येक रूम के अपने मैसेजेस लोड होंगे।
हमें इसे और अधिक प्रस्तुत करने योग्य बनाने की आवश्यकता है वर्तमान उपयोगकर्ता के मैसेजेस को दाईं ओर और दूसरों को बाईं ओर संरेखित करके। इसे प्राप्त करने का सबसे सीधा तरीका message.user == current_user कंडीशन के आधार पर क्लासेस असाइन करना होगा, लेकिन लोकल वेरिएबल्स स्ट्रीम्स के लिए उपलब्ध नहीं हैं; इसलिए, एक प्रसारित मैसेज के लिए, कोई current_user नहीं होगा। हम क्या कर सकते हैं? हम मैसेज कंटेनर को मैसेज प्रेषक आईडी के आधार पर एक क्लास असाइन कर सकते हैं और फिर current_user हेल्पर मेथड का लाभ उठाकर अपने application.html.erb फाइल में एक स्टाइल जोड़ सकते हैं। इस तरह, यदि वर्तमान उपयोगकर्ता की आईडी 2 है, तो application.html.erb में स्टाइल टैग में क्लास .msg-2 होगी, जो हमारे मैसेज पार्शियल में क्लास से भी मेल खाएगी जब मैसेज प्रेषक वर्तमान उपयोगकर्ता होगा।
#app/views/messages/_message.html.erb
<div class="cont-<%= message.user.id %>">
<div class="message-box msg-<%= message.user.id %> ">
<% unless message.room.is_private %>
<h6 class="name">
<%= message.user.username %>
</h6>
<% end %>
<%= message.content %>
</div>
</div>
हम message-box स्टाइलिंग जोड़ते हैं:
#app/assets/stylesheets/rooms.scss
.message-box {
width: fit-content;
max-width: 40%;
padding: 5px;
border-radius: 10px;
margin-bottom: 10px;
background-color: #555555;
padding: 10px
}
हमारे application.html.erb फाइल के हेड टैग में।
#app/views/layouts/application.html.erb
<style>
<%= ".msg-#{current_user&.id}" %> { background-color: #007bff !important; padding: 10px; }
<%= ".cont-#{current_user&.id}" %> { display: flex; justify-content: flex-end }
</style>
हम background-color में !important टैग जोड़ते हैं क्योंकि हम वर्तमान उपयोगकर्ता के लिए बैकग्राउंड कलर को ओवरराइड करना चाहते हैं।
हमारी चैट तब इस तरह दिखती है: 
निजी चैट
निजी चैट के लिए आवश्यक अधिकांश काम ग्रुप चैट सेटअप के दौरान किया गया था। अब हमें केवल निम्नलिखित करने की आवश्यकता है:
- किसी विशेष उपयोगकर्ता पर रूट करते समय एक निजी चैट के लिए एक निजी रूम बनाएं यदि ऐसा रूम मौजूद नहीं है।
- ऐसे रूम्स के लिए प्रतिभागी बनाएं ताकि घुसपैठिया ऐसे रूम्स में संदेश न भेज सके, यहां तक कि कंसोल से भी।
- नए बने निजी रूम्स को रूम सूची में प्रसारित होने से रोकें।
- जब यह निजी चैट हो तो रूम के नाम के बजाय उपयोगकर्ता का नाम प्रदर्शित करें।
किसी विशेष उपयोगकर्ता पर रूट करने में, वर्तमान उपयोगकर्ता इंगित कर रहा है कि वे उस उपयोगकर्ता के साथ निजी तौर पर चैट करना चाहते हैं। इसलिए, हमारे UsersController में, हम जांचते हैं कि क्या इन दो के बीच ऐसा निजी रूम मौजूद है। यदि हां, तो यह हमारा @single_room वेरिएबल बन जाता है; अन्यथा, हम इसे बनाएंगे। हम प्रत्येक निजी चैट रूम के लिए एक विशेष रूम नाम बनाएंगे ताकि हम जरूरत पड़ने पर इसे संदर्भित कर सकें। हमें इंडेक्स पेज द्वारा आवश्यक सभी वेरिएबल्स को शो मेथड में शामिल करने की भी आवश्यकता है ताकि हम उसी पेज पर बने रहें।
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@current_user = current_user
@rooms = Room.public_rooms
@users = User.all_except(@current_user)
@room = Room.new
@message = Message.new
@room_name = get_name(@user, @current_user)
@single_room = Room.where(name: @room_name).first || Room.create_private_room([@user, @current_user], @room_name)
@messages = @single_room.messages
render "rooms/index"
end
private
def get_name(user1, user2)
users = [user1, user2].sort
"private_#{users[0].id}_#{users[1].id}"
end
end
जैसा कि हम ऊपर देख सकते हैं, हमें अपने Room मॉडल में एक create_private_room मेथड जोड़ने की आवश्यकता है, जो एक निजी रूम और उसके प्रतिभागियों को बनाएगा। यह हमें Participant नामक एक नया मॉडल बनाने की ओर ले जाता है, जो एक उपयोगकर्ता और निजी रूम को इंगित करेगा जिससे वह संबंधित है।
rails g model Participant user:references room:references
rails db:migrate
हमारे रूम मॉडल में, हम create_private_room मेथड जोड़ते हैं और अपने after_create कॉल को केवल रूम नाम प्रसारित करने के लिए बदलते हैं यदि यह निजी रूम नहीं है।
#app/models/room.rb
has_many :participants, dependent: :destroy
after_create_commit { broadcast_if_public }
def broadcast_if_public
broadcast_append_to "rooms" unless self.is_private
end
def self.create_private_room(users, room_name)
single_room = Room.create(name: room_name, is_private: true)
users.each do |user|
Participant.create(user_id: user.id, room_id: single_room.id)
end
single_room
end
अन्य उपयोगकर्ताओं को निजी रूम में मैसेज भेजने से रोकने के लिए जब वे प्रतिभागी नहीं हैं, हम Message मॉडल में एक before_create चेक जोड़ते हैं। इस प्रकार, निजी रूम्स के लिए, एक पुष्टि की जाती है कि मैसेज का प्रेषक वास्तव में उस निजी वार्तालाप का प्रतिभागी है इससे पहले कि ऐसा मैसेज बनाया जाए। हमें ध्यान देना चाहिए कि डैशबोर्ड से, यदि आप प्रतिभागी नहीं हैं तो निजी रूम में मैसेज भेजना असंभव है क्योंकि आपको केवल उपयोगकर्ता नाम पर क्लिक करना होगा और फिर रूम दोनों उपयोगकर्ताओं के लिए बनाया जाता है। यह चेक केवल अतिरिक्त सुरक्षा के लिए है, क्योंकि एक मैसेज कंसोल से एक गैर-प्रतिभागी द्वारा बनाया जा सकता है।
#app/models/message.rb
before_create :confirm_participant
def confirm_participant
if self.room.is_private
is_participant = Participant.where(user_id: self.user.id, room_id: self.room.id).first
throw :abort unless is_participant
end
end
निजी चैट के दौरान रूम के नाम के बजाय उपयोगकर्ता का उपयोगकर्ता नाम प्रदर्शित करने के लिए, हम अपने इंडेक्स पेज पर इंगित करते हैं कि यदि @user वेरिएबल मौजूद है, तो उपयोगकर्ता का उपयोगकर्ता नाम दिखाया जाना चाहिए। ध्यान दें कि यह वेरिएबल केवल UsersController शो मेथड में मौजूद है। यह रूम नाम दिखाने वाले h4 टैग को इसमें बदलने की ओर ले जाता है:
<h4 class="text-center">
<%= @user&.username || @single_room.name %>
</h4>
अब, जब हम किसी उपयोगकर्ता पर नेविगेट करते हैं, तो हम रूम के नाम के बजाय उपयोगकर्ता का उपयोगकर्ता नाम देखते हैं, और हम संदेश भेज और प्राप्त कर सकते हैं। आइए अपने होम पेज पर साइन-आउट लिंक जोड़ना न भूलें।
<%= link_to 'Sign Out', signout_path, method: :delete %>
जैसा कि आप नीचे देख सकते हैं, निजी चैट के लिए, प्रेषक का उपयोगकर्ता नाम नहीं दिखाया जाता है। हम अपने मैसेजेस को उनकी स्थिति से पहचानने में सक्षम हैं।

Stimulus
हम फॉर्म्स को क्लियर करने के लिए Stimulus का उपयोग करेंगे क्योंकि पूर्ण-पेज री-रेंडर नहीं होता है, और फॉर्म्स नए मॉडल इंस्टेंस निर्माण पर क्लियर नहीं होते हैं।
bundle add stimulus-rails
rails stimulus:install
यह निम्नलिखित फाइल्स जोड़ता है, जैसा कि नीचे दी गई छवि में देखा गया है। 
हम अपने फॉर्म्स को रीसेट करने के लिए एक reset_form_controller.js फाइल बनाते हैं और इसमें निम्न फंक्शन जोड़ते हैं।
//app/javascript/controllers/reset_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
reset() {
this.element.reset()
}
}
फिर, हम अपने फॉर्म्स में एक डेटा एट्रिब्यूट जोड़ते हैं, जो कंट्रोलर और एक्शन को इंगित करता है।
data: { controller: "reset-form", action: "turbo:submit-end->reset-form#reset" }
उदाहरण के लिए, हमारे मैसेज फॉर्म का form_with टैग निम्न में बदल जाता है:
<%= form_with(model: [@single_room ,@message], remote: true, class: "d-flex", data: { controller: "reset-form", action: "turbo:submit-end->reset-form#reset" }) do |f| %>
अंत में, यही सब कुछ आवश्यक है; नए मैसेज या रूम के निर्माण के बाद हमारे फॉर्म्स क्लियर हो जाते हैं। हमें यह भी ध्यान देना चाहिए कि Stimulus एक्शन "ajax:success->reset-form#reset" भी एक फॉर्म को क्लियर कर सकता है जब एक ajax:success इवेंट होता है।
निष्कर्ष
इस ऐप में, हमने Turbo Streams के ऐपेंडिंग एक्शन पर ध्यान केंद्रित किया है, लेकिन यह सब नहीं है जो Turbo Streams में शामिल है। वास्तव में, Turbo Streams में पांच एक्शन होते हैं: append, prepend, replace, update, और remove। रियल टाइम में चैट मैसेजेस को हटाने और अपडेट करने को लागू करने के लिए, ये एक्शन काम आएंगे, और Turbo फ्रेम्स के कुछ ज्ञान और वे कैसे काम करते हैं, इसकी आवश्यकता हो सकती है। यह भी ध्यान रखना महत्वपूर्ण है कि उन एप्लिकेशन के लिए जो कुछ विशेषताओं के लिए WebSocket अपडेट्स पर निर्भर करते हैं, खराब कनेक्शन पर, या यदि सर्वर समस्याएं हैं, तो आपका WebSocket डिस्कनेक्ट हो सकता है। इसलिए, अपने ऐप्स में Turbo Streams का उपयोग करना केवल तभी उचित है जब यह अत्यंत महत्वपूर्ण हो।