আপনি যদি পেজ চেঞ্জ এবং ফর্ম সাবমিশন দ্রুত করার এবং কমপ্লেক্স পেজগুলোক কম্পোনেন্টে ভাগ করার একটি উপায় খুঁজছেন যেকোনো JavaScript কোড লিখতে ছাড়া, এই পোস্ট আপনাকে Rails-কে Hotwire-র সাথে পরবর্তী লেভেলে নিয়ে যেতে সাহায্য করবে। এই আর্টিকেল আপনাকে শিখাবে সার্ভার-সাইড রেন্ডারিং-এর জন্য একটি টুল কীভাবে ব্যবহার করতে হয়।
Hotwire কী?
Hotwire মডার্ন ওয়েব অ্যাপ্লিকেশন বিল্ড করার একটি উপায় প্রদান করে JavaScript লিখতে ছাড়া HTML পাঠিয়ে JSON-এর বদলে ওয়ায়ারের ওভার, যা পেজগুলো দ্রুত লোড করে। এটি রেন্ডারিং সার্ভার-সাইডে মেইনটেইন করে এবং একটি সিম্পলার এবং আরও প্রোডাক্টিভ ডেভেলপমেন্ট এক্সপেরিয়েন্স অনুমতি দেয়, যেন Rails সবসময় তার টেকনোলজিগুলোর মধ্যে অন্তর্ভুক্ত করে আসেছে, SPA-র সাথে জড়িত স্পীড বা রেসপন্সিভনেস ছাড়া।
Hotwire-এর কোর হলো Turbo gem। এটি একটি কমপ্লিমেন্টারি টেকনিক সেট যা পেজ নেভিগেশন এবং ফর্ম সাবমিশন স্পিড আপ করে, কমপ্লেক্স পেজগুলো কম্পোনেন্টে ভাগ করে, এবং পেজের পারশিয়াল আপডেট ট্রান্সমিট করে WebSocket-এর মাধ্যমে (যেটি সম্বলিত ActionCable, চ্যানেলস, এবং স্ট্রিমিং ডেটা)।
আপনি কেন এটি ব্যবহার করবেন?
আপনি যদি JavaScript-র সাথে লড়াই করে থাকেন এবং একটি बेटার ইউজার এক্সপেরিয়েন্সের সুবিধা নিতে চান পেজগুলোর মাঝে নেভিগেট করার ফিলিং দ্রুত হয় সেটি পাওয়ায়, Hotwire আপনার জন্য একটি অপশন।
Hotwire কীভাবে কাজ করে? Hotwire সার্ভার-সাইড রেন্ডারিং (SSR) ব্যবহার করে SPA-র সাথে জড়িত কিছু সমস্যা সমাধান করতে তাদের মূল সুবিধাগুলো রক্ষা করে। SSR রেন্ডারিং প্রসেস রিভার্স করে, SPA রেন্ডারিং এফর্টের একটি অংশ সার্ভারে আনে, ট্রেডিশনাল লোডিং-এর মত। SSR ইউজারদের অ্যাপ্লিকেশন আরও দক্ষতার সাথে লোড করতে দেয় যেহেতু কিছু রেন্ডারিং সার্ভারে করা হয়। পারফরমেন্স উন্নত করার সম্ভাবনার পাশাপাশি, এটি কিছু SEO সমস্যা হ্যান্ডেল করতে সাহায্য করে, যেমন ইন্ডেক্সিং।
সহজ কিনা ব্যবহার করা?
এটি ব্যবহার করা খুব সিম্পল; আপনার শুধু একটি Rails প্রজেক্টের ডিফল্ট প্যাকেজ দরকার (Ruby, RoR, ActionCable, এবং WebSocket), একটি Turbo gem যা সব JS ডিপেন্ডেন্সি ডাউনলোড করে, এবং Redis কিছু টেমপোরারি ডেটা স্টোর করতে WebSocket ব্রাউজ করার সময়।
আপনাকে আর অন্য ভাষা (JS) শিখতে হবে না একটি সিংগেল-পেজ ওয়েব অ্যাপ্লিকেশনের স্পীড পাওয়ার জন্য কারণ Turbo কামপ্লিমেন্টেড হয় Turbo Drive, Frames, Streams, এবং Native-র দ্বারা। Drive লিংক এবং ফর্ম স্পিড আপ করে এবং নেটওয়ার্ক রিলোডের প্রয়োজন কমায়, অন্যদিকে Frames নেটওয়ার্ককে স্বতন্ত্র কনটেক্সটে ভাগ করে যা লোড করা সহজ।
Rails-এ এটি কীভাবে ব্যবহার করা হয়?
এই সেকশন আপনাকে একটি ধাপে ধাপে উদাহরণ দেখাবে।
এই উদাহরণের জন্য, আমাদের দরকার:
- Ruby
- Ruby on Rails
- Redis
- SQLite (ডিফল্ট ডেটাবেস)
- Gem Hotwire
- TurboRails
- StimulusJS
- WebSocket
- ActionCable
আমরা কী প্রজেক্ট তৈরি করব?
আমরা একটি সোশ্যাল মিডিয়া প্রজেক্ট তৈরি করব।
প্রথম, আপনার টার্মিনাল খুলুন। একটি নতুন rails প্রজেক্ট দিয়ে শুরু করা যাক:
rails new social-media
আপনার প্রজেক্টে প্রবেশ করুন:
cd social-media
Hotwire যোগ করা
Hotwire gem আপনার প্রজেক্টে যোগ করুন:
bundle add hotwire-rails
বিকল্পভাবে, আপনার Gemfile খুলুন এবং এটি যোগ করুন:
gem "hotwire-rails"
আপনি যদি دومরা অপশন ব্যবহার করেন, আপনাকে এখন bundle রান করতে হবে।
bundle install
তারপর, আপনি এটি ইনস্টল করবেন।
rails hotwire:install
প্রাথমিক সেটিংস
এখন Hotwire যা প্রাথমিক সেটিংস করলো সেগুলো অ্যানালাইজ করার চমৎকার সময়। Gemfile দেখতে হলে এখন এরকম দেখাবে:
gem 'redis', '~> 4.0'
আমাদের কেন Redis দরকার?
Redis gem যোগ করা হয়েছে কারণ ActionCable-কে দরকার এটি কিছু টেমপোরারি ডেটা স্টোর করতে WebSocket ব্রাউজ করার সময়। যাইহোক, শুধু Redis ইনস্টল করা যথেষ্ট নয় এটি ব্যবহার করার জন্য। আমাদের চেক করতে হবে এটি সঠিকভাবে কনফিগার করা আছে কিনা। আপনি config/cable.yml ফাইলটি দেখলে সেটি এরকম দেখাবে:
development:
adapter: redis
url: redis://localhost:6379/1
নিশ্চিত করুন আপনার অ্যাপ্লিকেশন শুরু করার সময় Redis চলছে (redis-server)।
JS ডিপেন্ডেন্সি
package.json-এ ডিপেন্ডেন্সিগুলো চেক করুন:
dependencies: {
@hotwired/turbo-rails: ^7.0.0-beta.5,
@rails/actioncable: ^6.0.0,
@rails/activestorage: ^6.0.0,
@rails/ujs: ^6.0.0,
@rails/webpacker: 4.3.0,
stimulus: ^2.0.0
}
আমাদের মডেল জেনারেট করা
সব ফাইল চেক করার পর, আমরা আমাদের views, controllers, models এবং migrations জেনারেট করব posts টেবিলের জন্য body, এবং likes কলামসহ। এটি করতে, টার্মিনালে নিচের কমান্ড রান করুন:
rails g scaffold posts body:text likes:integer
আমাদের ডেটাবেস তৈরি করা
এখন যেহেতু সব কিছু জেনারেট হয়েছে, আমাদের এই চেঞ্জগুলো ডেটাবেসে পাঠাতে হবে, তাই টার্মিনালে নিচের কমান্ড রান করুন:
rails db:create db:migrate
যদি সব ঠিক থাকে, আমরা সার্ভার রান করতে পারি (rails server) এবং চেক করতে পারি সব কিছু ঠিক আছে কিনা। এটি করতে, আমরা সার্ভার রান করব এবং তারপর posts পেজ ভিজিট করব, যা হবে https://localhost:3000/posts:
posts ডিরেক্টরির প্রিন্টস্ক্রিন
পোস্টগুলো লিস্ট করা
এখন, আমরা পোস্টগুলো লিস্ট করব। এটি করতে, আমরা app/views/posts/_post.html.erb ফাইল তৈরি করব এবং নিচের কোডটি সেখানে যোগ করব:
<div style="background: lightgrey; width: 300px; padding: 10px;">
<%= post.body %>
<br>
<%= link_to :edit, edit_post_path(post) %>
<%= button_to "likes (#{post.likes || 0})", post_path(post, like: true), method: :put %>
</div>
<br>
ভ্যালিডেশন
আমাদের body ফিল্ড ভ্যালিডেট করতে হবে (যেটি null হতে পারবে না), এবং আমরা ব্রডকাস্টকে বলব একটি টুইট ডিসপ্লে করার জন্য প্রথম টুইটের সাথে একই স্ক্রিনে তৈরি হওয়ার পর। এটি করতে, আমরা app/models/post.rb ফাইল এডিট করব এবং নিচের কোড ইনসার্ট করব:
class Post < ApplicationRecord
validates_presence_of :body
after_create_commit { broadcast_prepend_to :posts }
end
অর্ডারিং
আমাদের পোস্টগুলো অর্ডার করতে হবে, তাই আপনার কন্ট্রোলার খুলুন (app/controllers/posts_controller.rb)।
...
def index
@posts = Post.all.order(created_at: :desc)
@post = Post.new
end
...
শেষ করা
এখন, আমাদের index এডিট করি (app/views/posts/index.html.erb) new post পেজ এবং সব posts লিস্ট দেখাতে।
<%= turbo_stream_from :posts %>
<%= turbo_frame_tag :post_form do %>
<%= render 'posts/form', post: @post %>
<% end %>
<%= turbo_frame_tag :posts do %>
<%= render @posts %>
<% end %>
ইন্ডেক্সে রিডাইরেক্ট করা
শেষে, আমাদের কন্ট্রোলারের create মেথড মডিফাই করি। show post পেজে রিডাইরেক্ট করার বদলে, সব কিছু একই পেজে রাখি।
...
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to posts_path }
format.json { render :show, status: :created, location: @post }
else
format.turbo_stream { render turbo_stream: turbo_stream.replace(@post, partial: 'posts/form', locals: { post: @post }) }
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
...
আমাদের ফাইনাল পেজটি এরকম দেখাবে:
ফাইনাল পেজের প্রিন্টস্ক্রিন
লগ চেক করা
যখন একটি পোস্ট তৈরি হয়, আপনার টার্মিনাল লগ চেক করুন; সেগুলো এরকম দেখাবে:
[ActionCable] Broadcasting to posts: "<turbo-stream action=\"prepend\" target=\"posts\"><template><div style=\"background: lightgrey; width: 300px; padding: 10px;\">\n first post\n <br>\n <a href=\"/posts/1/edit\">edit</a>\n <form class=\"button_to\" method=\"post\" action=\"/posts/4?like=true\"><input type=\"hidden\" name=\"_method\" value=\"put\" /><input type=\"submit\" value=\"likes (0)\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"<token>==\" /></form>\n</div>\n<br>\n</template></turbo-stream>"
চিন্তা
এর মানে ActionCable turbo stream-র সাথে কাজ করছে সুপার ফাস্ট পারফরম্যান্সের সাথে, আপনি দেখতে পাচ্ছেন: Completed 302 Found in 18ms।
ডেপ্লয় করা
আমাদের Rails সার্ভারের পাশে আরেকটি এক্সিকিউটেবল রান করতে হবে কি?
না, কারণ ActionCable জনপ্রিয় সার্ভারগুলোর সাথে ফাইন কাজ করে, যেমন Unicorn, Puma, এবং Passenger।
Heroku Hotwire সাপোর্ট করে কি?
হ্যাঁ, Heroku Hotwire-এর বেস (websockets) সাপোর্ট করে। আপনি যদি আরও প্রশ্ন থাকে, আপনি Heroku-এর অফিশিয়াল ডকুমেন্টেশন চেক করতে পারেন।
আপনি যদি চিন্তা করছেন কেন আমরা Redis ব্যবহার করছি, তার এক আরো কারণ আছে
যদি Heroku Redis ছাড়া ব্যবহার করা হয়, মেসেজ সবার কাছে পাঠানো হবে না যখন আপনার অ্যাপ একক ডাইনো থেকে বেশি স্কেল করা হয়। বর্তমান অ্যাপের স্টেটলেস প্রকৃতির কারণে, প্রথম ডাইনোতে কানেক্টেড ক্লায়েন্টরা দ্বিতীয় ডাইনোতে কানেক্টেড ক্লায়েন্টদের দ্বারা পাঠানো মেসেজ পাবে না। Redis এই সমস্যাটি সমাধান করে মেসেজ স্টেট গ্লোবাল স্টোরেজে স্টোর করে।
Heroku-এ Redis সেট আপ
আপনার রিমোট Redis ইনস্ট্যান্সের সাথে একটি Redis CLI সেশন স্থাপন করতে, heroku redis:cli ব্যবহার করুন। আপনি যদি কোনো ইনস্ট্যান্স স্পেসিফাই না করেন, তাহলে REDIS_URL-এ অবস্থিত ইনস্ট্যান্সটি ডিফল্টলি ব্যবহার করা হবে। আপনার একাধিক ইনস্ট্যান্স থাকলে, সে ইনস্ট্যান্স স্পেসিফাই করুন যেটিতে আপনি কানেক্ট করতে চান।
এটি অ্যাপ্লিকেশন সার্ভারে কীভাবে কাজ করে?
আমাদের প্রতিটি ডাইনোকে Redis pub-sub সিস্টেম ব্যবহার করতে সেট আপ করতে হবে। সব ডাইনো একই চ্যানেলে (ডিফল্ট) সাবস্ক্রাইব করবে এবং মেসেজের জন্য ওপেক্ষা করবে। যখন প্রতিটি সার্ভার একটি মেসেজ পাবে, এটি কানেক্টেড ক্লায়েন্টদের কাছে পাবলিশ করতে পারবে। আমরা subscribe একটি আলাদা থ্রেডে কভার করব, কারণ subscribe একটি ব্লকিং ফাংশন, তাই মেসেজের জন্য ওপেক্ষা করা এক্সিকিউশন ফ্লো থামিয়ে দেবে। এর পাশাপাশি, একটি সেকেন্ড Redis কানেকশন দরকার যেহেতু একবার একটি সাবস্ক্রাইব কমান্ড একটি কানেকশনে করা হলে, কানেকশনটি শুধু আনসাবস্ক্রাইব বা মেসেজ রিসিভ করতে পারবে। স্কেলিং সম্পর্কে আরও জানুন এখানে।
সিকিউরিটি
বর্তমানে, অ্যাপ্লিকেশন এক্সপোজড এবং অনেক আক্রমণে আহত। অনুগ্রহ করে WSS সেট আপ করুন এবং আপনার ইনপুট স্যানিটাইজ করুন। WebSocket সিকিউরিটি সম্পর্কে আরও জানুন এখানে।
উপসংহার
Hotwire ব্যবহার করা সম্ভব একটি নতুন অ্যাপ্লিকেশনের মাধ্যমে, যেন আমরা এখানে দেখিয়েছি, কিন্তু Turbolinks- থেকে আপডেট করা ও সম্ভব।
এই আর্টিকেলটি আপনাকে বুঝতে সাহায্য করা উচিত "ম্যাজিক ফ্রেমওয়ার্ক" কীভাবে কাজ করে। এতে, আপনার সম্ভাবনা ছিল প্র্যাকটিসে, একটি ছোট সিম্পল অ্যাপ্লিকেশন ডেভেলপ করার যা অ্যাগ্রিগেটেড turbo stream ভিয়া রিকোয়েস্ট শো করে ম্যানিপুলেশন এবং নেভিগেশন WebSocket ভিয়া।