Rails App
Phase Ten - Adding User Comments
One of the things you want to be able to do is engage your users with your application. For this application, allowing people to comment on a transcription can go a long way in helping you engage your audience. This also shows off how to associate a model with another (a very common task in web development).
Comments
A comment needs to be associated with a particular transcription, and a user. In Rails, these associations are handled at the application layer. If you’ve done any database development before, you may have handled this at the database layer.
Scaffold
We’ll start by getting rails to create a scaffold for the comment, and add a
placeholder to reference the Transcription model (transcription_id
).
$ bin/rails generate scaffold Comment user_name:string body:text transcription:references
This generates the new files needed and updates most of the files for this to work properly. The next step is to tell the database about the update
$ rake db:migrate
== 20140730172249 CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0052s
== 20140730172249 CreateComments: migrated (0.0053s) ==========================
Model Relations
The association between a Transcription
object and a Comment
object is that
a Transcription
has many Comments
. We define this association in the
model. Open app/models/transcription.rb
. Add the line has_many :comments
to
the file.
class Transcription < ActiveRecord::Base
mount_uploader :picture, PictureUploader
has_many :comments
end
We also want to make sure that the Comment
model properly belongs to to
the Transcription
object. Open app/models/comment.rb
and make sure it looks
like this:
class Comment < ActiveRecord::Base
belongs_to :transcription
validates :user_name, presence: true, length: { minimum: 2 }
end
Render Comments
We need a form for adding comments, as well as updating the Transcription
controller to know how to save and display a comment. Open
app/views/transcriptions/show.html.erb
.
<div class="row">
<div class="col-md-12">
<h3 class="page-header">Comments</h3>
</div>
</div>
<% @comments.each do |comment| %>
<div class="row">
<div class="md-col-10 col-md-offset-1">
<div class="comment">
<p><strong><%= comment.user_name %></strong></p>
<%= simple_format comment.body %>
</div>
</div>
</div>
<% end %>
<%= render 'comments/form' %>
We also need to tell the Transcription
controller about the comment. Open
app/controllers/transcriptions_controller.rb
and add find the show
method.
Update the method to read like this:
def show
@comments = @transcription.comments.all
@comment = @transcription.comments.build
end
Now open the comment form (app/views/comments/_form.html.erb
) and update the
styles:
<div class="row">
<div class="md-col-12">
<h2 class="page-header">New Comment</h2>
</div>
</div>
<div class="row">
<div class="md-col-4 col-md-offset-1">
<%= form_for(@comment) do |f| %>
<% if @comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@comment.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% @comment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :transcription_id %>
<div class="form-group">
<%= f.label :user_name %><br>
<%= f.text_field :user_name, class: "form-control", placeholder: "Your Name" %>
</div>
<div class="form-group">
<%= f.label :body %><br>
<%= f.text_area :body, class: "form-control", placeholder: "Comment", rows: 5 %>
</div>
<div class="actions">
<%= f.submit "Save", class: "btn btn-default" %>
</div>
<% end %>
</div>
</div>
Flow
Try out your newly added feature. Notice anything annoying? Yeah, when you post
a comment, the Comment
controller takes over and takes you to the index
view for the transcription. Let’s update this action so when you add a new
comment, you go back to the transcription.
Open app/controllers/comments_controller.rb
and find the create
method.
Update it to read as follows:
# POST /comments
# POST /comments.json
def create
@comment = Comment.new(comment_params)
@transcription = @comment.transcription
respond_to do |format|
if @comment.save
format.html { redirect_to transcription_path(@transcription), notice: 'Comment was successfully created.' }
else
format.html { render :new }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
end
Git
Don’t forget to add the new files and commit them in git
, and also push them
to github!
$ git add .
$ git commit -am "Implements comments on the Transcription"
$ git push origin master
Summary
There’s a lot of code here, but they’re mainly updates to code that Rails generated for you. Most of the “work” here is actually going to the Bootstrap documentation to find and add the appropriate HTML markup and CSS classes to produce the desired layout. This exercise did get in to some of the specifics of creating associated models in Rails that encapsulates different ideas that are hopefully easy to reason about. You can now build out many more features using these techniques.