21: Adding the user page

Right now, the users are the main actors in the system. However, the only thing we know about them is their email. This is both unsafe and not very friendly.

So let's give our users names and pages.

Ading a user's name

The datatabase change

Adding names to users is related to changing a table. As you've probably guessed, that means changing a table. And to change a table, we create a migration. Open your terminal and type:

$ rails generate migration add_name_to_users name:string

Rails will infer the name of the database table and will create the appropriate migration for you. If you want to see what it generated, look inside the db/migrate/123123123_add_name_to_users.rb file:

class AddNameToUsers < ActiveRecord::Migration
  def change
    add_column :users, :name, :string
  end
end

This migration is OK, so we can safely apply the changes to our database:

$ rake db:migrate

The column now exists in the users table.

Requesting the name during registration

Let's move to the Registration form and ask for the name during the registration. Open the file app/views/devise/registrations/new.html.erb and, after the line

<%= f.email_field :email, autofocus: true %>

add:

<%= f.text_field :name, label: "Name", required: true%>

Now launch your server (using rails server) and logout if you were logged in. Visit http://localhost:3000/users/sign_up and see the new field! Try to type a name and sign up. It works!

You will also notice that our form sets the name as required: true. This is to make sure the users enter a name during registration. This is a validation on a very superficial level, we would like to add one more.

Rails has several types of validations we can use. Let's try one. Open app/models/user.rb and add the following on the line before the last end:

validates :name, presence: true, allow_blank: false

One more thing, though. Devise is very, very strict about its parameters, so we'll have to tell Rails to also include the name in the check for the sign_up action.

To do this, open up app/controllers/application_controller.rb and add the following before the last end:

    before_filter :configure_permitted_parameters, if: :devise_controller?

    protected

    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
    end

What this code is doing is letting the user create itself with not only the default attributes, but also a name.

You can use various validations on testing the format, the length, uniqueness, and others. For more validations consult the validations guide.

Displaying the user's name on the cases and updates

Let's start by showing the users' names instead of their emails.

Open the views/cases/index.html.erb file, locate the following fragment:

<%= "Created by: " + c.user.email %>

and replace it with

Created by: <%= c.user.name %>

Do the same in views/cases/show.html.erb, both for the case and for the updates sections.

When you refresh the pages, for some users, you will not see names. That's because the users who have posted the content were created before the change with the name. The simple solution is to delete the old cases and recreate them.

If you still want to keep the cases, you can open the rails console in your terminal and run the following:

User.where(name: nil).each { |user| user.update_attributes(name: "User #{user.id}") }

This line just finds all the users who don't have a name, and going through each of the users, sets the name to User user_id.

Adding the user's page

We would like to see all the user's activity. Let's start by transforming the text within the label into a link. By clicking on this link we'll jump to the user's page, where we'd like to see the user's name and a list of posts: cases and updates.

First, let's turn the user's name into a link. The first question we have to answer is where does the link lead to, or what is the URL it points to. Open config/routes.rb and add (after get "landing" => "pages#landing"):

resources :users, only: [:show]

This tells the Rails routing system that there is a resource called User, for which only the show action will work. That is, Rails won't generate routes that let us create, edit or delete users. You can see what the URLs are by running

$ rake routes

in your terminal. The last line of the output should be the following:

user GET    /users/:id(.:format)              users#show

And it means:

  • the generated path is user (we'll refer to it as user_path)
  • the method is GET
  • the URL will be /users/:id
  • the controller will be users_controller and the action will be show.

Great, we've generated a route, let's add a link. Open app/views/cases/index.html.erb and find the lines similar to:

<span class="label label-default">Created by: <%= c.user.name %></span>

and replace them with:

<span class="label label-default">  
  <%= link_to(c.user.name, user_path(c.user)) %>
</span>

Refresh the page and see the following:

The link color clashes with the default label style, so let's apply a quick fix:

<span class="label label-default">  
  <%= link_to(c.user.name, user_path(c.user), style: "color: white") %>
</span>

The labels are now white. Much better!

It is not a good practice, though, to include styles in the html.erb file. After we've finished prototyping, we will extract the styling to the associated .scss files.

Apply the same logic to app/views/cases/show.html.erb, both for the case and updates section. Make sure to use the appropriate variable in the context (instead of c, you may need to use the instance variable @case or the block variable u, respectively).

Controller and action

Now, when you click on a user link, you will get the following error

This is what we see. Rails tries to find a UsersController (as specified in the rake routes output), but cannot find one.

Create a new file, users_controller.rb under app/controllers and paste the following in the newly created file:

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

This defines a show action that returns a user based on the ID provided in the URL. Refresh the page and look at the result:

When Rails says that a template is missing, we need to create one.

The view

The template (or the view) should be located under app/views/users/show.html.erb. Create the users folder, then the show.html.erb file.

Since the file is empty, the user's page will be empty. Let's populate it with information.

We would like to display two blocks of information: the most recent updates and the most recent cases created by this user:

This is a quick solution that displays the user's recent activity:

<%= render "shared/internal_navbar" %>

<div class="container">
  <h2><%= @user.name %></h2>

  <h3>Recent updates:</h3>
  <ul class="list-group">
    <% @user.updates.last(20).each do |u| %>
      <li class="list-group-item">
        <span class="label label-primary"><%= u.domain %></span>
        <%= link_to(u.case.title, u.case) %>: <%= u.title %>
      </li>
    <% end %>
  </ul>
</div>

<div class="container">
  <h3>Recent cases:</h3>
  <% @user.cases.last(20).each do |c| %>
    <div class="col-lg-4 col-md-4 col-sm-6 col-xs-12">
      <div class="thumbnail", style="height: 500px; overflow: hidden;">
        <div class="caption">
          <h3><%= c.title %></h3>
        </div>
        <%= link_to(cl_image_tag(c.case_image.path, width: 500, class: 'img-responsive'), c) %>
      </div>
    </div>
  <% end %>
</div>

And this is what it looks like:

results matching ""

    No results matching ""