Controllers Introduction
Controllers are what are accessed first when a user goes to our site. These are instructions for the application and are somewhat of a staging area for the output, or the view, of the site. In our controller we call methods on our model classes in order to collect data to present in the view.
We already have two controllers generated for us, SessionController and UsersController, and these deal with the registration, logging in and logging out of users on our site. We’re not going to touch these. Instead, we’re going to generate our own controller called ForumsController. To do this, we use the <span class=’term’>script/generate</span> command like we did to create the models: <span class=’term’>script/generate controller forums index show</span>. Using this command we’ve generated the forums controller (note that the controller name is pluralized, where the model is singular) and two actions within it, index and show. We could’ve used script/generate resource to generate everything for us, but I feel this adds more cruft than what we’re interested in for this guide.
The way this command has been called is that we specified what we wanted generated, a controller, and then the name of the controller, which is always pluralized for restful controllers, and then the two actions after it. These two actions are from the seven restful actions which are the base for restful routing, but don’t necessarily need to be the only seven actions in your controller and you may add more if there is a need for it. The actions are as follows:
<h3>The Seven</h3>
<ul>
<li><b>index [GET]</b>: Where we initialise the variable containing all our forums that we want to display on the main page.</li>
<li><b>show [GET]</b>: Where we initialize a single forum object as a variable and used for viewing a single forum.</li>
<li><b>new [GET]</b>: Provides an interface for creating a new forum, sends its form data to the create action.</li>
<li><b>create [POST]</b>: Parses the form data from the new action, creating a new forum.</li>
<li><b>edit [GET]</b>: Provides an interface for editing an existing forum, sends its form data to the update action.</li>
<li><b>update [PUT]</b>: Parses the form data provided from the update action </li>
<li><b>destroy [DELETE]</b>: A place to delete an existing forum.</li>
</ul>
The words in square brackets next to our action name are the HTTP methods that are used when we’re accessing those actions. Usually HTTP has two methods, GET and POST. Rails has added two more, PUT and DELETE. PUT is used when you’re updating an object, such as the update action and DELETE is used when you want to delete an object, like in the destroy action.
The reason why we haven’t put the last 5 actions in that list in our forums controller is because the forums controller we’ve just generated is for the users of our site only. For the administrative actions we’re going to generate another controller which will deal with these last five actions. To do this, type <span class=’term’>script/generate controller admin/forums new create edit update destroy</span>. The admin/ prefix to our forums name, means that we want this controller to be namespaced. This command will create a new folder called <em>admin</em> in <em>app/controllers</em> in which it will place another <em>forums_controller.rb</em>. If you look in this file you’ll see that the class is actually <span class=’term’>Admin::ForumsController</span>, showing that it is namespaced, where in the one in the root controllers directory is just ForumsController.
We’ll now define the code within our ForumsController. Open up the projects view in Netbeans (by default it’s the window in the top left and already opened) by clicking on Window and then Projects, or pressing [shortcut key]. Open up your project folder, <em>forum</em>, and then click on Controllers and double click on the <em>forums_controller.rb</em> file within it. This will open up a file that looks like this:
<div class=’code’>
<b>app/controllers/forums_controller.rb</b>
<pre lang=’rails’>
class ForumsController < ApplicationController
def index
end
def show
end
end
</pre>
</div>
These are the two actions we specified. In here we’re going to call the find method on our model to collect all the forums and then just the one forum, like this:
<div class=’code’>
<b>app/controllers/forums_controller.rb</b>
<pre lang=’rails’>
class ForumsController < ApplicationController
def index
@forums = Forum.find(:all)
end
def show
@forum = Forum.find(params[:id])
end
end
</pre>
</div>
The index action now has an instance variable (defined by prefixing the variable name with an @) containing all the forums currently available on our site, none. specifying <span class=’term’>:all</span> as the only argument will gather up all the forums for us. This means when we look inside @forums we’ll see that it is just an empty array, for now. The show action calls <span class=’term’>#find</span> on <span class=’term’>Forum</span>, but specifies <span class=’term’>params[:id]</span> for the argument rather than all. <span class=’term’>params[:id]</span> is the forum we want to view’s id.
<span class=’term’>params</span> is a HashWithIndifferentAccess object that is passed to each request and because it’s a HashWithIndifferentAccess it won’t matter if you access the keys by using symbols, such as in <span class=’term’>params[:id]</span>, or as in <span class=’term’>params[”id”]</span>. It always has two keys defined in it, and those are the current controller and action, accessed by <span class=’term’>params[:controller]</span> and <span class=’term’>params[:action]</span> respectively. Anything passed in a request, such as the data from a form, will wind up in the params hash.
Lets create a new ApplicationController now. We’re going to create it and namespace it from Admin, because from here we’re going to be defining admin rules later on. To do this, you can right click on the Controllers directory in NetBeans and click generate, specifying application as the name, or you can go into the console and type script/generate controller admin/application.
Now we’ll open up the <span class=’term’>admin</span> directory inside our Controllers in the project window and edit the forums_controller.rb inside of this, which should look like this:
<div class=’code’>
<b>app/controllers/admin/forums_controller.rb</b>
<pre lang=’rails’>
class Admin::ForumsController < Admin::ApplicationController
before_filter :store_location, :only => [:new, :edit]
def new
@forum = Forum.new
end
def create
@forum = Forum.new(params[:forum])
if @forum.save
flash[:success] = “A forum has been added successfully.”
redirect_back_or_default admin_forums_path
else
flash[:failure] = “A forum could not be added.”
render :action => “new”
end
end
def edit
@forum = Forum.find(params[:id])
rescue ActiveRecord::RecordNotFound
not_found
end
def update
@forum = Forum.find(params[:id])
if @forum.update_attributes(params[:forum])
flash[:success] = “The forum has been updated successfully.”
redirect_back_or_default admin_forums_path
else
flash[:failure] = “The forum could not be updated.”
render :action => “edit”
end
rescue ActiveRecord::RecordNotFound
not_found
end
def destroy
@forum = Forum.find(params[:id])
@forum.destroy
flash[:success] = “The selected forum has been deleted.”
rescue ActiveRecord::RecordNotFound
not_found
end
private
def not_found
flash[:error] = “The forum you were looking could not be found!”
redirect_back_or_default admin_forums_path
end
end
</pre>
</div>
This is a huge chunk of code, and defines all our administrative actions for just the forums of our site. We’ll go through each action one by one.
The first line we call <span class=’term’>#before_filter</span>, which will run a method before calling the action; <span class=’term’>#store_location</span> in this instance. <span class=’term’>#store_location</span> will store the current location of a user so that we can reference that position when we want to go back to it.
The new action, <span class=’term’>def new</span> defines a new instance variable <span class=’term’>@forum</span> by calling <span class=’term’>Forum.new</span>. Calling new on a model class without specifying arguments will give you a new object of that class, with all the defaults set on the fields. We’ve done this so we can create a new forum object for our form, and also so our <span class=’term’>form_for</span> method call knows where to point to to get to the create action.
The create action defines a new instance variable called <span class=’term’>@forum</span>, but calls <span class=’term’>Forum.new</span> with one argument, <span class=’term’>params[:forum]</span>. <span class=’term’>params[:forum]</span> is what has been passed from our new form to this action, and should include a title and a description. Calling save on the <span class=’term’>@forum</span> object will return either true or false; true if it passes the validations we set on the model, false if it doesn’t. If it returns true then it will set <span class=’term’>flash[:success]</span> to have a message informing the user that a forum has been created. <span class=’term’>flash</span> is another <span class=’term’>HashWithIndifferentAccess</span> object and is mainly used for one-off messages, such as what we’re doing here. After it sets the flash it will redirect us back to our last stored location, or /admin/forums if there isn’t a stored location.
The edit action defines a new instance variable called <span class=’term’>@forum</span>, by calling find on the <span class=’term’>Forum</span> class with <span class=’term’>params[:id]</span> as its argument. This works much in the same way the new action does, providing us with a form to edit the data and then send it to the update action. It will raise an error if it cannot find the forum we’re after.
The update action again defines a new instance variable called <span class=’term’>@forum</span> exactly the same way the edit action does. The difference here is that we call <span class=’term’>#update_attributes</span> on this object which works just like a save call, but we pass in the new attributes in a hash to it, <span class=’term’>params[:forum]</span>. If <span class=’term’>#update_attributes</span> likes what we gave it, then it’ll return true in which case it’ll notify us that the forum has been updated successfully and will redirect us back to admin/forums, if there isn’t a stored location. It will also raise an error if it cannot find the forum we’re after.
The destroy action. We get an existing forum object from the database and then call destroy on it. What this does is call <span class=’term’>DELETE FROM `forums` WHERE `id` = ‘[the id we passed in]’</span> on the database, removing the record for that forum. It will also raise an error if it cannot find the forum we’re after.
The last action we’ve specified there we’ve put <span class=’term’>private</span> above it. This means that if a user tried going to /admin/forums/not_found, the ForumsController class would play dumb and tell the user there isn’t such an action, even though there is. <span class=’term’>not_found</span> is called when we rescue the ActiveRecord::RecordNotFound errors in the edit, update and destroy actions and just informs the user that what they’re looking for could not be found.
