Setup instructions for Le Wagon's students on their first day of Web Development Bootcamp
Reference: https://github.com/lewagon/setup
Jump start your Rails development with Le Wagon best practices
$ rails new \
--database postgresql \
--webpack \
-m https://raw.githubusercontent.com/appwebd/rails-templates/master/devise.rb \
CHANGE_THIS_TO_YOUR_RAILS_APP_NAME
Reference: https://github.com/lewagon/rails-templates
Reference: https://github.com/appwebd/rails-templates
Generate a model and create a migration for the table (files in folder app/models/)
Note: Name models in Rails is Capital case and singular
$ rails g model Movie title:string description:text photo user:references
The migration automatically created for the above command:
class CreateMovies < ActiveRecord::Migration[6.1]
def change
create_table :movies do |t|
t.string :title
t.text :description
t.string :photo
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
Migrate of table to database
$ rails db:migrate
Reference: http://guides.rubyonrails.org/active_model_basics.html
Migration Data Types
:boolean
:date
:datetime
:decimal
:float
:integer
:primary_key
:references
:string
:text
:time
:timestamp
When the name of the migration follows the format AddXXXToYYY
followed by a list of columns, it will add those columns to the existing table
$ rails g migration AddDateTakenToMovies date_taken:datetime
The above creates the following migration:
class AddDateTakenToMovies < ActiveRecord::Migration[5.0]
def change
add_column :movies, :date_taken, :datetime
end
end
You can also add a new column to a table with an index
$ rails g migration AddDateTakenToMovies date_taken:datetime:index
The above command generates the following migration:
class AddDateTakenToMovies < ActiveRecord::Migration[5.0]
def change
add_column :movies, :date_taken, :datetime
add_index :movies, :date_taken
end
end
The opposite goes for migration names following the format: RemoveXXXFromYYY
$ rails g migration RemoveDateTakenFromPhotos date_taken:datetime
The above generates the following migration:
class RemoveDateTakenFromPhotos < ActiveRecord::Migration[5.0]
def change
remove_column :photos, :date_taken, :datetime
end
end
$ rails console
rails c
Running via Spring preloader in process 23453
Loading development environment (Rails 6.1.4.1)
[1] pry(main)>
Movie.new
=> #<Movie:0x00007ff0ccb7cbf8
id: nil,
title: nil,
description: nil,
photo: nil,
user_id: nil,
created_at: nil,
updated_at: nil>
[2] pry(main)>
movie_1 = Movie.create description: "Testing"
TRANSACTION (0.2ms) BEGIN
Movie Exists? (0.4ms) SELECT 1 AS one FROM "movies" WHERE "movies"."title" IS NULL LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.2ms) ROLLBACK
=> #<Movie:0x00007ff0ccaa3bf0
id: nil,
title: nil,
description: "Testing",
photo: nil,
user_id: nil,
created_at: nil,
updated_at: nil>
[3] pry(main)>
movie_1.valid?
Movie Exists? (0.5ms) SELECT 1 AS one FROM "movies" WHERE "movies"."title" IS NULL LIMIT $1 [["LIMIT", 1]]
=> false
[4] pry(main)>
movie_1.errors.messages
=> {:user=>["must exist"], :title=>["can't be blank"]}
[5] pry(main)>
A doctor has many interns
Migration to create doctors
$ rails g model Doctors first_name last_name
# db/migrate/20141027100300_create_doctors.rb
class CreateDoctors < ActiveRecord::Migration[6.0]
def change
create_table :doctors do |t|
t.string :first_name
t.string :last_name
t.timestamps
end
end
end
Migration to create interns
$ rails g model Interns first_name last_name doctor:references
# db/migrate/20141027100400_create_interns.rb
class CreateInterns < ActiveRecord::Migration[6.0]
def change
create_table :interns do |t|
t.string :first_name
t.string :last_name
t.references :doctor, foreign_key: true
t.timestamps
end
end
Note the t.references
Always add foreign_key: true to enforce referential integrity
Create 2 models
# app/models/doctor.rb
class Doctor < ActiveRecord::Base
has_many :interns
end
# app/models/intern.rb
class Intern < ActiveRecord::Base
belongs_to :doctor
end
Read it:
- A Doctor has_many interns
- An Intern belongs_to a doctor
Create interns
doctor = Doctor.new(first_name: "Gregory", last_name: "House")
doctor.save
intern = Intern.new(first_name: "Allison", last_name: "Cameron")
intern.doctor = doctor
intern.save
Retrieve interns for a given doctor:
doctor = Doctor.last
doctor.interns
# => `Array` (`ActiveRecord::Relation`) of `Intern` instances
Let’s add patients / consultations table
Migration to create patients
$ rails g model Patients first_name last_name
# db/migrate/20141027114700_create_patients.rb
class CreatePatients < ActiveRecord::Migration[6.0]
def change
create_table :patients do |t|
t.string :first_name
t.string :last_name
t.timestamps
end
end
end
Migration to create consultations
$ rails g model Consultations doctor:reference patient:reference
# db/migrate/20141027114800_create_consultations.rb
class CreateConsultations < ActiveRecord::Migration[6.0]
def change
create_table :consultations do |t|
t.references :doctor, foreign_key: true
t.references :patient, foreign_key: true
t.timestamps
end
end
end
Create 2 new models
# app/models/patient.rb
class Patient < ActiveRecord::Base
has_many :consultations
end
# app/models/consultation.rb
class Consultation < ActiveRecord::Base
belongs_to :patient
belongs_to :doctor
end
What about the Doctor model?
# app/models/doctor.rb
class Doctor < ActiveRecord::Base
has_many :interns
# Do not forget to add this! :)
has_many :consultations
end
How can I retrieve patients for a given doctor?
doctor = Doctor.last
patients = []
doctor.consultations.each do |consultation|
patient = consultation.patient
patients << patient if !patients.include?(patient)
end
# => `Array` of `Patient` instances
Use :through
# app/models/doctor.rb
class Doctor < ActiveRecord::Base
has_many :interns
has_many :consultations
has_many :patients, through: :consultations
end
Then you can do:
doctor = Doctor.last
doctor.patients
# => `Array` (`ActiveRecord::Relation`) of `Patient` instance
Reference: [https://guides.rubyonrails.org/active_record_validations.html](Active record validations)
Reference: [https://guides.rubyonrails.org/active_record_callbacks.html#available-callbacks] (Active Record Callbacks)
First, install the faker gem by adding it to your list of gems in your gemfile.
# for finding the version number
gem search faker | grep '^faker ('
file: Gemfile
----
gem 'faker'
---
# finally, do the following command in the DCL (DCL: Definition Command Language)
bundle install
In “db/seeds.rb”, create some movies:
# URL faker github page: https://github.com/faker-ruby/faker
require 'faker'
puts 'Removing all the database information...'
User.destroy_all
Movie.destroy all
puts 'OK database is empty'
puts "Generating 20 users"
# generate 10 users
(1..10).each do |id|
User.create!(
# each user is assigned an id from 1-10
id: id,
name: Faker::Name.name,
email: Faker::Internet.email,
# issue each user the same password
password: "password",
password_confirmation: "password",
# a user can have only one of these roles
role: %w[customer admin agent].sample
)
end
puts 'Creating a new movies ...'
url = "http://tmdb.lewagon.com/movie/top_rated"
10.times do |i|
puts "Importing movies from page #{i + 1}"
movies = JSON.parse(open("#{url}?page=#{i + 1}").read)['results']
movies.each do |movie|
puts "Creating #{movie['title']}"
base_poster_url = "https://image.tmdb.org/t/p/original"
Movie.create(
title: movie['title'],
overview: movie['overview'],
poster_url: "#{base_poster_url}#{movie['backdrop_path']}",
rating: movie['vote_average']
)
end
end
puts 'Was done (all the seed information was created)'
Reference: https://github.com/faker-ruby/faker
Create a route that maps a URL to the controller action
# config/routes.rb
get 'welcome' => 'pages#home'
Shorthand for connecting a route to a controller/action
# config/routes.rb
get 'movies/show'
# The above is the same as:
get 'movies/show', :to 'movies#show'
get 'movies/show' => 'movies#show'
Automagically create all the routes for a RESTful resource
# config/routes.rb
resources :movies
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /movies | movies#index | display a list of all movies |
GET | /movies_new | movies#new | return an HTML form for creating a new movie |
POST | /movies | movies#create | create a new movie |
GET | /movies/:id | movies#show | display a specific movie |
GET | /movies/:id/edit | movies#edit | return an HTML form for editing a movie |
PATCH/PUT | /movies/:id | movies#update | update a specific movie |
DELETE | /movies/:id | movies#destroy | delete a specific movie |
Create resources for only certain actions
# config/routes.rb
resources :movies, :only => [:index]
# On the flip side, you can create a resource with exceptions
resources :movies, :except => [:new, :create, :edit, :update, :show, :destroy]
Create a route to a static view, without an action in the controller
# config/routes.rb
# If there's a file called 'about.html.erb' in 'app/views/movies', this file will be
# automatically rendered when you call localhost:3000/photos/about
get 'movies/about', to: 'movies#about'
you can do in the console:
rails routes
Filtering movies routes
$ rails routes | grep movies
movies GET /movies(.:format) movies#index
POST /movies(.:format) movies#create
new_movie GET /movies/new(.:format) movies#new
edit_movie GET /movies/:id/edit(.:format) movies#edit
movie GET /movies/:id(.:format) movies#show
PATCH /movies/:id(.:format) movies#update
PUT /movies/:id(.:format) movies#update
DELETE /movies/:id(.:format) movies#destroy
$
Reference: http://guides.rubyonrails.org/routing.html
Generate a new controller
Note: Name controllers in Ruby on Rails are pluralized and capital case.
$ rails g controller Movies
Generate a new controller with default actions, routes and views.
$ rails g controller Movies index show
Reference: http://guides.rubyonrails.org/action_controller_overview.html
Scaffolding is great for prototypes but don't rely too heavily on it: http://stackoverflow.com/a/25140503
$ rails g scaffold Movie path:string caption:text
$ rake db:migrate
View all the routes in an application
$ rake routes
Seed the database with sample data from db/seeds.rb
$ rake db:seed
Run any pending migrations
$ rake db:migrate
Rollback the last migration performed
NOTE: Be VERY careful with this command in production, it's destructive and you could potentially lose data. Make sure you absolutely understand what will happen when you run it
$ rake db:rollback
Creating a path helper for a route
# Creating a path helper for a route
get '/movies/:id', to: 'movies#show', as: 'movies'
# app/controllers/movies_controller.rb
@movie = Movie.find(17)
# View for the action
<%= link_to 'Movie Record', movie_path(@movie) %>
Path helpers are automatically created when specifying a resource in config/routes.rb
# config/routes.rb
resources :movies
HTTP Verb | Path | Controller#Action | Named Helper |
---|---|---|---|
GET | /movies | movies#index | movies_path |
GET | /movies/new | movies#new | new_movie_path |
POST | /movies | movies#create | movies_path |
GET | /movies/:id | movies#show | movie_path(:id) |
GET | /movies/:id/edit | movies#edit | edit_movie_path(:id) |
PATCH/PUT | /movies/:id | movies#update | movie_path(:id) |
DELETE | /movies/:id | movies#destroy | movie_path(:id) |
Access images in the app/assets/images
directory like this:
<%= image_tag "rails.png" %>
Within views, link to JavaScript and CSS assets
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
<!-- Filenames are fingerprinted for cache busting -->
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen"
rel="stylesheet" />
<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script>
Reference: http://guides.rubyonrails.org/asset_pipeline.html
Bind a form to a model for creating/updating a resource
Use this method if you're using strong params to protect against mass assignment
# app/controllers/photos_controller.rb
def new
@movie = Movie.new
end
# ERB view
<%= form_for @movie, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
<%= f.text_field :path %>
<%= f.text_area :caption, size: "60x12" %>
<%= f.submit "Create" %>
<% end %>
<!-- HTML output -->
<form accept-charset="UTF-8" action="/movie/create" method="post" class="nifty_form">
<input id="movies_path" name="movie[path]" type="text" />
<textarea id="movies_caption" name="movie[caption]" cols="60" rows="12"></textarea>
<input name="commit" type="submit" value="Create" />
</form>
Create a form with a custom action and method
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
<form accept-charset="UTF-8" action="/search" method="get">
<input name="utf8" type="hidden" value="✓" />
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
<input name="commit" type="submit" value="Search" />
</form>
Reference: http://guides.rubyonrails.org/form_helpers.html
Reference: https://uikit.lewagon.com/documentation
Reference: https://getbootstrap.com/docs/4.6/getting-started/introduction/
Reference: https://get.foundation/sites/docs/
Reference: https://tailwindcss.com/docs
Reference: https://bulma.io/documentation/
Reference: [https://www.ruby-lang.org/en/documentation/] (Docuimentation in English)
Reference: [https://www.ruby-lang.org/es/documentation/] (Docuimentation in spanish)
Reference: [https://docs.ruby-lang.org/en/] (Documentation for Ruby classes and methods)
Reference: [https://guides.rubyonrails.org/](Ruby on Rails Guides)
Reference: [https://rubygems.org/] (Ruby Gems)
Reference: [https://github.com/heartcombo/devise] (Devise An authentication gem for Rails)
Reference: [https://github.com/tigrish/devise-i18n](devise-i18n github)
Reference: [https://github.com/varvet/pundit](pundit github)
Reference: https://www.rubydoc.info/gems/pundit/0.3.0