The problem
From the Practice section of the web-apps README:
Contacts. Make a page at
/contactsthat lists every contact's first and last name and the name of their company. Look the company up withCompany.find_by({ "id" => contact["company_id"] }). Then add each contact's email.
It's the companies page from class, with one new thing: a contact doesn't hold its company's name, only its company's id.
1. The route and the controller
In config/routes.rb, under the companies line:
get("/contacts", { :controller => "contacts", :action => "index" })
In the terminal that isn't running the server:
bin/rails generate controller contacts
And the action, in app/controllers/contacts_controller.rb:
class ContactsController < ApplicationController
def index
render({ :template => "contacts/index" })
end
end
The errors on the way are the tacos ones: no controller, unknown action, missing template.
2. Every contact's name
app/views/contacts/index.html.erb. The same loop as the companies page, over Contact.all:
<h1>Contacts</h1>
<ul>
<% for contact in Contact.all %>
<li><%= contact["first_name"] %> <%= contact["last_name"] %></li>
<% end %>
</ul>
Four names: Tim Cook, Craig Federighi, Andy Jassy, Dario Amodei.
3. The company, by its id
Open /data and look at the contacts table. Tim Cook's company_id is 1, and the company whose id is 1 is Apple. So for each contact, find the company whose id is that contact's company_id:
<% company = Company.find_by({ "id" => contact["company_id"] }) %>
find_by gives back one row, the first that matches, so company["name"] is its name. It goes inside the loop, because each contact has its own company:
<h1>Contacts</h1>
<ul>
<% for contact in Contact.all %>
<% company = Company.find_by({ "id" => contact["company_id"] }) %>
<li>
<%= contact["first_name"] %> <%= contact["last_name"] %>, <%= company["name"] %>, <%= contact["email"] %>
</li>
<% end %>
</ul>
The email was the last step: contact["email"], one more cell of the same row.
Tim Cook, Apple, tim@apple.com
Craig Federighi, Apple, craig@apple.com
Andy Jassy, Amazon, andy@amazon.com
Dario Amodei, Anthropic, dario@anthropic.com
What the log shows
Refresh the page and look at the bottom of log/development.log. One SELECT for the contacts, then one more for each contact's company, with a WHERE "companies"."id" = ? in it: five lines in all. The second Apple one starts with CACHE: Craig's company is Tim's, and Rails remembered the answer from a moment before, inside the same request. Every other find_by is its own trip to the database. For four contacts that's nothing. For four thousand it would be slow, and later in the course you'll see ways to ask for all of it at once. For now, the point is that you can read exactly what your page asked the database for.
When it went wrong
- undefined method '[]' for nil.
find_byfound nothing, socompanyisnil. Usually a typo in the column:"company_id", with the underscore. (Or a contact whose company was deleted.) - Every contact shows the same company. The
find_byline is above the loop instead of inside it. - company.name instead of company["name"]. It works, but stay with the brackets: a row is read the same way everywhere in this course, column name in brackets.