Let's recap the major things we've done up to this point in the course

  • We've learned to use the basic tools, like Git, the command line, and a code editor
  • We've learned to query and manipulate data using SQL
  • We've learned the basics of the Ruby programming language
  • We've learned how to use Ruby models to read and change the data in a table, instead of writing SQL
  • We've built web pages with routes, controllers and views, and put Ruby inside them

Now it's time to put all of these tools to work at the same time, and build a page that shows the data in our database.

The data is already there

Your copy of web-apps came with two tables, made and filled when the Codespace started:

  • companies: name, city, state, url
  • contacts: first_name, last_name, email, phone_number, company_id

Visit /data to see them: three companies, four contacts. Each contact's company_id is the id of a row in companies. The rows come from scripts/create_data.rb.

Open app/controllers/data_controller.rb while you're there. The Data page reads every table and turns its rows into HTML. That's all a model is for in this post: rows in, HTML out.

A companies page

The route and the controller go the same way as tacos and dice. In config/routes.rb:

get("/companies", { :controller => "companies", :action => "index" })

Then the controller, in the terminal that isn't running the server:

bin/rails generate controller companies

And the index action in the file it made, app/controllers/companies_controller.rb:

class CompaniesController < ApplicationController
  def index
    render({ :template => "companies/index" })
  end
end

Then the view, app/views/companies/index.html.erb:

<% companies = Company.all %>
<h1><%= companies.count %> companies</h1>
<ul>
  <% for company in companies %>
    <li><%= company["name"] %>, <%= company["city"] %>, <%= company["state"] %></li>
  <% end %>
</ul>

Visit /companies: "3 companies", and a line for each. Three lines of Ruby are doing the work, and you've seen every one of them before:

  • Company.all is every row in the companies table, the same as in last week's scripts.
  • for company in companies is the same loop as for filling in fillings on the tacos page. This time each pass hands you one row instead of one string.
  • company["name"] is one cell of that row: the row, then the column name in brackets.

The page didn't change; the data did

Open scripts/create_data.rb and add a fourth company, the same way the other three are made:

microsoft = Company.new
microsoft["name"] = "Microsoft"
microsoft["city"] = "Redmond"
microsoft["state"] = "WA"
microsoft["url"] = "https://microsoft.com"
microsoft.save

Put it after the Anthropic lines, before the contacts. In the second terminal, run the script:

bin/rails runner scripts/create_data.rb

It prints "There are now 4 companies and 4 contacts." It deletes the companies and contacts first and makes them again, so it always ends with exactly the rows in the file. Refresh /companies: "4 companies". You didn't touch the view. That's the difference between this page and the tacos page: the fillings were written into the view, and the companies come from the database every time someone asks.

The SQL is still there

Open log/development.log and scroll to the bottom. Among the lines for your last request is something like:

Company Load (0.1ms)  SELECT "companies".* FROM "companies"

That's the SELECT your loop ran. Company.all wrote it for you, and it's the same SQL you wrote by hand in week 2.

Change Company.all to only the California companies:

<% companies = Company.where({ "state" => "CA" }) %>

Refresh: "2 companies", and a WHERE in the log. Change it back to Company.all when you're done.

Fell behind in class? code-along-complete/2-companies/ in your copy has the routes, the controller, the view and scripts/create_data.rb with Microsoft in it, at the same paths as yours.

Try it

Make a page at /contacts that lists every contact's first and last name and the name of their company. A contact only holds its company's id, so look the company up inside the loop:

Company.find_by({ "id" => contact["company_id"] })

Then add each contact's email. The practice section of the web-apps README has the same exercise, and the cards page after it.