Rails

We’re talking a bit about Ruby on Rails in the final part of this class. This framework promises the world, let’s see what happens.

just following along with the lecture…

routing basics

Routing is defined in ./config/routes.rb. we went to app/controllers/application_controller.rb and changed it to

class ApplicationController < ActionController:
  def hello
    render  'hello world'
  end
end

then in the routes

get "application/hello"

this means:

other routes

get "foo",  "application#hello"

routes <host>/foo to the same place

root  "application#main"

routes <host>/ to the same place

g

rails g controller staticPage home help; short for rails generate

prints

      create  app/controllers/static_page_controller.rb
       route  get "static_page/home"
              get "static_page/help"
      invoke  erb
      create    app/views/static_page
      create    app/views/static_page/home.html.erb
      create    app/views/static_page/help.html.erb
      invoke  test_unit
      create    test/controllers/static_page_controller_test.rb
      invoke  helper
      create    app/helpers/static_page_helper.rb
      invoke    test_unit

we codegen all these files

interesting is that the controller looks like this:

class StaticPageController < ApplicationController
  def home
  end

  def help
  end
end

but visiting <host>/static_page/about does indeed render the view in app/views/static_page/home.html.erb

main template

views/layouts/application.html.erb contains the html framework, with a yield for the slot accepting the page-specific template

this corresponds to ApplicationController? yes. i don’t know the exact mechanism by which these are plugged in together - maybe it’s because StaticPageController extends ApplicationController?

yes indeed - changing StaticPageController to extend ActionController::Base causes no outer template to be rendered

slotting things into the outer template

running this typea thing in the inner template <% provide(:title, "lalskhdjashkasd") %> causes :title to hold the value lalskhdjashkasd

TODO: surely there’s also a way to do this from the controller? calling the same function from controllers/static_page_controller.rb doesn’t do anything.

<%= link_to "link text", "url" %> can be used from a template. for example you could build a nav by slapping a few of these in controllers/application.html.erb.

the second parameter can be a relative url fragment, or some magical variable called asd_path where asd is … something in the routing table? like if you have get "home", "something#home" then the variable home_path is defined

this seems kind of silly, because the variable home_path just contains the value “/home”

partials

partials can be put in app/views/layouts/, they start with an underscore. so maybe layouts/header.html.erb

invoke the partial with <%= render "layouts/header" %>. no underscore! dont forget the <%= since you do want to output it right.

data

rails db:create makes a database file, it’s stored in storage/xxxx.sqlite3

you don’t describe the shape of the database directly, but you describe how to change the existing database into the one you want. this allows you to go forward and back in time.

rails generate migration helps you make a migration file. there are other arguments to append which set the name and prefill some bits of the migration (todo look those up). the current datetime is prepended to the file, so that rails db:migrate can run the migrations in order.

theyve got activerecord ORM stuff so you dont write too much sql directly. thats the idea anyway.

databases

i took a databases class before but yeah the basics

associations can have like, “on delete” actions

class Student < ApplicationRecord
  student belongs_to 
end

class Team < ApplicationRecord
  team has_many 
end

alright whats in this next class

class User < ApplicationRecord
(..something...) password  has_secure_password

can add the bcrypt gem like gem "bcrypt", "~> 3.1.7"

rails g migration add_pasword_to_users password_digest:string

there was a little script with faker to add fake users for testing

another class whatever

rails generate migration add_password_to_users password_digest:string

rails generate controller user index new show edit

typical crud apis:

these notes are a mess

let’s start over

bigger picture

rails is a model-view-controller framework

probably the most important thing: methods in the controller configure the view basically. when you set instance-variables (@var) you can read @var from the erb template

basically dhh likes the word Active like how mr melon musk likes the letter x

routing

https://guides.rubyonrails.org/routing.html

the most general syntax:

get "/users/:id",  "users",  

get -> the HTTP verb, there are ruby methods for each verb.

controller -> the controller to pass this to. this corresponds to some file in the controllers directory. (if there’s more than one word, you need to snake_case it)

show -> the method to call inside the controller. this is a symbol

the routing file is interpreted top-to-bottom and the first matching rule will work

“to” shorthand

you can glue controller to action by using to instead

get "/users/:id",  `users#show`

“resources” shorthand

this set of eight routes

get    "/photos"         ,  "photos#index"   # list photos
get    "/photos/new"     ,  "photos#new"     # form for making photo
post   "/photos"         ,  "photos#create"  # endpoint for making photo
get    "/photos/:id"     ,  "photos#show"    # show photo
get    "/photos/:id/edit",  "photos#edit"    # form for editing photo
patch  "/photos/:id"     ,  "photos#update"  # endpoint for editing photo
put    "/photos/:id"     ,  "photos#update"  # endpoint for editing photo (2)
delete "/photos/:id"     ,  "photos#destroy" # delete photo

can be abbreviated simply as

resources 

if you only want a few of these, use only:. for example

resources ,  [, , , ]

and if you want to move it, use path:.

resources ,  "/some/path/things`

“resource” shorthand

afaik this is basically resources :thing but doesn’t include :index and with different rules about pluralization

path helpers

every route comes with two “route helpers”, global variables that contain paths to the route

for example

get "/foo",  "foo#index"

defines

also, somehow this works (this is from the example)

get "/users/:id",  "users#show",  "user"

#...

<%= link_to 'User Record', user_path(@user) %>

(as gives an explicit name for the route helpers)

so this time user_path and user_url are functions that take a User object and presumably plucks out its id. user_path is like a way of saying “url to this user”. imagine you’re emitting, like, a comments section and want to link to each user: yeah.

if you want to make your own url helpers, use direct :helper_name do and return a url_forrable thing in there (string, hash, array, activemodel instance or class)

from the guide.

direct  do
  "https://rubyonrails.org"
end

# >> homepage_url
# => "https://rubyonrails.org"

listing routes

the rails routes command will parse your routes table and list them all

namespaces

namespace :admin do
  resources :articles

this puts the articles routes under /admin, i.e. /admin/articles/new

the controller is also namespaced. it will look for Admin::ArticlesController

nested routes

now we’re at the good stuff

if you have two models

class Post < ApplicationRecord
  has_many 
end

class Comment < ApplicationRecord
  belongs_to 
end

then this makes sense

resources  do
  resources 
end

this creates routes for posts and also creates routes for comments. for example get /posts/:post_id/comments/:comment_id/edit. the url helpers take a Post and a Comment record

you can nest routes as deeply as you like but it does kinda become a mess. the right amount of nesting depends on what you want the urls to look like.

optional segments

get "photos(/:id)", to: "photos#display" routes both /photos/:id and /photos to photos#display. this controller should handle the case where the parameter is not present

wildcard segments

get "photos/*id", the id can contain slashes.

routing-table level redirections

get "/stories", to: redirect("/articles")

this serves a 301 by default. you can serve a 302 like this

get "/stories", to: redirect("/articles", status: 302)

“Rack applications”

“Rack” is a ruby standard for web server middleware. a “rack application” is anything with a function call that returns [status, headers, body].

you can specify a rack application in to instead of a string. The guide mentions that when rails sees a string in to (say posts#index), it expands that to PostsController.action(:index) and that is a rack application. probably somewhere in rails machinery

root

root  "posts#index"
root "posts#index" # same thing

i will split these off again

how does a “creation” actually work from the user’s end?

how does a deletion actually work?

a modification?

n.b. HTTP only supports GET and POST but ruby adds a hidden field to forms for the “real” verb. the server receives a POST but routes it according to the route in the hidden field

the :resources action pack gives you all these forms and all the endpoints. (if u want you can add more routes on a specific member or on the whole collection)



on request types:

these arent enforced in http. they’re a convention


“two problems with routes”