Ruby on Rails - Building a Reddit-like Commenting System with Rails - ruby on rails tutorial - rails guides - rails tutorial - ruby rails
What is Reddit?
- Reddit is a website in which a community of registered users (redditors) submits content.
- Its format resembles a traditional bulletin board system allowing users to post messages and links to other websites and comment on each other's posts.
- Entries are ranked by a voting system; other users can vote comments and posts either up (upvoted) or down.
- Users who post and comment receive "karma" for upvotes and lose karma for downvotes.
Approach:
- Massive platforms like YouTube and reddit are driven by their threaded commenting features.
- Reddit as the starting point for this tutorial. Assume your project involves someone submitting a story link to share on the site.
- Other users can then comment directly on that story, or comment on another user’s comment.
- To build this, you could also use one model, with one column for “story_id” and one for “comment_id” (meaning one column would always be nil). Or you could use polymorphic associations.
- With a polymorphic association, we’ll create one Comments model. There will then be one column for “commentable_id”, which will store the ID of the object we’re commenting on, and then “commentable_type”, which will indicate what type of object we’re commenting on (in this case, a Story or a Comment).
Models:
- we’ll be starting with a brand-new Rails app (using Rails 4). We’re only going to add what we need to demonstrate this feature, so for starters, let’s create the stories model.
- This is what users submit to our Reddit-like site, so it’s just a Title and URL (that the title would link out to).
- So now we have the objects that make the core of our user experience.
- We decided to use “text” for the title, so we’re not limited by length (string is limited to 255 characters). Now let’s add the model for comments.
- Now that we have these two models created, we need to create the associations between them.
- The easiest place to start is with Stories. A story can have many comments, so we need to add that.
- However, Rails would normally assume that comments would include a column called “story_id” which it doesn’t so we need include the name we gave to the polymorphic association:
app/models/story.rb
apps/models.comment.rb
- So, to recap, a Story can have many comments. A Comment can have many comments.
- And since a comment can belong to more than one model, we specify that a comment belongs to a polymorphic association through commentable.
- Now let’s see how this works. Fire up your trusty Rails console:
- Since we have no entries in our database for our Reddit-like site, let’s create one:
- You should then see this entry be created:
- We then want to add a comment to that story, to make sure our associations work properly.
- Let’s enter it through the comments association with the story, to mimic if we were to leave a comment on a story on the story’s show page.
- We can just use Story.first since it’s the only entry in our database.
And we should see the following:
- You’ll see that Rails knew that the commentable_type was Story, since it was a comment on a story, and that it used the story_id (1, since this was our first entry) as the commentable_id.
- Now let’s add a comment to that first comment, creating our first threaded/nested comment.
- We could do it the same way:
- And we should see the following:
Routes:
- As the traffic cop for Rails, we have to tell our routes.rb what to do with web requests.
- First we’ll tell the app to use the index page for stories as the root for this site. Then we’ll add the resources for stories and comments.
- This will create all of the default actions (index, show, new, edit, create, update and destroy) for each controller.
- But to each, we’ll also add a nested resource, to nest those actions for comments within each set of routes. So it looks like this:
config/routes.rb
- Let’s see if everything is routing correctly. Since we didn’t limit the default routes, we’ll see more than we need (for instance, we won’t have a comments Index page), but this will help you see how it’s all nested. In the Terminal, check your routes with:
- This is what you should see:
- Success again! You’ll see there are stories, there are comments, then there are comments nested within stories, and comments nested within comments.
- Now that the requests are going where they need to go, let’s make sure we’re providing the proper response for each.
Controllers:
- As we’re dealing with stories and comments, we’ll need to create a controller for each. We’ll start with stories.
- For the sake of brevity in this tutorial, we’re going to skip creating, editing, or deleting stories, since we have at least one to already work with.
- So we’ll just handle the index and show requests, so that we can view the story we created. Notice that we don’t have to address comments at all here.
app/controllers/stories_controller.rb
- Now we move onto the comments controller. Here we don’t need index and show, as comments always live on story view pages, and don’t have their own pages.
- But we’ll need new and create, because we want to create new comments.
- We also need to create a method to let Rails know if we’re creating a comment for a story or for a comment.
app/controllers/comments_controller.rb
- Since our comments are nested within other comments or stories, we’re using the instance variable @commentable in the create action.
- We have a private method (find_commentable) that is telling Rails that if the params contains a comment_id, it’s a comment on a comment, and if it has story_id, it’s a comment on a story.
- We then added a filter at the top of the controller, telling Rails to run the private method before performing any other action (otherwise it wouldn’t know what @commentable was when it got to the create action).
- Lastly, we need to create the views to see all this magic we created work.
Views:
- There are four elements we need for all of this to work.
- We need a show page for a story, and index page for stories, a way to see comments, and a way to post comments.
- Let’s start with the index page and show page.
- The index page will display all of the stories in our database, along with a link to the show page for each story.
app/views/stories/index.html.erb:
- Now we need the show page for each story. Inspired by a show page on reddit, this page will have details about the story, a form for submitting a comment about the story, the display of each comment, and the ability to comment on a comment.
app/views/stories/show.html.erb
- You’ll see that we broke out some elements into a partial. So, we need to add that view file.
apps/views/comments/_comment.html.erb
- Because we’re passing this partial the collection of comments, it displays each comment and the form for replying to that comment.
- But then it renders itself within itself (recursive!), to display the replies each comment might have.
- And by using the ul/li structure, we’re making sure they all nest correctly when they display. Fancy.
See Our Handiwork in Action:
- Fire up the server, and take a look at what we did.
- Because we created a story, a comment, and a reply already, so you should see them all.
- Then play around with adding a new comment on that story, and then a reply to that comment.
- If you add the new and create actions and new view to Stories, you will basically have the core functionality of reddit built.