Today, we will be building a simple forum using Ruby on Rails, and we will be working up from the basics covering things like authentication and more advanced database techniques.

Ruby on Rails

If you're unfamiliar with Ruby on Rails, it is a web application framework built using the Ruby programming language. There have been some good tutorials published here on net.tuts+ on getting starting with Rails, and for a quick review you can see my previous article.

Step 1 - Create the Rails Application

To create our application, we use the rails command, followed by the name of our project, and that happens to be forum in this case. The terminal command below is what I used. Also, make sure you have changed into the proper directory in your terminal.

After running the command you will see a long line of text with all the files that have just been created. Now you will want to change into the directory we just created.

Step 2 - Installing Gems

You are probably wondering what a Gem is. A Gem is a self-contained package for distributing Ruby programs and libraries. They come in handy very often, they make your life easier. We will be using a few gems for this project, listed below. Also, make sure that you have the latest version of RubyGems and your Gems by running:

(with sudo if needed)

(with sudo if needed)

nifty-generators: Ryan Bates's amazing nifty-generators will be used for the nifty-authentication and nifty-scaffold generators

Ryan Bates's amazing nifty-generators will be used for the nifty-authentication and nifty-scaffold generators database: Make sure you have any gem you need to interface with the database you will use, I am going to use SQLite

Step 3 - Setting up the Authentication

Now that we have all the gems we need installed, run the following in your terminal: (make sure you are in the directory rails created)

You should see text fly by like above. Now go ahead and open up the folder in your favorite IDE. We are first going to edit the config/database.yml file. The file should be set up to use whatever database system you want, and in this case I will be using SQLite.

Making sure everything is setup properly, and then run the following command to migrate changes to our database:

Step 4 - The Application Structure

No, this part is not going to actually deal with the application layout in terms of overall design, we will work on that shortly. This section will actually deal with how the application will be built. One of the major upsides with Ruby on Rails is the plethora of generators available. The only problem that some people miss is that you need to plan you application to make these generators really go the extra-mile.

Below is how the models in the database will be organized and related is a rough manner:

Forum: A forum is the top-most level. Basically a group of topics that relate to one another.

A forum is the top-most level. Basically a group of topics that relate to one another. Topic: a topic, also called a thread concerns a certain topic.

a topic, also called a thread concerns a certain topic.

Post: a post belongs to a topic, and is a message by a certain user.

So we are going to use nifty-scaffold to generate three models, one for each of the parts listed above. So getting started, we are going to use the scaffold to create our forum. To do this, run the following command:

Next we will scaffold for the topic:

Now we will scaffold the post:

Then migrate the database:

Step 5 - Creating the Layout

Now, we are actually going to work on the the first part of the overall look of the site. Due to the fact we are using nifty_scaffold, we are also going to use nifty_layout to generate some of the basic layout, that we will change. To use nifty_layout, run:

Now this was a real quick step just to get this working for now. We will come back to this later.

Step 6 - Setting up the Associations

So we are going to create associations in our models. Associations are ways to link models. Let's first open up our app/models/forum.rb and you should see:

Now we are going to add some code and it should look like the following, and I will explain it in a minute.

The code has_many means exactly what it sounds like; it 'has many' and then we refer to our model Topic. You may wonder why it works when you add an 's', and that is the ActiveRecord convention. The :dependent => :destroy means that when the forum is deleted, all of it's topics will be too. Now we are going to open up our app/models/topic.rb and edit it to the following:

Again, this is plain text. It says a Topic belongs to a Forum, and has many posts. We are also using :dependent => :destroy here to delete all of it's children posts. Now open up app/models/post.rb and edit it to the following:

Now we are going to create three migrations that will add our foreign keys to our database. If you had wanted to do this when you scaffolded, you could have, though the way we are going about it also teaches you another Rails technique, so I felt that this way would be better. So the create our first migration, run:

This command generates a migration named add_foreign_to_topics, automatically recognizes that we want to change the Topic model, and then we pass what we want to add. In this case we are adding a column named forum_id that is an integer type. To create the migration to add our foreign key of forum_id to our Topic model. Then next command will create the migration

Now we can migrate the database again with:

Step 7 - Getting Started on the Home Page

So now we can start on building the functionality of the site. So lets first open up config/routes.rb and add the following line near the commented out lines below:

Lets save the file and delete or rename the file public/index.html. Now we want to open up the view for the index file in app/views/forums/index.html.erb and edit it to the following:

I changed the table header text into the text that we will be putting in each cell, as well as the width of the cells There are still changes that we are going to make later. Now, open up the application.css file in the public/stylesheets directory. I added the 960.gs CSS Reset and Typography files to the top of the stylesheet. Then I added the following styles to the end of the file:

Now if you want to fire up the server with script/server, you should see the following: (P.S. I added a record into the database just to give an example.)

Now that we have our index action taken care of, lets work on our show action for the forum. This file lies in app/views/forums/show.html.erb. We are going to copy most of the code from our index action into this one, and the final code is below:

I know I have not explained the code in the two views above, so I'll explain it now. So the first line we set the title for the page. We then create a table and set up the table headers. After, we start a loop for each item in the topics of our forum, and uses the local variable of topic. We then create a link for each topic, then the number of posts, minus 1 to account for the original post. Then we have an extra td that we will use later, and then the admin links, which we will also take care of later.

Step 8 - Working on the Forms

So now that we have the basic layout for our front end. We need to work on the form to create a topic. You should have something that looks like the following:

Well this currently does not work for what we want to do. We currently do not create a post when we create a topic, and we currently give users access to options they should not see. So open up app/views/topics/_form.html.erb. Your current file should look like:

So we are going to delete the parts with the last_poster_id and last_post_at. We are also going to add in a text area for the post and some hidden fields. You will see that I do not use the Rails helpers, and that is just my personal preference. Your final form should look like:

And it should look like this in your browser:

We will be saving these in our controller. So, save the view and open up the app/controllers/topic_controller.rb and find the create action. Edit it so it looks like:

We have added in code to handle creating the post that goes with the topic. Now to make this easier we are going to delete the index action in our controller. We are also going to update the update and destroy actions to redirect properly, so change the redirect_to on these two actions to:

Now go ahead and open up app/controllers/posts_controller.rb and delete the index and show actions.

Step 9 - Setting up More Associations

If you noticed that I skipped two associations above, I applaud you on noticing. The associations we skipped were between users and their posts and topics. To do this lets first create two migrations and migrate the changes to the database:

Now, to tell ActiveRecord about these associations, open up app/models/user.rb and add the following lines under before_save.

Now open up both app/models/post.rb and app/models/topic.rb and add the following lines:

Now we have the extra associations to continue.

Step 10 - Polishing up the Forums List

So now that have have the basics now, we are going to continue on and polish up the forums listing, after we will continue on and polish the topics list.

Let's first open up app/models/forum.rb. We are going to add a new method to help us find the most recent post in a forum. The method that we are going to add is below:

This method finds the first Topic that has a forum ID of whatever the ID of the forum we called this method upon, and then sorts it by date and returns the value.

So open up the app/views/forums/index.html.erb and find the table cell inside of the loop that has a class of right. We are going to change it to the following:

This code may look confusing, so I will go through it. First we reference to the method we added to our model, and see if it returned a record, because we may have a forum without any topics. Next, if we have a record, we use the date helpers Rails provides to parse the time into readable text. If we don't have a record returned, we then output 'no posts.' Your page should look like:

Now that we have added the date, we are going to add in the link to the user who posted. Back in the view, change that table cell to look like the following:

Your page should now look like:

Step 11 - Polish the Topic List

So now we are going to head back to our app/views/forums/show.html.erb file. The title of this step may be a bit confusing due to the fact that we are editing the forum's show action, but actually working on the Topics list. Now that you have the file open, find the table cell inside the look with the class of 'right' again. We are going to be using the same date helper, and our code will look like:

Now if you open up your browser, you should see:

Step 12 - Working on the Topic View

So now that we have everything in the Forum and Topic lists sorted out, we are going to work on the third major view. Go ahead and open up app/views/topics/show.html.erb and edit it so it looks more like:

Let's also add in some styling:

And now viewing a topic will look like this:

Step 13 - User Show Action

When a username is clicked on, we want to show their profile, correct? So to do this open up the app/controllers/users_controller.rb and and the show action below:

That is all we will need for this action in our controller. Now create a file named show.html.erb in app/views/users, and place the following inside:

That simple amount of code will display the user's topics. No, there is not a limit here so we would technically show all of the topics by that user. The most ideal solution would be to create methods to handle this in our User model, but I will not do that here.

Step 14 - Working With Posts

So now that we are almost done, we need to work on the functions to edit a post. Let's first open up app/controllers/topics_controller.rb and our view for the show action. First, on the controller, we are going to delete the edit and update functionality. You may wonder why, and the reason is that we are not going to allow a user to edit the topic, but rather posts. Yes, this does not allow a user to change the title of a topic but that is better than writing the extra logic required to edit a topic. So go ahead and delete those actions, and you can also delete the corresponding views.

Now open up the app/views/topics/show.html.erb file. First, in the paragraph tag at the bottom, delete the Edit link, the first one in the list. Then go up to the loop, and before the link to delete the post, we are going to add in a link to edit the post. The view when we are done should look like:

We also added a reply link for functionality we will add in a minute. Now, when you click on the link to edit, it should work. Then if you try and change the post, Rails gives you an error. Well, we don't want to have it redirecting where it is currently, so open up app/controllers/posts_controller.rb, scroll down to the create action, and find the line "redirect_to @post." We are going to change the line so the final action looks like:

So that code will redirect us now to the show action for the forum id of the post. We only have one problem though, the create action is not passed the topic_id in the view, so open up app/views/posts/_form.html.erb and add the following line under the error messages.

So now that we also need to update the Topic that we have a new post. So to do this, we are going to again change the create action to look like:

What we did was after the post was saved, we used the topic_id to find our topic, and then update two columns in our database. Go ahead and copy those two lines that we added into the same place on our update action, so it looks like:

We also changed the redirect on this one so that it does the same as the create action. Now go down to the destroy action, and change the redirect to:

Step 15 - Putting the Final Touches on the Forum

Now, we need to do some quick cleaning up in our views for links that are unnecessary or no longer work. First up, the show action for the forums, so open up app/views/forums/show.html.erb and delete the show and edit links so that the view looks like:

We also need to update the Topic creation method so that it adds in the user_id of our current user, so open up app/controllers/topics_controller.rb and change the Topic.new line to:

We also need to clean up the app/views/forums/index.html.erb file, so go ahead and open it up and delete the show link so the file looks like:

Step 16 - Administrative Functions

Currently, any user can edit, delete, and create anything they want. So we are going to open up our Application helper in app/helpers/application_helper.rb. We are going the add in a few extra methods to help us deal with authentication and user levels. Before the 'end' tag, add these methods:

These methods allow us is find out if a user is an admin, and owner by passing in the user_id of what we are looking for, and one that handles both. The method checking for an admin also allows the first user to be an admin automatically. So we are first going to open up the Forum index.html.erb file. We are going to add in some extra code now to hide the create, edit, and destroy forum links from anyone but admins. We are going to wrap each of these links with <% if admin? %> and <% end %>. The final code will look like:

Now if you try and reload the page, you will receive and error saying "undefined method `permission_level'." The error is telling you that there is no column named permission_level. We need to create a migration, so run the following commands to create and migrate the changes:

Now, everything should work properly. Now, we are going to move onto the show action, so open up app/views/forums/show.html.erb, and edit it to look like the following:

What we did was make the destroy action admin only, and the create topic action logged in only. Now, we are going to work on the Topic show action, the file being app/views/topics/show.html.erb. Open it up and edit it so it looks like:

We again just implemented some methods so that unregistered users and users that do not have enough permissions are unalbe to see some links. Now that we have the links locked down, we are going to lock down the controllers, so first open up app/controllers/forum_controller.rb and at the op right under the class declaration, add:

So, now if you try your application, you will notice that Rails will give us an error, and that is because he have not defined admin_required. To add this, we are going to define it in our application controller, so open it up and add the following method right before the last end tag:

This says that unless we are an admin, redirect to the home page. We now are going to continue to our topics controller, just like with the forums contoller, we are going to add some before filters, and this time, add:

So what we are saying is that you must be logged in for every action except index and show, and you must be an admin to access the destroy action. Next, we are going to work on our posts controller. This one is going to be a tad more work. First, add the before filter:

Next, we are going to add in an extra method to our application_controller.rb file to handle this. Here is the method we are going to add:

This method checks to see if the user_id passed to it equals our current user's id, or if our current user is an admin. Back to our posts controller, and edit the actions of edit, update, and destroy so they look like:

Now that we have the authentication done, we have one last step.

Step 17 - Finishing the Application Layout

So now we are going to add a header to our application layout file. Go ahead an open up app/views/layouts/application.html.erb and add the following right after the body tag:

Now open up our CSS file and add the following line:

Opening up your web browser, you should see something like the following:

Conclusion

I hope you enjoyed this tutorial, and if you made your way through this monstrosity, I commend you! If you are looking for a forum, this wouldn't be a bad start, but Beast (altered_beast in this case) is a great forum with a really nice and smooth design. Below is a screen with stats about our program, and it took 276 lines of code (excluding views) to build this forum!

Follow us on Twitter, or subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.



