Finally we’re going to design some user profiles and create a public list of everybody who’s registered to our site. We also allow users to set their own bio.

Controllers

We’re going to start by creating a Users controller with two methods: show and index — our profile pages will be at users#show and our user list will be at users#index.

### app/controllers/users_controller.rb

class UsersController < ApplicationController
  def index
    @users = User.all
  end
  
  def show
    # params[:id] comes from the URL: /users/:id
    @user = User.find(params[:id])
  end
end

Now we wire these methods up to routes:

### config/routes.rb

...
  get 'users', to: "users#index", as: "users"
  get 'users/:id', to: "users#show", as: "user"
end

The as: "users" and as: "user" arguments give us access to some convenient methods in our views and controllers. Having specified these route names, we can access the URL for the users path with users_path and the URL to any particular user’s profile with user_path(:id). Now we can get straight into writing our views.

Views

Let’s create the index first. Create the file app/views/users/index.html.erb with the following contents:

### app/views/users/index.html.erb

<div class="row">
  <% @users.each do |user| %>
    <div class="col-sm-6 col-md-4">
      <div class="thumbnail">
        <img src="<%=user.avatar_url%>" alt="Avatar" style="width: 100%;">
        <div class="caption">
          <h3><%=user.name%></h3>
          <p>TODO: Add user bio.</p>
          <p><a href="<%=user_path(user)%>" class="btn btn-primary" role="button">Profile</a></p>
        </div>
      </div>
    </div>
  <% end %>
</div>

<% @users.each do |user| %> repeats the code up to <% end %> once for every user in @users . In users#index we set to User.all equal to a list of all our users. The pipe |user| means we can refer to the user whose turn it is in the loop as userRemember: ruby embedded in tags like this: <%= %> returns a value that’s injected into our HTML, whereas ruby embedded in these tags: <% %> is processed silently. Using <%= %> tags where they are not needed can cause confusing errors.

And create the file app/views/users/show.html.erb:

### app/views/users/show.html.erb

<div class="row">
    <div class="col-md-3 col-sm-12">
        <img src="<%[email protected]_url%>" alt="Avatar" style="width: 300px; max-width: 100%;">
        <h1><%[email protected]%></h1>
        <p>TODO: User bio</p>
    </div>
    <div class="col-md-9 col-sm-12">
        <p>TODO: Show user's posts here</p>
    </div>
</div>

And we need to fix up our navigation bar so that it actually does something. Replace the contents of app/views/layouts/_navbar.html.erb with the following:

### app/views/layouts/_navbar.html.erb
<nav class="navbar navbar-default">
    <div class="container-fluid">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="<%= root_url %>">Memespace</a>
      </div>
      <div id="navbar" class="navbar-collapse collapse">
        <ul class="nav navbar-nav">
          <li id="newsfeed-link"><a href="#">News Feed</a></li>
          <li id="users-link"><a href="<%= users_path %>">Users</a></li>
          <% if current_user %>
            <li id="profile-link"><a href="<%= user_path(current_user) %>">Profile</a></li>
            <li><a href="<%= logout_path %>">Log Out</a></li>
          <% else %>
            <li><a href="<%= login_path %>">Log In</a></li>
          <% end %>
        </ul>
      </div>
    </div>
</nav>

<script>
 <% if request.original_url == users_url %>
   $('#users-link').addClass('active');
 <% elsif current_user && request.original_url == user_url(current_user) %>
   $('#profile-link').addClass('active');
 <% end %>
</script>

We’re adding some javascript here too, which highlights the current page on the navbar.  $('#users-link') and $('#profile-link') is jQuery syntax. Specifically, the dollar sign is a jQuery function. jQuery is a javascript library we have in our rails app by default. Here it’s using the CSS selector “#<id>” to choose the page element with the id of “users-link” or “profile-link” (depending on the current page, according to our ruby if-elsif statement). Then we continue to use jQuery to add a class of active (a bootstrap class) to the element — this will make it clear that this is the current page.

Our navbar will be finished when we have a news feed to link to. Our app is starting to take form. The last thing we’ll do in this chapter is add a bio property to our User model and create a form so that people can edit their bio at will.

Adding A User Bio Property

First, generate an empty migration called AddBioToUsers with rails generate migration AddBioToUsers. Navigate to the new migration file (db/migrate/20160423141720_add_bio_to_users.rb) and make it look like this:

### db/migrate/20160423141720_add_bio_to_users.rb

class AddBioToUsers < ActiveRecord::Migration
  def change
    add_column :users, :bio, :string, default: "No bio"
  end
end

Now run rake db:migrate and add a couple of routes:

### config/routes.rb

...
  get 'users/:id/edit', to: "users#edit", as: "edit_user"
  patch 'users/:id', to: "users#update"
end

The GET users/:id/edit route pointing to users#edit is how a user accesses a form to edit their profile. The PATCH users/:id route pointing to users#update is where the form is submitted to, and where we actually update the user model. Again, the as: "edit_user" route name will give us access to the helper method edit_user_path(:id) as a convenient way of getting the URL of a user’s edit profile page.

Controllers

Let’s add the new actions to our users controller:

### app/controllers/users_controller.rb

class UsersController < ApplicationController
  
  def edit
    @user = User.find(params[:id])
  end  
  
  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      redirect_to user_path(@user.id)
    else
      render 'edit'
    end
  end
...

users#update is referring to a the method user_params, which we haven’t defined yet. We’re going to make user_params return the hash of new information about the user that’s submitted from the users#edit page. We also need to define a method which will only allow users to edit their own profile; this will be called correct_user. Add the following methods as private methods (following the private keyword) to the end of the controller:

### app/controllers/users_controller.rb

...
  private
  
    def correct_user
      # .to_i converts values to integers here because params[:id] is a string
      # Without this current_user.id and params[:id] would never match
      if current_user.id != params[:id].to_i
        redirect_to root_url
      end
    end
    
    def user_params
      params.require(:user).permit(:bio)
    end
end

In user_params, the .require(:user) method will throw an exception if our params hash doesn’t contain an object identified by the key ‘user’, and the .permit(:bio) method strips all properties other than bio from this ‘user’ hash to prevent malicious users attempting to manually edit their names and other information.

Still in our users controller, we must now call the correct_user method before both edit and update. We could literally put it at the beginning of each action, but rails gives us a shortcut. Add this line to the top of the users controller:

### app/controllers/users_controller.rb

class UsersController < ApplicationController
  before_action :correct_user, only: [:edit, :update]
...

Now users can only edit their own profiles. Other users will be redirected to the home page. We’ll add some feedback messages later so users know why they’re being redirected. Time to work on our views.

Views

We just need one new view, app/views/users/edit.html.erb:

### app/views/user/edit.html.erb

<div class="row">
    <div class="col-md-6 col-md-offset-3 col-sm-12">
        <h2 align="center">Updating <%[email protected]%></h2>
        <%= form_for(@user) do |f| %>
            <div class="form-group">
                <textarea name="user[bio]" class="form-control" placeholder="Bio"><%[email protected]%></textarea>
            </div>
            <input type="submit" value="Submit" class="btn btn-primary"/>
        <% end %>
    </div>
</div>

Look at the <textarea> tag above. Textareas are form elements, so if they’re inside <form> tags, their content is submitted with the form. It has a name attribute — this is what we find in our params hash. If the name attribute was set to “bio” instead of “user[bio]”, our user_params method would throw an exception because it wouldn’t be able to find params[:user].

Now let’s just add the bio and an Edit Profile button to our user profile in app/views/users/show.html:

### app/views/users/show.html.erb

<div class="row">
    <div class="col-md-3 col-sm-12">
        <img src="<%[email protected]_url%>" alt="Avatar" style="width: 300px; max-width: 100%;">
        <h1><%[email protected]%></h1>
        <p><%[email protected]%></p>
        <% if current_user.id == @user.id %>
            <p><a href="<%=edit_user_path(@user.id)%>" class="btn btn-primary" role="button">Edit Profile</a></p>
        <% end %>
    </div>
    <div class="col-md-9 col-sm-12">
        <p>TODO: Show user's posts here</p>
    </div>
</div>

User Profile With BioEdit UserFinally, add bios to our user index:

### app/views/users/index.html.erb

...
  <div class="caption">
    <h3><%=user.name%></h3>
    <p><%=user.bio%></p>
    <p><a href="<%=user_path(user)%>" class="btn btn-primary" role="button">Profile</a></p>
  </div>
...

User Index With BioAnd there we go! Users now have profiles which they can customise with a bio that also appears on the user index.

To finish off, deploy your new code to Heroku and migrate the database.

git add -A
git commit -am "Added user profiles"
git push heroku master
heroku run rake db:migrate

In the next chapter we’re going to start adding functionality that allows users to post messages and images to a shared news feed.