আপনি কি কখনও ফেসবুকে ছিলেন এবং পৃষ্ঠাটি রিফ্রেশ না করেই একটি বিজ্ঞপ্তি পেয়েছেন? জাভাস্ক্রিপ্ট ফ্রেমওয়ার্ক ব্যবহার করে বেশিরভাগ অ্যাপ্লিকেশনে এই ধরনের রিয়েল-টাইম কার্যকারিতা অর্জন করা হয়, যেমন রাষ্ট্র পরিচালনার মাধ্যমে প্রতিক্রিয়া। এই অ্যাপ্লিকেশনগুলির বেশিরভাগই একক-পৃষ্ঠার অ্যাপ্লিকেশন হিসাবে কাজ করে, কারণ তাদের বাস্তব সময়ে ডেটা আপডেট করার জন্য ব্যবহারের সময় পৃষ্ঠা পুনরায় লোড করার প্রয়োজন হয় না৷ দীর্ঘ সময়ের জন্য, রেল অ্যাপ্লিকেশনগুলি এই অর্থে রাষ্ট্রহীন ছিল যে সাধারণত একটি পৃষ্ঠা পুনরায় লোড করার প্রয়োজন হয়৷ আবেদনের বর্তমান অবস্থা। উদাহরণস্বরূপ, আপনি যদি এমন একটি Rails অ্যাপে থাকেন যা থিয়েটারে উপলব্ধ চলচ্চিত্রগুলির একটি তালিকা দেখায় এবং একটি প্রশাসক দ্বারা একটি চলচ্চিত্র যোগ করা হয়, তবে আপনি পৃষ্ঠাটি রিফ্রেশ না করা পর্যন্ত নতুন যোগ করা চলচ্চিত্রটি আপনার ড্যাশবোর্ডে প্রদর্শিত হবে না৷
কেন ActionCable?
ActionCable এই ব্যবধান পূরণ করে এবং একটি রেল অ্যাপ্লিকেশনে রিয়েল-টাইম আপডেট এবং এই গতিশীল কার্যকারিতা থাকা সম্ভব করে তোলে। এটি একটি যোগাযোগ প্রোটোকল ব্যবহার করে যাকে বলা হয় WebSocket নামক একটি স্টেটকে অ্যাপ্লিকেশানে প্রবর্তন করার জন্য যখন এখনও পারফরম্যান্ট এবং মাপযোগ্য। এটির মাধ্যমে, ব্যবহারকারীরা তাদের পৃষ্ঠাগুলিকে রিফ্রেশ না করেই তাদের ড্যাশবোর্ডে আপডেট করা সামগ্রী পেতে পারেন৷
৷দ্য ম্যাজিক অফ টার্বো-রেল
TurboRails টার্বো ড্রাইভ, টার্বো ফ্রেম এবং টার্বো স্ট্রীম নিয়ে গঠিত। যখন একটি টার্বো-ফ্রেমে মোড়ানো একটি পৃষ্ঠার একটি অংশ থেকে একটি অনুরোধ পাঠানো হয়, তখন এইচটিএমএল প্রতিক্রিয়াটি সেই ফ্রেমটিকে প্রতিস্থাপন করে যেটি এটি থেকে উদ্ভূত হয়েছিল যদি তাদের একই আইডি থাকে৷ অন্যদিকে টার্বো স্ট্রিমগুলি, ওয়েবে এই আংশিক পৃষ্ঠা আপডেটগুলি সক্ষম করে৷ সকেট সংযোগ। ActionCable একটি চ্যানেল থেকে এই আপডেটগুলি সম্প্রচার করে এবং Turbo স্ট্রিম এই চ্যানেলের গ্রাহক তৈরি করে এবং আপডেটগুলি সরবরাহ করে৷ ফলস্বরূপ, মডেল পরিবর্তনের প্রতিক্রিয়া হিসাবে অ্যাসিঙ্ক্রোনাস আপডেটগুলি সরাসরি তৈরি করা যেতে পারে৷
আমরা কী তৈরি করতে চাই
এই নিবন্ধটির উদ্দেশ্য হল একটি Rails 6 অ্যাপে রিয়েল-টাইম আপডেট সম্প্রচার এবং প্রদর্শন করতে পর্দার আড়ালে টার্বো অ্যাকশনকেবলের সাথে কীভাবে কাজ করে তা প্রদর্শন করা। এইভাবে, আমরা একটি চ্যাট অ্যাপ তৈরি করব যাতে যে কোনও ব্যবহারকারী একটি চ্যাট রুম তৈরি করতে পারে এবং সমস্ত ব্যবহারকারী সেই ঘরে বার্তা পাঠাতে এবং রিয়েল টাইমে আপডেট পেতে পারে। আমরা ব্যবহারকারীদের একে অপরের সাথে ব্যক্তিগতভাবে চ্যাট করতে সক্ষম করব। আমরা গ্রুপ চ্যাট আমন্ত্রণ বাস্তবায়ন করা হবে না; এগুলি এই ব্লগ পোস্টের সুযোগের বাইরে কারণ এটি শুধুমাত্র Turbo এবং ActionCable এর পরিবর্তে অতিরিক্ত ডাটাবেস ডিজাইন জড়িত। যাইহোক, এই পাঠের পরে, আপনি যদি আরও যেতে চান তবে এটি একটি কেকের টুকরো হওয়া উচিত।
এটি দেখতে কেমন হওয়া উচিত, বা এতে কী থাকা উচিত?
- একটি সূচী পাতা যা সমস্ত বিদ্যমান চ্যাট রুম এবং ব্যবহারকারীদের তালিকা করে।
- নতুন ব্যবহারকারীরা সাইন আপ করলে বা নতুন রুম তৈরি হলে এই পৃষ্ঠাটি গতিশীলভাবে আপডেট করা উচিত।
- নতুন চ্যাট রুম তৈরি করার জন্য একটি ফর্ম উপস্থিত।
- যেকোন চ্যাট রুমে থাকাকালীন বার্তা তৈরি করার জন্য একটি বার্তা চ্যাট বক্স।
শুরু করার ৭টি সহজ ধাপ
এই অ্যাপে, আমরা ব্যবহারকারীদের শুধুমাত্র তাদের অনন্য ব্যবহারকারীর নাম ব্যবহার করে লগইন করতে চাই, যা সেশন ব্যবহার করে অর্জন করা হয়।
1. একটি নতুন রেল অ্যাপ তৈরি করুন
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 ফাইলে বুটস্ট্র্যাপ 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
এ রেন্ডার করব রুমকন্ট্রোলার তৈরি করার সময় যে ফাইলটি তৈরি করা হয়েছিল, কারণ এটি আমাদের ল্যান্ডিং পৃষ্ঠা।
# 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 সার্ভার চালু করুন . আপনাকে সাইন ইন করতে বলা হবে; উপরে তৈরি করা ব্যবহারকারীদের একজনের ব্যবহারকারীর নামের সাথে এটি করুন, এবং আপনার নতুন তৈরি করা রুম এবং আপনি যে ব্যবহারকারী সাইন ইন করেননি তা নীচের ছবিতে দেখানো হিসাবে দেখানো উচিত।
Introducing Turbo
৷রিয়েল-টাইম আপডেটগুলি পেতে, আমাদের টার্বো ইনস্টল করা দরকার৷
bundle add turbo-rails
rails turbo:install
আপনার যদি Redis ইনস্টল না থাকে তবে নিম্নলিখিত কমান্ডগুলি চালান:
sudo apt install redis-server
#installs redis if you don't have it yet
redis-server
#starts the server
turbo-rails
আমদানি করুন application.js
-এ import "@hotwired/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
এখানে, আমরা ব্যবহারকারী মডেলকে প্রতিটি ব্যবহারকারীর নতুন উদাহরণ তৈরি করার পরে "ব্যবহারকারী" নামে একটি চ্যানেলে সম্প্রচার করতে বলছি৷
#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
এখানে, আমরা রুম মডেলকে প্রতিটি নতুন রুমের উদাহরণ তৈরি করার পরে "রুম" নামে একটি চ্যানেলে সম্প্রচার করতে বলছি৷
আপনার কনসোলটি চালু করুন যদি এটি ইতিমধ্যেই শুরু না হয়, অথবা reload!
ব্যবহার করুন৷ এটি ইতিমধ্যে আপ এবং চলমান হলে কমান্ড. এইগুলির যেকোন একটির একটি নতুন দৃষ্টান্ত তৈরি করার পরে, আমরা দেখতে পাই যে ActionCable একটি টেমপ্লেট হিসাবে এটিকে নির্ধারিত আংশিক ব্যবহার করে একটি টার্বো স্ট্রিম হিসাবে নির্দিষ্ট চ্যানেলে যোগ করা উদাহরণ সম্প্রচার করে। নতুন যোগ করা রুমের জন্য, এটি আংশিক _room.html.erb
সম্প্রচার করে নতুন যোগ করা উদাহরণের সাথে সম্পর্কিত মানগুলির সাথে, যেমনটি নীচে দেখানো হয়েছে।
সমস্যা, যদিও, সম্প্রচারিত টেমপ্লেট ড্যাশবোর্ডে প্রদর্শিত হয় না। এর কারণ হল আমাদের দৃষ্টিভঙ্গিতে সম্প্রচারের একটি রিসিভার যোগ করতে হবে যাতে অ্যাকশনকেবল দ্বারা সম্প্রচার করা যাই হোক না কেন তা গ্রহণ করা যায় এবং যুক্ত করা যায়। আমরা একটি turbo_stream_from
যোগ করে এটি করি ট্যাগ করুন, যে চ্যানেল থেকে আমরা সম্প্রচার পাওয়ার আশা করি তা উল্লেখ করে। উপরের ছবিতে দেখা গেছে, সম্প্রচারিত স্ট্রীমের একটি টার্গেট অ্যাট্রিবিউট রয়েছে এবং এটি সেই কন্টেইনারটির আইডি নির্দিষ্ট করে যেখানে স্ট্রিমটি যুক্ত করা হবে। এর মানে হল যে সম্প্রচারিত টেমপ্লেটটি সংযুক্ত করার জন্য "রুম" আইডি সহ একটি ধারক অনুসন্ধান করবে; তাই, আমরা আমাদের ইনডেক্স ফাইলে উল্লিখিত আইডি সহ একটি ডিভি অন্তর্ভুক্ত করি। এটি অর্জন করতে, আমাদের 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
ব্যবহার করা হয়, কিন্তু এটি এখনও আমাদের নিয়ামকের মধ্যে সংজ্ঞায়িত করা হয়নি; এইভাবে, আমরা এটিকে সংজ্ঞায়িত করি এবং এটিকে আমাদের রুম কন্ট্রোলারের সূচী পদ্ধতিতে যোগ করি।
@room = Room.new
যখন তৈরি বোতামটি ক্লিক করা হয়, তখন এটি রুমকন্ট্রোলারে একটি তৈরি পদ্ধতিতে রুট করবে, যা এই মুহূর্তে বিদ্যমান নেই; তাই, আমাদের এটা যোগ করতে হবে।
#app/controllers/rooms_controller.rb
def create
@room = Room.create(name: params["room"]["name"])
end
আমরা এই ফর্মটিকে আমাদের ইনডেক্স ফাইলে এটির আংশিক রেন্ডার করে যুক্ত করতে পারি:
<%= render partial: "layouts/new_room_form" %>
এছাড়াও, আমরা কিছু বুটস্ট্র্যাপ ক্লাস যোগ করতে পারি পৃষ্ঠাটিকে একটি অংশে রুম এবং ব্যবহারকারীদের তালিকা এবং অন্যটি চ্যাটের জন্য ভাগ করতে।
<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>
রিয়েল-টাইমে আপডেট করা রুমগুলির ছবি
এখন, নতুন রুম তৈরি করার পরে, আমরা দেখতে পাচ্ছি যে এই রুমগুলি তৈরি করা হয়েছে, এবং পৃষ্ঠাটি রিয়েল-টাইমে আপডেট করা হয়েছে। আপনি সম্ভবত এটিও লক্ষ্য করেছেন যে প্রতিটি জমা দেওয়ার পরে ফর্মটি পরিষ্কার হয় না; আমরা পরবর্তীতে উদ্দীপনা ব্যবহার করে এই সমস্যাটি পরিচালনা করব।
গ্রুপ চ্যাট
গ্রুপ চ্যাটের জন্য, আমাদের পৃথক রুমে রুট করতে সক্ষম হতে হবে কিন্তু একই পৃষ্ঠায় থাকতে হবে। আমরা আমাদের 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
নামে একটি অতিরিক্ত ভেরিয়েবল শো পদ্ধতিতে যোগ করা হয়েছে। এটি আমাদের নির্দিষ্ট রুম দেয় যা রুট করা হচ্ছে; তাই, আমরা আমাদের সূচী পৃষ্ঠায় একটি শর্তসাপেক্ষ বিবৃতি যোগ করতে পারি যা একটি রুমের নাম ক্লিক করার সময় আমরা যে রুমে নেভিগেট করেছি তার নামটি দেখায়। এটি div-এর মধ্যে 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
রয়েছে পরিবর্তনশীল; তাই, আমাদের কন্ট্রোলারে এটি সংজ্ঞায়িত করতে হবে। আমরা এটিকে আমাদের রুম কন্ট্রোলারের শো পদ্ধতিতে যুক্ত করি।
@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 }
এইভাবে, এটি সেই নির্দিষ্ট ঘরে সম্প্রচার করে যেখানে এটি তৈরি করা হয়েছিল।
আমরা আমাদের সূচী ফাইলে স্ট্রীম, বার্তা ধারক এবং বার্তা ফর্ম যোগ করি:
#within the @single_room condition in app/views/rooms/index.html.erb
<%= 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>
কনসোল থেকে, যদি আমরা একটি বার্তা তৈরি করি, আমরা দেখতে পাব যে এটি নির্ধারিত টেমপ্লেট ব্যবহার করে তার ঘরে সম্প্রচার করা হয়েছে৷
ড্যাশবোর্ড থেকে বার্তা তৈরি সক্ষম করতে, আমাদের মেসেজ কন্ট্রোলারে তৈরি করার পদ্ধতি যোগ করতে হবে।
#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 শো পদ্ধতিতে, আমরা একটি ভেরিয়েবল যোগ করি যা একটি রুমের সমস্ত বার্তা নিয়ে আসে এবং সূচী পৃষ্ঠায়, আমরা আনা বার্তাগুলিকে রেন্ডার করি। আমরা আরও দেখতে পারি যে একটি বার্তা পাঠানোর পরে বার্তা ফর্মটি পরিষ্কার হয় না; এটি স্টিমুলাস ডাউন দ্য লাইন দিয়ে পরিচালনা করা হবে।
#in the show method of app/controllers/rooms_controller.rb
@messages = @single_room.messages
#within the div with id of '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>
আমরা !important
যোগ করি background-color
-এ ট্যাগ করুন কারণ আমরা বর্তমান ব্যবহারকারীর জন্য পটভূমির রঙ ওভাররাইড করতে চাই।
আমাদের চ্যাটগুলি এইরকম দেখায়:
ব্যক্তিগত চ্যাট
ব্যক্তিগত চ্যাটের জন্য প্রয়োজনীয় বেশিরভাগ কাজ গ্রুপ চ্যাট সেটআপের সময় করা হয়েছিল। আমাদের এখন যা করতে হবে তা হল নিম্নলিখিত:
- কোন নির্দিষ্ট ব্যবহারকারীকে রুট করার সময় একটি ব্যক্তিগত চ্যাটের জন্য একটি ব্যক্তিগত রুম তৈরি করুন যদি এমন একটি রুম বিদ্যমান না থাকে৷
- এমন কক্ষের জন্য অংশগ্রহণকারীদের তৈরি করুন যাতে কোনও অনুপ্রবেশকারী এই ধরনের কক্ষগুলিতে বার্তা পাঠাতে না পারে, এমনকি কনসোল থেকেও৷
- নতুন তৈরি ব্যক্তিগত রুমগুলিকে রুমের তালিকায় সম্প্রচার করা থেকে বিরত রাখুন৷ ৷
- ব্যক্তিগত চ্যাট হলে রুমের নামের পরিবর্তে ব্যবহারকারীর নাম প্রদর্শন করুন।
একটি নির্দিষ্ট ব্যবহারকারীকে রাউটিং করার সময়, বর্তমান ব্যবহারকারী ইঙ্গিত দিচ্ছে যে তারা সেই ব্যবহারকারীর সাথে ব্যক্তিগতভাবে চ্যাট করতে চায়। তাই, আমাদের 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
আমরা উপরে দেখতে পাচ্ছি, আমাদের একটি 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
অন্যান্য ব্যবহারকারীরা যখন অংশগ্রহণকারী নয় তখন একটি ব্যক্তিগত রুমে বার্তা পাঠাতে বাধা দিতে, আমরা একটি 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 %>
আপনি নীচে দেখতে পাচ্ছেন, ব্যক্তিগত চ্যাটের জন্য, প্রেরকের ব্যবহারকারীর নাম দেখানো হয় না। আমরা তাদের অবস্থান দ্বারা আমাদের নিজস্ব বার্তা সনাক্ত করতে সক্ষম.
উদ্দীপক
আমরা ফর্মগুলি সাফ করার জন্য উদ্দীপনা ব্যবহার করব যেহেতু একটি পূর্ণ-পৃষ্ঠা পুনঃ-রেন্ডার হয় না, এবং একটি নতুন মডেল উদাহরণ তৈরিতে ফর্মগুলি সাফ করা হয় না৷
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| %>
অবশেষে, এই সব যে প্রয়োজন; একটি নতুন বার্তা বা রুম তৈরি করার পরে আমাদের ফর্মগুলি পরিষ্কার হয়ে যায়। আমাদের আরও লক্ষ্য করা উচিত যে উদ্দীপক ক্রিয়া "ajax:success->reset-form#reset"
ajax:success
হলে একটি ফর্মও সাফ করতে পারে ঘটনা ঘটে।
উপসংহার
এই অ্যাপটিতে, আমরা টার্বো স্ট্রীমগুলির সংযোজন অ্যাকশনের উপর ফোকাস করেছি, তবে টার্বো স্ট্রীমগুলির মধ্যে এটিই নয়। প্রকৃতপক্ষে, টার্বো স্ট্রীমগুলি পাঁচটি ক্রিয়া নিয়ে গঠিত:যুক্ত করা, অগ্রসর করা, প্রতিস্থাপন করা, আপডেট করা এবং অপসারণ করা। রিয়েল টাইমে চ্যাট বার্তাগুলি মুছে ফেলা এবং আপডেট করা কার্যকর করতে, এই ক্রিয়াগুলি কাজে আসবে এবং Turbo ফ্রেমের কিছু জ্ঞান এবং সেগুলি কীভাবে কাজ করে সে সম্পর্কে কিছু জ্ঞানের প্রয়োজন হতে পারে৷ এটাও মনে রাখা গুরুত্বপূর্ণ যে অ্যাপ্লিকেশনগুলির জন্য যা কিছু নির্দিষ্ট সময়ের জন্য WebSocket আপডেটের উপর নির্ভর করে বৈশিষ্ট্য, দুর্বল সংযোগে, বা সার্ভারের সমস্যা থাকলে, আপনার ওয়েবসকেট সংযোগ বিচ্ছিন্ন হতে পারে। তাই আপনার অ্যাপে টার্বো স্ট্রীম ব্যবহার করার পরামর্শ দেওয়া হয় যখন এটি অত্যন্ত গুরুত্বপূর্ণ।