Hướng dẫn tạo chức năng comment bằng Gem private_pub
Bài đăng này đã không được cập nhật trong 4 năm
1.Giới thiệu
Trong bài viết này tôi sẽ hướng dẫn tạo 1 web đơn giản với chức năng tạo status và comment. Sử dụng gem private_pub để có thể hiển thị comment khi có comment mới của mình hoặc của người khác comment từ nơi khác mà không cần load lại trang.
Trước khi làm những hướng dẫn dưới đây, bận cần làm trước việc tạo user và đăng ký, đăng nhập bằng devise. Có thể xem hướng dẫn tại: https://github.com/plataformatec/devise
2.Cài đặt Gem
Thêm vào Gemfile
gem “private_pub”
gem “thin”
Chạy
$ bundle install
Sau đó chạy lệnh
$ rails g private_pub:install
để tạo file init cho private_pub
3.Tạo model
Tạo model Status
$ ails generate model Status content:text user:references
Tạo model StatusComment
$ rails generate model StatusComment content:text user:references status:references
Thêm và sửa các file model:
File user.rb
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :statuses, dependent: :destroy
  has_many :status_comments, dependent: :destroy
end
File status.rb
class Status < ActiveRecord::Base
  belongs_to :user
  has_many :status_comments, dependent: :destroy
  accepts_nested_attributes_for :status_comments, allow_destroy: :true
  default_scope -> {order(created_at: :desc)}
  validates :user_id, presence: true
  validates :content, presence: true, length: {maximum: 2000}
end
File status_comment.rb
class StatusComment < ActiveRecord::Base
  belongs_to :user
  belongs_to :status
  validates :user_id, presence: true
  validates :content, presence: true, length: {maximum: 140}
end
4. Tạo controller và view
File config/routes.rb
Rails.application.routes.draw do
  root "statuses#index"
  devise_for :users
  devise_scope :user do
    get "sign_out", to: "devise/sessions#destroy"
    get "sign_in", to: "devise/sessions#new"
    get "sign_up", to: "devise/registrations#new"
  end
  resources :statuses
  resources :status_comments
end
File statuses_controller.rb
class StatusesController < ApplicationController
  respond_to :html, :json
  def index
    @statuses = Status.all
    @status = Status.new
  end
  def create
    @status = current_user.statuses.build status_params
    if @status.save
      flash[:success] = "Status created!"
      redirect_to root_url
    else
      @feed_items = []
      render "static_pages/home"
    end
  end
  def destroy
    @status.destroy
    flash[:success] = "Status deleted"
    redirect_to request.referrer || root_url
  end
  private
  def status_params
    params.require(:status).permit :content
  end
end
status_comments_controller.rb
class StatusCommentsController < ApplicationController
  respond_to :html, :json
  def create
    @status_comment = StatusComment.create! status_comment_params
    respond_to do |format|
      format.html { }
      format.js
    end
  end
  def destroy
    @status_comment = StatusComment.find params[:id]
    @status_comment.destroy
    redirect_to :back
  end
  private
  def status_comment_params
    params.require(:status_comment).permit :status_id, :user_id, :content
  end
end
Trang index của status index.html.erb
User: <%= current_user.name %>
<%= link_to "Sign out", sign_out_path %></br>
----------------------------------------------------------
</br>
<%= form_for @status, html: {multipart: true} do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new status...", class: "form-control" %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
<%= render @statuses %>
File views/statuses/_status.html.erb
<li id="<%= status.id %>">
  <span class="user">
    <%= status.user.name %>
  </span>
  <br>
  <span class="timestamp text-success">
    <%= "Posted #{time_ago_in_words(status.created_at)} ago." %>
    <% if current_user == status.user %>
      <%= link_to "Delete", status, method: :delete, data: {confirm: "You sure?"} %>
    <% end %>
  </span></br>
  <span class="content">
    <%= status.content %></br>
  </span></br>
  Comments:</br>
  <% @status_comments = status.status_comments %>
  <span id="status-comment<%= status.id %>">
    <%= render @status_comments %>
  </span>
  <span id="form-status">
    <%= form_for StatusComment.new , remote: true do |f| %>
      <%= render 'shared/error_messages', object: f.object %>
        <div class="field">
          <%= f.hidden_field :status_id, value: status.id %>
          <%= f.hidden_field :user_id, value: current_user.id %>
          <%= f.text_area :content, placeholder: "Compose new comment...", class: "form-control" %>
        </div>
      <%= f.submit "Comment", class: "btn btn-primary" %>
    <% end %>
  </span>
</li>
</br>
File views/status_comments/_status_comment.html.erb
<li>
  <% if status_comment.id %>
    <%= status_comment.user.name %>:
    <%= status_comment.content %></br>
    <%= time_ago_in_words(status_comment.created_at) %>
    <%= link_to "Delete", status_comment_path(status_comment), method: :delete, data: {confirm: "You sure?"} if current_user == status_comment.user%>
    </br></br>
  <% end %>
</li>
4.Sử dụng private_pub
Thêm vào file application.js
//= require private_pub
Thêm file private_pub.js và add vào layout
<%= javascript_include_tag "private_pub" %>
Thêm vào file config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( private_pub.js )
Chạy Faye.
$ rackup private_pub.ru -s thin -E production
Dùng subscribe_to trong trang mà minh muốn bắt thay đổi để subscribe một kênh.
<%= subscribe_to "/comments/new" %>
Dùng publish_to để gửi JS đến kênh đó. Ta sẽ viết trong file create.js.erb và để trong thư mục views/status_comments/
<% publish_to "/comments/new" do %>
  $("#status-comment<%= @status_comment.status_id %>").append("<%= j render(@status_comment) %>");
<% end %>
Thêm
$("#form-status form textarea").val("");
để xóa text khi đã comment xong.
Như vậy ta đã hoàn thành. Chạy rails s và thử kết quả.

Ứng dụng chạy thử trên heroku: https://protected-plateau-5161.herokuapp.com/
5.Tài liệu tham khảo
All rights reserved