Exercise: Create a New Application that Tracks People

This week we are going to start from scratch with a new rails application. However, I’m going to let you do most of the initial work of getting it up and running. It should be very similar to the previous couple of weeks. Don’t forget to:

A couple of hints:

Now that you understand migrations, I can tell you a secret: the rails generate resource command allows you to specify columns on the command line, and it will automatically generate the appropriate code in the migration.

$ rails generate resource Person name:string gender:string age:integer

Notice the automatically generated code in the migration.

Don’t forget to run rake db:create and rake db:migrate at the appropriate times to create the database and execute the migrations that actually create the table(s) in the database.

Also, this isn’t a test. You don’t have to do this purely from memory. Use all the resources at your disposal to do a good job: other people, previous labs, Google, etc.

Swap Drivers

Now it is time to swap who is driving and who is navigating. Don’t forget to check in your changes to git so your partner has access to them:

$ git add .
$ git commit
$ git push

Now swap who is driving the computer, and make sure the new partner has the changes:

$ git pull
$ rails server

Use Partials in the Views

Now, for the new material in the lab. We are going to use partial views to make it easier to reuse code for multiple purposes. Partials are really nice because if you want to modify something, you only have to modify it in one place, and it automatically gets updated everywhere. For example, we will begin by creating a partial that displays the new person form. This partial can then also be used to display a form for editing a user. And when we want to add something to the form, we only have to add it in one place, and it automatically works for both new persons and editing persons.

Use a Partial for the Person form

Rather than the traditional view file that we’ve used before, for this lab we will use a partial to display the form. Partial files always start with the underscore character. So let’s create a form partial, which will be stored in a file called app/views/people/_form.html.erb. (note the underscore).

The only thing that should be in this file is the actual form.

<%= form_for @person do |f| %>
  <%= f.label :name, "Name" %> <%= f.text_field :name %> <br />
  <%= f.label :age, "Age" %> <%= f.text_field :age %> <br />
  <%= f.label :gender, "Gender" %> <%= f.text_field :gender %> <br />
  <%= f.submit %>
<% end %>

Next, we need to construct our new view file: app/views/people/new.html.erb. Remember, this is the file that will be displayed to the user when they go to the new action. In this file, we will insert the form partial into the view:

<h2> Add a New Person </h2>

<%= render "form"%>
<hr />
<%= link_to "Return to the List of Users", people_url %>

Now run your server and go visit this page. Notice that it displays everything in this new.html.erb file – the “Add a New Person” heading and the link at the bottom, and it also displays the form from the partial.

Use a Partial for the Person list

We can also use partials to replace lists and .each loops. To illustrate, lets replace our standard .each loop in the list action with a partial. First, we need to create a new partial that will print out a single person in the list. Since it prints a person’s information, let’s call the partial _person.html.erb:

<li> <%= person.name %> (<%= link_to "View", person %>) </li>

Yup, that file should only contain one line right now. Also notice that we used the variable person – when rails uses a partial for a model object, it fills in the variable with the same name as the partial with the object of interest.

Now, we can simplify our index.html.erb view by using a render partial command:

<h2> List of People </h2>
<ul>
<% @people.each do |person| %>
<%= render person %>
<% end %>
</ul>
<%= link_to "Add a New Person", new_person_url %>

Note that in this we still looped through all the people, and rendered each person using the partial. Test it to see that it works. We can make this simpler. Rails will actually do the looping for us if you want it to:

<h2> List of People </h2>
<ul>
<%= render @people %>
</ul>
<%= link_to "Add a New Person", new_person_url %>

Now you can test this to see that it works. Run your server, and try looking at the list. See how rails renders the partial once for each item in the collection. You can view the source of the webpage if you like to see how it looks.

Add an Edit Action

One of the really nice things about partials is that we can easily re-use them in other pages. To illustrate this, we will create an edit action that allows us to edit an existing person’s information.

The first thing we need to do is to create an edit action in the controller. This action is intended to display a form where the user can edit the person’s data. This action is pretty simple; all it needs to do is to find the user’s current information:

  # Find a person for editing.   
  # Note, this action expects a parameter of :id containing the id of the user to be edited
  def edit
    @person = Person.find(params[:id])
  end

Next, we need to create a view file for editing. We can re-use the partial from before that renders the form, since we store the person’s information in the same variable: @person. So the app/views/people/edit.html.erb should look like this:

<h2> Edit <%= @person.name %> </h2>

<%= render "form" %>

Also, we need to have a way to get to this page. Let’s modify our _person.html.erb partial to provide a link to edit a user:

<li> <%= person.name %> (<%= link_to"View", person %>, <%= link_to "Edit", edit_person_url(person) %>) </li>

If you run your server and go to the list of users, you should see an edit link for each user. Clicking on that link should display a form that has the user’s information already filled in, ready for the user to edit. Try this out and make sure you get the interface you expect. Sure enough, the form is displayed, and the person’s information is filled in.

Now, clicking submit to save the user’s information won’t work quite right yet. When the information is submitted, it currently goes to the “update” action, which hasn’t been written yet. To do this, we create a new action in the controller called update:

def update
  @person = Person.find(params[:id])
  person_params = params.require(:person).permit(:name, :age, :gender)
  if @person.update_attributes(person_params)
    redirect_to people_url
  else
    render :action => "edit"
  end
end 

There, now we should be able to edit exiting records in addition to creating new records.

Install a Layout for all pages, with stylesheet

A layout specifies a “template” that all your webpages will have. Basically, it is the outside stuff of every webpage, with content that changes based on which action is currently being rendered. This is best illustrated with an example. Let’s create a layout for all the webpages in our application. To do this, we need to edit the file app/views/layouts/application.html.erb.

Now, we can put whatever we want into this layout file. Our main content (from our view pages) will be places whereever you have the yield command:

<!DOCTYPE html>
<html>
<head>
  <title>Rick's Friends</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>
  <h1> Rick's Amazingly Awesome Friends List </h1>
    <h4> Even better than Facebook </h4>

<%= yield %>

<hr />
<div class="footer">This website was created by Rick Wash </div>
</body>
</html>

(Of course, you’ll want to replace my name with your own name.. This isn’t my application, its yours. Take credit.)

Once you have this file created, go and explore your application again. Go back to the list page, add a new person, and view their information. Notice how the layout is added to every page as it is drawn. Layouts are great because they help provide some of the look and feel of a site.

Note, if you want, you can create a separate layout for each controller. To do so, create a new layout file with the name of the controller: app/views/layouts/people.html.erb for example.

Also, notice that the default layout automatically includes the ‘application’ css file. Actually, due to the way rails handles CSS files, it automatically includes all CSS files in the directory app/assets/stylesheets/.

So, to add style to our page, create the file app/assets/stylesheets/tracker.css and fill it in with some fun CSS:

h1 {
  color: red;
}

.footer {
  font-size: small;
}

Reload your pages and notice how the CSS has taken effect. And it is taking effect on all of your pages automatically!

Swap Drivers

Now that we’ve successfully used partials and layouts to make our application work better, and added the ability to edit existing records, it is time to swap drivers. The original driver should do the 2nd half of this lab.

Allow People to Upload Photos

The final thing that we are going to do today in lab is to add the ability to upload photos of people to our application, which will be displayed on the view page for each user.

Install the paperclip plugin

To do this, we need to install a plugin called paperclip to make this easy. Information about the plugin is available here: hhttps://github.com/thoughtbot/paperclip . Edit the file “Gemfile” in the top directory of your application and add the following line:

gem 'paperclip' 

Then run this command to install the plugin:

$ bundle install 

Add the “picture” columns to the Database

Next, we need to add a picture column to our database for a person. To do this, we use a migration. But, as the paperclip documentation describes, we can ask paperclip to write the migration ourselves:

$ rails generate paperclip people picture 

Notice that this wrote a new migration file for us. Open up the migration and look at it. What this actually does is add four columns to the database: picture_file_name (which stores the name and location on the filesystem where the file is stored), picture_content_type (which stores what kind of file it is. e.g. PNG, jpg, etc.), picture_file_size, and picture_uploaded_at.

Don’t forget to rake db:migrate after creating this to actually update the database.

Next, we need to tell rails that this column is a special column for uploaded files. To do this, we edit the model file app/models/person.rb:

class Person < ActiveRecord::Base
  has_attached_file :picture
  validates_attachment :picture, :content_type => { :content_type => ["image/jpg", "image/gif", "image/png", "image/jpeg"] }
end

Notice we also add a validator for this. This is required by the Paperclip plugin for security reasons. We validate that the file that was uploaded is actually an image file.

Finally, we need to modify our form to allow the user to upload a picture. Remember, the form is stored in app/views/people/_form.html.erb:

<%= form_for(@person, :html => {:multipart => true}) do |f| %>
  <%= f.label :name, "Name" %> <%= f.text_field :name %> <br />
  <%= f.label :age, "Age" %> <%= f.number_field :age %> <br />
  <%= f.label :gender, "Gender" %> <%= f.text_field :gender %> <br />
  <%= f.label :picture, "Picture" %> <%= f.file_field :picture %> <br />
  <%= f.submit %>
<% end %>

Note that there are two changes here. First, we use a file_field to allow the user to specify a file to upload. Second, we have to specify that the form is a multipart form so that the web browser uploads the file appropriately.

Next, we have to modify our controller to permit the form to include this field:

  def create
    person_params = params.require(:person).permit(:name, :age, :gender, :picture)
    @person = Person.create person_params
    if @person.save
      redirect_to people_url
    else
      render action: 'new'
    end
  end

Now the user should be able to upload photos of their friends to our application. The only thing left is to modify the app/views/people/show.html.erb file to display the photo:

<h2> <%= @person.name %> </h2>
Age: <%= @person.age %><br />
Gender <%= @person.gender %><br />
<% if @person.picture.exists? %>
  <%= image_tag @person.picture.url %>
<% end %>

<%= link_to "All People", people_url %>

For viewing, we used two things. First is the function image_tag, which displays an image on the webpage, and accepts one parameter – the URL of the image to display. The second is from our model. @person.picture is a reference to the photo that was uploaded for this person, and @person.picture.url gives the URL of that photo. Combining these two, we get a way to display the photo on the view page. Notice that these are wrapped by an if statement; that way it only tries to display the image if an image was uploaded.

Optional: Create Thumbnails

It is possible to have multiple versions of a file automatically created, such as a thumbnail image. To do this, we modify the model file to add an additional parameter to has_attached_file:

class Person < ActiveRecord::Base 
  has_attached_file :picture, :styles => {:medium => "300x300>", :thumb => "100x100>" }
end

This tells rails to automatically create a thumbnail version of the file that is 100 pixels by 100 pixels, and a medium version that is 300x300. When the user uploads a photo, these thumbnails will automatically be created.

Next, you can use these alternate versions in a very similar way as you use the normal version. If the normal version is @person.picture.url, then the thumbnail is @person.picture.url(:thumb). Lets modify our list of users to display the thumbnail images of each person. To do that, remember that our list is using a partial app/views/people/_person.html.erb:

<li> 
<% if person.picture.exists? %>  
  <%= image_tag person.picture.url(:thumb) %>
<% end %>
<%= person.name %> (<%= link_to"View", person %>, <%= link_to "Edit", edit_person_url(person) %>) 
</li>

(Remember that inside this partial, the person object is stored in the person variable because that is the name of the partial.)

Now go use your application. You’ll have to upload new files for the thumbnail to display because it only creates the thumbnail images at the time the file is uploaded. It should display a small version of the image in the user list, and the full-size image when viewing the user.

Further help: The initial controller

Below, I’ve put the code for the controller for the initial exercise for this lab. Try to do the exercise without looking at the code below. But, if you get stuck, its here to help you.

class PeopleController < ApplicationController
  # Display a form for adding a new person
  def new    
    @person = Person.new  
  end  

  # Receive a submitted form, and actually create a new person in the database
  # Note: expects to receive a 'person' form containing at least 'name', 'age', and 'gender'
  def create    
    person_params = params.require(:person).permit(:name, :age, :gender)
    @person = Person.create person_params
    if @person.save      
      redirect_to people_url    
    else
      render action: 'new'
    end
  end

  # Display a list of all people
  def index
    @people = Person.all
  end

  # Display information about a single person
  # Note: expects to receive the id of the person to be displayed
  def show
    @person = Person.find params[:id]
  end

end