Table of Contents

View Screencast (Quicktime)
Right-click and choose Save As to save file to your computer


Sign Up Now!

If you aren’t already receiving our course lessons via email, sign up now to be sure you don’t miss anything.

Every few days, we’ll send you an email with a link to the next episode, plus a list of additional resources for advancing your knowledge.

There’s no cost and no obligation. And we’ll never share your email address with any third party.


We’ll send you the first lesson right away.


Want to help spread the word? We’d be grateful if you would include a link to the course in your blog, web site, or emails.

User Management

11 comments

Goals

In this lesson, we work towards implementing an administrative dashboard by expanding the user controller generated by restful_authentication to give us full abilities to manage user data.

Setup

We begin with the code with which we ended Lesson 11. These zip files contain the beginning and ending states of the code:

Clean up session create with a notice if user incorrectly enters credentials

Before diving in to the core work in this lesson, let’s continue to do some small changes to improve the experience our application provides. One pain point occurs when you try to log in and type the wrong credentials.

Let’s put a notice in the create failure path, before re-rendering the edit dialog

flash[:notice] = "Try entering your credentials again" 

Create a default admin user (migration)

Let’s use a migration called a “data migration”. It doesn’t change the structure of the database. Instead it loads data, in this case, a default user account.

script/generate migration AddDefaultUser
self.up
  if !User.find_by_login('admin')
    User.create(:login => 'admin', :email => 'admin@sample.com', 
                :password => 'changeme', :password_confirmation => 'changeme')
  end
end

We aren’t creating a down migration in this case. We don’t know if we were the ones who created this account for sure, so we won’t delete it when this migration is run in reverse.

Run the migration in the terminal:

rake db:migrate

Add a user list page

The “restful_authentication” plugin’s generate creates a RESTful controller, but it only creates the new and create actions. We need to fill the rest out and can use the Pages controller as a model.

Add an index action to users controller.

def index
  @users = User.find(:all)
end

Create users/index view in the users directory (index.html.erb). Also prepare by adding links to show, edit, and delete. Put a link at the bottom to create new users.

<h1>Listing Users</h1>

<table>
  <tr>
    <th>Login</th>
    <th>Email</th>
  </tr>

  <% for user in @users %>
    <tr>
      <td><%=h user.login %></td>
      <td><%=h user.email %></td>
      <td><%= link_to 'Show', user %></td>
      <td><%= link_to 'Edit', edit_user_path(user) %></td>
      <td><%= link_to 'Delete', user, 
          :confirm => "Are you sure you want to delete '#{user.login}'?",
          :method => :delete %></td>
    </tr>
  <% end %>
</table>

<br />

<%= link_to 'New User', new_user_path %>

Take a look…seems to work, but we need to fill out the rest of the actions and the appropriate views.

Add show user page

Add a show action to the users controller.

def show
  @user = User.find(params[:id])
end

Add the show view (show.html.erb).

<p>
  <b>Login:</b>
  <%=h @user.login %>
</p>

<p>
  <b>Email:</b>
  <%=h @user.email %>
</p>

<p>
  <b>Password:</b>
  [secret]
</p>

<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>

Add a user delete function

Add destroy action to users controller.

def destroy
  @user = User.find(params[:id])
  @user.destroy

  redirect_to(users_url)
end

Note the link is already in the index view and uses a javascript alert dialog to confirm the deletion with the user.

Add a edit/Update user functions

Add edit and update actions to the users controller. New/create and edit/update always work in pairs in the RESTful implementation in Rails 2.

def edit
  @user = User.find(params[:id])
end

def update
  @user = User.find(params[:id])

  if @user.update_attributes(params[:user])
    flash[:notice] = 'User was successfully updated.'
    redirect_to(user_path(@user))
  else
    render :action => 'edit'
  end
end

Create a corresponding view for edit. It is nearly identical to the new view, but we changed the form_for line to take advantage of Rails behavior. By specifying the object on the form_for line, Rails automatically generates the proper URL to our update action and submits the form using the “PUT” HTTP method.

<%= error_messages_for :user %>

<% form_for @user do |f| -%>
  <p><label for="login">Login</label><br/>
  <%= f.text_field :login %></p>

  <p><label for="email">Email</label><br/>
  <%= f.text_field :email %></p>

  <p><label for="password">Password</label><br/>
  <%= f.password_field :password %></p>

  <p><label for="password_confirmation">Confirm Password</label><br/>
  <%= f.password_field :password_confirmation %></p>

  <p><%= submit_tag 'Update' %></p>
<% end %>

Now test all of the pages together. They work!

Wrapping up

Of course, it is tiresome to keep typing the URLs manually to get to these pages. You can navigate directly to the users controller at localhost:3000/users, but it would be a lot easier to have a dashboard that collects all of these links in one easy to access place. Even better, the dashboard should be available from our navigation tags when appropriately logged in.

Coming up

In our next lesson, we’ll add support for “administrative” pages in our simple CMS, so we can create the dashboard using our own technology. We’ll also tweak the tab navigation so it automatically discovers and displays links without us needing to edit code.


Add a Comment

Have a comment or question about this lesson? Add it here.






Comments on This Lesson

From: Christopher Haupt       Date: 07/01/08 04:16 PM

Subject: RE: User

We are referring to the class “User” and invoking class methods on User. Class methods are those that don’t require a specific instance object of a class and typically affect all members of the Class. In this specific example, the finder “find_by_login” searches for all Users with a login name of ‘admin’. You’ll see this pattern all over Rails.

-c

From: Ali       Date: 07/01/08 04:16 PM

Subject: Question about if 'User'

Hello, What does the ‘User’ in ‘if !User.find_by_login(‘admin’)’ and some other lines of code refer to.

From: Steve       Date: 05/29/08 10:10 AM

Subject: thanks

Christopher – that’s awesome! Thanks! Now I’ve got to get busy implementing it. :)

From: Christopher Haupt       Date: 05/29/08 10:10 AM

Subject: RE: protect some actions

Steve, absolutely you can protect only certain methods. There are a couple of ways, but perhaps the easiest is to list which methods you want to “except” from the filter:

before_filter :login_required, :except => :show

You could use the :only option instead to just do the filter on specific actions. If you need to specify more than on action, put them into an array:

before_filter :login_required, :only => [ :edit, :update ]

From: Steve       Date: 05/28/08 09:09 AM

Subject: protect some actions but not others?

Hi; I really appreciate the meticulous thoroughness you have provided in these first screencasts. My question is: is it possible to use the restful authentication plugin to prevent access to only some actions without login? You have set up two categories of controllers in your sample project: ones that a logged in user has access to and one that is public. I’m thinking of a project in which the “show” method of a controller would be public (unprotected), but only logged in admins would have access to the create, update and destroy actions. Can you point me to some tips on how to do this? Thanks.

From: Enrique        Date: 05/25/08 01:13 PM

Subject: @user.destroy

When doing @user.destroy on the destroy method the session is destroy as well not just that record, I can see in your screencast this does not happen to you.

However I checked and compared each file modified by you (that is the users, sessions controllers and the views for the user controller) and I can’t find anything wrong nor anything different, What could be wrong with my app?.

Thanks in Advance

From: Walter       Date: 05/15/08 11:11 AM

Subject: Solved missing Edit :id parameter in show.html.erb

Found the error! <= link_to ‘Edit’, edit_user_path(@user) %> in show.html.erb had @users not @user. Now the Edit link works fine. Interestingly enough playing around with <= debug(@user) %> added in the index and show views pointed me back to the user controller, then I realized I made a typo….. Anyway, killed a brain cell but good practice I guess. Cheers

From: Walter       Date: 05/14/08 08:20 PM

Subject: missing :id in Edit link_to from show view

I have an error that I’m trying to track down. In /users I do get the List of all logins, and can successfully click Show/Edit/Delete without problems. However, when inside a show action ie: /user/2, the lower Back button works but the Edit button shows /user//edit and is missing the :id reference. The Edit in the /users comes from inside the index.html.erb loop <=link_to ‘Edit’, edit_user_path(user) %> where (user) is the iteration of @users, and works just fine. Inside show.html.erb this is accessed directly with <= link_to ‘Edit’, edit_user_path(@user) %> but does not include the :id. Checked the edit_user reference in rake routes but that’s okay. Any ideas? Thanks

From: Michael Slater       Date: 05/09/08 04:16 PM

Subject: Restful routes

Shilpa, these are the “magic” restful routes provided by the single line in routes.rb, map.resources :user. You can find the docs the explain them here: http://api.rubyonrails.org/classes/ActionController/Resources.html

From: Shilpa Som       Date: 05/09/08 12:12 PM

Subject: Restful paths

Thanks a lot for responding for my question in previous episode. This is one more great screencast.

Again query :) Could you pls clarify the restful paths used in the code like edit_user_path,users_path,user_url. I didn’t find find any expansion for these paths in routes.rb.

Or Could you redirect me to any links online for further reading in this regard??

Thanks again.

From: Adam Teale       Date: 04/29/08 12:00 AM

Subject:

Christopher & Michael your RoR screencast series is so awesome, thanks a lot for all the effort to put it together. I really appreciate the cruisy pace that you take the viewer/user through the whole process. It’s great that you have the lessons doc/files online too. Looking forward to your next ep!