# Week 7 Woah, what just happened? ---- # Project 1 Status Check - Don't forget to fill out this week's survey <!-- .element: class="fragment" --> - Start if you have not done so already. <!-- .element: class="fragment" --> - Final Project has been released. Presentation and project due on last week of class. <!-- .element: class="fragment" --> ---- # Project 2 Release - Find your teammates - Submit a project plan - Meet with a TA next week ---- # Where are we? - Today marks the end of our core Rails narrative <!-- .element: class="fragment" --> ![](https://i.imgur.com/XAew2dg.png) <!-- .element: class="fragment" --> ---- # What's next? - Today we go over convention, Rails Helpers, and Advanced Modeling <!-- .element: class="fragment" --> - Week 8: Gem Showcase <!-- .element: class="fragment" --> - Week 9: Making our Webapps even more powerful <!-- .element: class="fragment" --> - Week 10: Final Project Presentations <!-- .element: class="fragment" --> --- # More on Routes Why can't we just write english instead of code? ---- # Review: what's in a path? ![](https://i.imgur.com/Ewd2GlO.png) ---- # Review: what's in a path? * Let's focus on the *resource* part <!-- .element: class="fragment" --> * The *resource* can include multiple "_segments_" (separated by `/`) <!-- .element: class="fragment" --> * How many segments are in this path? `/students/10/edit` <!-- .element: class="fragment" --> * **Answer:** 3 <!-- .element: class="fragment" --> ---- # Static Segments - So far we've _only seen_ static segments <!-- .element: class="fragment" --> - **Sample Route**: `/students/edit` - `students` and `edit` are static segments of the route (they don't change, for anyone) <!-- .element: class="fragment" --> ---- # Dynamic Segments - Dynamic segments can _be anything_ - they represent placeholders for routes that may pertain to a _particular model_ <!-- .element: class="fragment" --> - **Sample Route**: `/students/:id/edit` - `:id` is a dynamic segment of the route - it determines what `Student` we want to edit here <!-- .element: class="fragment" --> ---- # Example: * Route: `get '/students/:id/edit, to: 'students#edit'` <!-- .element: class="fragment" --> * What are the static segment(s)? **Static Segments**: `students` and `edit` <!-- .element: class="fragment" --> * What are the dynamic segment(s)? **Dynamic Segment**: `:id` <!-- .element: class="fragment" --> * GET Request to `/students/3/edit` would go to `students#edit` with a param `id` with the value 3! <!-- .element: class="fragment" --> * All Dymamic Segments get passed in to `params` <!-- .element: class="fragment" --> ---- # Route Prefixes - We can gave "nicknames" to routes in order to create _helper functions_ <!-- .element: class="fragment" --> - **With only static segments:** ```ruby get '/photos/new', as: photos_new ``` - You can get this path with `photos_new_path` (returns `"/photos/new"`) ---- # Route Prefixes - **With dynamic segments:** ```ruby get '/photos/:id/', to: 'photos#view', as: 'photo_view' ``` - You can get this path, _with a model_, using: `photo_view_path(Photo.first)` (returns what?) - If `Photo.first` had ID 1 - you can think about this like passing in a hash: `photo_view_path(id: 1)` - Returns `/photos/1` <!-- .element: class="fragment" --> ---- # Links, The Hard Way - **Let's say we have the route:** `get '/photos/new', to: 'photos#new'` - If we wanted to create a link to one of these routes, use the link view helper `<%= link_to 'Create a new Photo', '/photos/new', method: :get %>` - What if the route changes? We have to change all references to it! ---- # Links, Made Easier! `get '/photos', to: 'photos#index', as: 'photos'` <!-- .element: class="fragment" --> - We can refer to this route as `photos_path` in views <!-- .element: class="fragment" --> - `<%= link_to 'See all photos', photos_path %>` <!-- .element: class="fragment" --> - Much easier and easier to read as well! <!-- .element: class="fragment" --> ---- # Path Helpers with Dynamic Segments `get '/photos/:id/edit', to: 'photos#edit', as: 'edit_photos'` <!-- .element: class="fragment" --> - This route is named `edit_photos_path` (also, `edit_photos_url`) <!-- .element: class="fragment" --> - If we had an instance of a Photo object, we could get the ID from it and use it in the dynamic segment <!-- .element: class="fragment" --> - `<%= link_to 'Edit Photo', edit_photos_path(@photo) %>` <!-- .element: class="fragment" --> - What is the URL of `edit_photos_path(Photo.first)`? --- # Convention over Configuration Who gives DHH the right? ---- # What we need - 4 core operations on models <!-- .element: class="fragment" --> - Create, Read, Update, and Delete <!-- .element: class="fragment" --> - 4 GET routes to 1) display ALL objects 2) display a single object 3) show a form to create new object 4) show a form to update an object <!-- .element: class="fragment" --> - 3 POST, PUT(or PATCH), and DELETE routes to perform their actions <!-- .element: class="fragment" --> ---- # Ways to do it? - Infinite! <!-- .element: class="fragment" --> - You can literally structure your routes however you want <!-- .element: class="fragment" --> --- #### Rails Doctrine Optimize for programmer happiness <!-- .element: class="fragment" --> Convention over Configuration <!-- .element: class="fragment" --> The menu is omakase <!-- .element: class="fragment" --> No one paradigm <!-- .element: class="fragment" --> Exalt beautiful code <!-- .element: class="fragment" --> Provide sharp knives <!-- .element: class="fragment" --> Value integrated systems <!-- .element: class="fragment" --> Progress over stability <!-- .element: class="fragment" --> Push up a big tent <!-- .element: class="fragment" --> ---- # Convention over Configuration! `resources :students` creates all the routes for an object named `Student` <!-- .element: class="fragment" --> ``` get '/students', to: 'students#index', as: 'students' get '/students/new', to: 'students#new', as: 'new_student' get '/students/:id/edit', to: 'students#edit', as 'edit_student' get '/students/:id', to: 'students#show', as 'student' post '/students', to: 'students#create' put 'students/:id', to: 'students#update' patch 'students/:id', to: 'students#update' delete 'students/:id', to: 'students#destroy' ``` <!-- .element: class="fragment" --> ---- # Resource routing * Let's talk about `resources :students` <table style="font-size: 16pt"> <thead> <tr> <th>HTTP Verb</th> <th>Path</th> <th>Controller#Action</th> <th>Used for</th> </tr> </thead> <tbody> <tr> <td>GET</td> <td>/photos</td> <td>photos#index</td> <td class="fragment">display a list of all photos</td> </tr> <tr> <td>GET</td> <td>/photos/new</td> <td>photos#new</td> <td class="fragment">return an HTML form for creating a new photo</td> </tr> <tr> <td>POST</td> <td>/photos</td> <td>photos#create</td> <td class="fragment">create a new photo</td> </tr> <tr> <td>GET</td> <td>/photos/:id</td> <td>photos#show</td> <td class="fragment">display a specific photo</td> </tr> <tr> <td>GET</td> <td>/photos/:id/edit</td> <td>photos#edit</td> <td class="fragment">return an HTML form for editing a photo</td> </tr> <tr> <td>PATCH/PUT</td> <td>/photos/:id</td> <td>photos#update</td> <td class="fragment">update a specific photo</td> </tr> <tr> <td>DELETE</td> <td>/photos/:id</td> <td>photos#destroy</td> <td class="fragment">delete a specific photo</td> </tr> </tbody> </table> ---- # An Opinionated Controller - index: show all instances of a model <!-- .element: class="fragment" --> - show: show a single instance of a model <!-- .element: class="fragment" --> - edit: show a page with a form to edit a model <!-- .element: class="fragment" --> - new: show a page with a form to create a new model <!-- .element: class="fragment" --> - create: create a new model <!-- .element: class="fragment" --> - update: update an existing model <!-- .element: class="fragment" --> - destroy: delete an existing mode <!-- .element: class="fragment" --> ---- # Flexible Resources - We might not want all 7 actions! <!-- .element: class="fragment" --> - `resources :students, only: [:show, :update, :destroy]` <!-- .element: class="fragment" --> - What routes does this create? <!-- .element: class="fragment" --> - Run `rails routes` to see the routes in your project <!-- .element: class="fragment" --> ---- # Second Look at Rails Generate Scaffold (Demo) ---- # Pros and Cons of Scaffolding? - Pro: Quickly build up something from nothing <!-- .element: class="fragment" --> - Con: A lot of the base code is going to be replaced anyway <!-- .element: class="fragment" --> - You can build your own custom generators to suit your own needs! <!-- .element: class="fragment" --> ---- # Resourceful Routes Peer Instruction ### True or False 1) The `resources` function creates 7 unique routes for us. 2) Rails builds path helpers for each of our routes to help us link to them. 3) `rails routes` will show us all the routes in our app. ---- # Resourceful Routes Peer Instruction 2 ### True or False 1) The `resources` function creates 7 unique routes for us. - True 2) Rails builds path helpers for each of our routes to help us link to them. - False 3) `rails routes` will show us all the routes in our app. - True --- # Advanced Models Join Tables, Self-Joins, and Error-Handling ---- # Review ![](https://i.imgur.com/tTkhyLa.png) ---- # Review Cont. - `customer_id` is a foreign key referring to another tables' id <!-- .element: class="fragment" --> - generated using the `references` type <!-- .element: class="fragment" --> - `belongs_to` and `has_many` declarations in the model class create helper functions! <!-- .element: class="fragment" --> ---- # Many to Many Relationships - What if we wanted to model a school. Two models: Professors and Expertises <!-- .element: class="fragment" --> - How do we represent this in tables?? <!-- .element: class="fragment" --> ---- # Join Tables! ![](https://i.imgur.com/2zMwTJE.png) <!-- .element: class="fragment" --> - Create a middle man! <!-- .element: class="fragment" --> ---- # Middle Man! - `expertises_professors` keeps track of pairs of professors and expertises <!-- .element: class="fragment" --> - What does each entry in the table represent? <!-- .element: class="fragment" --> - A single expertise that a single professor has <!-- .element: class="fragment" --> - If professor Denero had 5 expertises, there would be 5 entries in the expertises_professors table <!-- .element: class="fragment" --> ---- # How do we do this in Rails? `rails g migration CreateJoinTableProfessorsExpertises professor expertise` <!-- .element: class="fragment" --> - Creates migration to create a join table between two existing tables <!-- .element: class="fragment" --> - Let's try it! <!-- .element: class="fragment" --> ---- # Model Helpers - Two options for model helper <!-- .element: class="fragment" --> - Professor.rb: <!-- .element: class="fragment" --> - `has_many :expertises, through: :professor_expertise` <!-- .element: class="fragment" --> - `has_and_belongs_to_many :expertises` <!-- .element: class="fragment" --> ---- # Self Associations - Consider Employees and Managers <!-- .element: class="fragment" --> - Employees belongs_to a manager, but managers are also employee objects... <!-- .element: class="fragment" --> ``` class Employee < ActiveRecord::Base has_many :subordinates, class_name: "Employee", foreign_key: "employee_id" belongs_to :manager, class_name: "Employee", foreign_key: "employee_id", optional: true end ``` <!-- .element: class="fragment" --> ---- # Self Joins - Facebook! Users have a many-to-many relationship to themselves: "Friends"! <!-- .element: class="fragment" --> - Create a join table called "Friendship" <!-- .element: class="fragment" --> ``` class CreateFriendships < ActiveRecord::Migration def change create_table :friendships do |t| t.references :from_user, index: true t.references :to_user, index: true t.timestamps null: false end add_foreign_key :friendships, :users, column: :to_user_id add_foreign_key :friendships, :users, column: :from_user_id end end ``` <!-- .element: class="fragment" --> ---- # Model Helpers ``` class Friendship < ApplicationRecord belongs_to :from_user, class_name: "User" belongs_to :to_user, class_name: "User" end ``` <!-- .element: class="fragment" --> ``` class User < ApplicationRecord has_many :friendships, foreign_key: :from_user_id has_many :from_users, through: :friendships has_many :to_users, through: :friendships def friends from_users end end ``` <!-- .element: class="fragment" --> ---- # Lots of possibilities - Don't stress about memorizing how to do each of these <!-- .element: class="fragment" --> - Focus on the idea so that you can google the "how" when you need it <!-- .element: class="fragment" --> ---- # Validation Error Checking - If you try to save a model that doesn't match it's validations, it won't save! <!-- .element: class="fragment" --> - No crashes! That's nice...but how do we know what happened? <!-- .element: class="fragment" --> ``` a = Artist.create a.errors ``` <!-- .element: class="fragment" --> --- # In-Class Lab! https://your.rails.world ---- ## Don't forget attendance