# Terminal
rails action_text:install
rails g scaffold posts title
rails g model comment post:belongs_to session
rails g controller comments
# app/models/post.rb
class Post < ApplicationRecord
has_many :comments, dependent: :destroy
has_rich_text :content
has_one_attached :poster
broadcasts_refreshes
end
# app/views/posts/_form.html.erb
<div class="mb-3">
<%= form.label :poster, class: 'form-label' %>
<%= form.file_field :poster, class: 'form-control' %>
</div>
<div class="mb-3">
<%= form.label :content, class: 'form-label' %>
<%= form.rich_text_area :content, class: 'form-control' %>
</div>
# app/controllers/posts_controller.rb
def post_params
params.require(:post).permit(:title, :poster, :content)
end
# app/views/posts/show.html.erb
<%= turbo_stream_from @post %>
<%= render @post %>
<h2>Comments</h2>
<div id="comments">
<%= render partial: "comments/comment", collection: @post.comments, locals: { post: @post } %>
</div>
<div data-turbo-permanent>
<%= render partial: "comments/new", locals: { post: @post, comment: @post.comments.new } %>
</div>
# app/views/posts/_post.html.erb
<div id="<%= dom_id post %>" class="scaffold_record position-relative">
<% if post.poster.attached? %>
<%= image_tag post.poster.variant(resize_to_fill: [1600, 300]), class: "img-fluid" %>
<% end %>
<h1 class="position-absolute top-50 start-50 translate-middle text-white text-center">
<%= post.title %>
</h1>
</div>
<%= post.content %>
# config/routes.rb
resources :posts do
resources :comments, only: [:create, :destroy]
end
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :set_post
def create
@comment = @post.comments.new(comment_params)
@comment.session = session.id
@comment.save
# redirect_to @post
# render turbo_stream: [
# turbo_stream.replace(
# "new_comment",
# partial: "comments/new",
# locals: { post: @post, comment: Comment.new }
# ),
# turbo_stream.append(
# "comments",
# partial: "comments/comment",
# locals: { post: @post, comment: @comment }
# )
# ]
end
def destroy
@comment = @post.comments.find(params[:id])
@comment.destroy
redirect_to @post
end
private
def set_post
@post = Post.find(params[:post_id])
end
def comment_params
params.require(:comment).permit(:content)
end
end
# app/views/comments/_new.html.erb
<%= form_with model: [post, comment], id: :new_comment do |f| %>
<%= f.rich_text_area :content %>
<%= f.submit class: "mt-3 btn btn-primary" %>
<% end %>
# app/views/comments/_comment.html.erb
<%= content_tag :div, id: dom_id(comment), class: "card mb-3" do %>
<div class="card-body">
<p class="card-text"><%= comment.content %></p>
<p class="card-text">
<small class="text-muted">
Posted <%= time_ago_in_words comment.created_at %> ago |
Posted by <%= comment.session %>
<% if session.id.to_s == comment.session %>
<%= link_to "Delete", [post, comment], "data-turbo-method": :delete %>
<% end %>
</small>
</p>
</div>
<% end %>
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
has_rich_text :content
# broadcasts_to :post
broadcasts_refreshes_to :post
end
# Gemfile
gem "turbo-rails", "2.0.0-beta.2"
# package.json
"@hotwired/turbo-rails": "^8.0.0-beta.2",
# app/views/layouts/application.html.erb
<%# turbo_refreshes_with method: :replace, scroll: :reset %>
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
<%= yield :head %>
# app/views/comments/create.turbo_stream.erb
<%= turbo_stream.replace(
"new_comment",
partial: "comments/new",
locals: { post: @post, comment: Comment.new }) %>
<%= turbo_stream.append(
"comments",
partial: "comments/comment",
locals: { post: @post, comment: @comment }) %>