My beloved Ruby on Rails cheat sheet
Tags: ruby, rails, beginners
Published: 2021-02-08 01:29:33

---
title: My beloved Ruby on Rails cheat sheet
published: true
description: 
tags: ruby, rails, beginner
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/vfxt3x3unfv9mxc7kyhj.jpeg
---
Follow me!: <a href="https://twitter.com/EricTheCoder_" class="twitter-follow-button" data-show-count="false">Follow @EricTheCoder_</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<hr>
Rails CLI
```ruby

# Create a new rails app
$ rails new project_name 

# Start the Rails server
$ rails s

# Rails console
$ rails c

# Install dependencies
$ bundle install

# View all routes
$ rails routes

# Toggle rails caching
$ rails dev:cache

```

Rails generators CLI
```ruby
# CRUD Scaffold (model, migrations, controller, views, test)
rails g scaffold Product name:string price:decimal

# CRUD Scaffold with one to many relationship field
rails g scaffold Invoice customer:references

# Delete scaffold created files
rails destroy scaffold Product

# Controller (name, action1, action2, ...)
$ rails g controller Products index show

# Model, migration and table columns
$ rails g model Product name:string active:boolean

```

Migration
```ruby
# Create new table migration
rails g migration Invoices

# Update existing table migration
rails g migration add_comment_to_invoices comment:text

# Run migration
$ rails db:migrate

# Rollback last migration
$ rails db:rollback

# Run database seed code
$ rails db:seed

# Delete and re-create db and run migrations
$ rails db:reset

# Create table migration exemple
create_table :products do |t|
  t.string :name
  t.decimal :price, precision: 8, scale: 2
  t.timestamps
end

# Create table with foreign key
create_table :invoices do |t|
  # null: false = dont allow null value in this db field
  t.references :customer, null: false, foreign_key: true
  t.timestamps
end

# Change table migration exemple
add_column :invoices, :comment, :text


```

Routes
```ruby

# Route maps to controller#action
get 'welcome', to: 'pages#home'

# Root page (root_path name helper)
root 'pages#home' 

# Named route 
get 'exit', to: 'sessions#destroy', as: :logout

# Create all the routes for a RESTful resource
resources :items

# HTTP    Verb Path	   Controller#Action  Named Helper
# GET	  /items	       items#index	  items_path
# GET	  /items/new	   items#new	  new_item_path
# POST    /items	       items#create   items_path
# GET	  /items/:id	   items#show	  item_path(:id)
# GET	  /items/:id/edit  items#edit	  edit_item_path(:id)
# PUT	  /items/:id	   items#update   item_path(:id)
# DELETE  /items/:id	   items#destroy  item_path(:id)

# Only for certain actions
resources :items, only: :index

# Resource with exceptions 
resources :items, except: [:new, :create]

# Nested resources
resources :items do
  resources :reviews
end
# create 7 RESTful for items and reviews 
# :reviews will have /items/item:id prefixing each routes
# GET /items/:item_id/reviews  reviews#index  item_reviews_path 

# Dynamic segment: params['id']
get 'products/:id', to: 'products#show'
# Query String: url /products/1?user_id=2
# params will be {'id' 'user_id'}

# Namespace Admin::ArticleController
# and prefix '/admin'
namespace :admin do
  resources :articles
end

# only prefix '/admin'
scope '/admin' do
  resources :articles, :comments
end

# Redirect
get '/stories', to: redirect('/articles')



```


Model
```ruby
# Model validation
validates :title, :description, :image_url, presence: true
validates :email, presence: true, format: { with: /\A[^@\s]+@[^@\s]+\z/, message: 'Must be a valid email address'}
validates :price, numericality: { greater_than_equal_to: 0.01 }
validates :title, uniqueness:  true
validates :title, length: { minimum: 3, maximum: 100 }
validates :type, inclusion: types.keys

# Model relationship
belongs_to :customer

# Relation with cascade delete
has_many :invoices, dependent: :destroy

#One to one
has_one :profile

# Hook methods 
before_destroy :ensure_not_reference_by_any_invoices 
before_save :downcase_email 

# create virtual password and password_confirmation 
# and bcrypt password_digest
# add method to model: user.authenticate(params[:password])
has_secure_password

```
Controllers
```ruby
# Hook before running any code
before_action :set_post, only: [:show, :edit, :update, :destroy]

# If you use Devise (authentification)
before_action :authenticate_user!

# 7 Restfull action short exemple 
def index
  # Search input name :q
  if session[:q].present?
    params[:page] = 1
    # Create instance variable @
    @posts = Post.where "title like ?", "%" + session[:q] + "%"
  else
    @posts = Post.all
  end
  @posts = @posts.order("created_at DESC")
  # Pagination with will_paginate gem
  @posts = @posts.paginate(page: params[:page], per_page: 3)
  session[:q] = nil
end

# By convention all action (even empty one)
# run hook before_action and render view template
def show
end

def new
  @post = Post.new
end

def edit
end

def create
  @post = Post.new(post_params)
    if @post.save
      redirect_to @post, notice: 'Created successfully!'
    else
      render :new 
    end
end

def update
  if @post.update(post_params)
      redirect_to @post, notice: 'Updated successfully!'
    else
      render :edit 
    end
end

  def destroy
    @post.destroy
    redirect_to posts_url, notice: 'Delete successfully!' 
  end

private
  # Use callbacks to share common methods between actions.
  def set_post
    @post = Post.find(params[:id])
  end

  # Only allow a list of trusted parameters through.
  def post_params
    params.require(:post).permit(:title, :body, :image_url)
  end
end

# render text
render plain: 'Hello User'

```

Active Record
```ruby
# Active record common methods
Article.all
# Throw error if not found
Article.find(params[:id])
# Do not throw error if not found
Article.find_by(product_id: product_id)
@category = Category.find_by!(slug: params['slug']) # Return Not Found Error (404 page in production)
Article.group(:product_id).sum(:quantity)
Article.where('quantity > 1')
Article.where(cat_id: cat_id, model: model)
Article.where(model: model).or(Article.where(cat_id: cat_id))
Article.join(:categories).where(categories: { id: 2 } )
Article.where("title LIKE ?", "%" + params[:q] + "%")
Article.count
Article.first
Article.last
Article.column_names # ['id', 'name', 'price']
Category.delete_all # delete all rows in Category table
product.category = Category.all.sample # random for Faker data
@products = Product.offset(5).limit(10).all # skip 5, take 10
```

Template
```erb
<%# Comment tag %>

<%# Output return expression tag %>
<%= @user.name %>

<%# No output return expression tag %>
<% if @user.name == 'Mike' %>

<%# Layout file : app/view/layouts/application.html.erb %>

<%# In layout file replace expression with page content %>
<%= yield %>

<%# View for the route name helper path %>
<%= link_to 'About', about_path, class: 'nav-link' %>

<%# View for the route name helper path with params %>
<%= link_to 'Item record', item_path(@item) %>

<%# auto object to route map (/products/:id) %>
<%= link_to 'Show', @product %>

<%# Delete link %>
<%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %>

<%# Post link %>
<%= button_to 'Add to Cart', line_items_path(product_id: product) %>

# link_to with slot
<%= link_to category_path do %>
  <%= @product.category.name %>
<% end %>

<%# Button form %>
<%= button_to 'Logout', logout_path, method: :delete %>

<%# Image asset (app/assets/images) %>
<%= image_tag "rails.png" %>

<%# Format currency %>
<%= number_to_currency(product.price) %>

<%# Safe Html render %>
<%= sanitize(product.description) %>

<%# Enable caching %>
<%= cache @products do %>

<%# Check url %>
<%= request.path.include?('post') ? 'active' : '' %>">

<%# Check current page %>
<%= current_page?('about') ? 'active' : '' %>">

<%# Render shared partial _navbar.html.erb %>
<%= render partial: 'shared/navbar' %>

<%# Render partial _form.html.erb %>
<%= render 'form', product: @product %>

<%# data tables iterations %>
<% @users.each do |user| %> <% end %>

<%# Render a for each _user.html.erb %>
<%= render @users %> 

<%# Conditional %>
<% if current_user.signed_in? %> 
  ...
<% else %> 
  ...
<% end %>

<%# Display errors %>
<% if @post.errors.any? %> 
<% if form.object.errors.any? %>

<% if @post.errors.empty? %>
<% @post.errors.full_messages.each do |message| %>
  <p class='error'><%= message %></p>
<%= user.errors.count %>
<%= post.errors[:description] %>

<%# Flash messages %>
<% flash.each do |msg_type, msg| %>
  <div class="alert alert-<%= msg_type %>">
    <%= msg %>
  </div>
<% end %>

```

Form
```erb
<%# Model form %>
<%= form_with(model: product), local: true do |form| %>
  <%= form.label :title %>
  <%= form.text_field :title, class: 'form-control' %>

  <%= form.submit %>
<% end %>

<%# Generic form %>
<%= form_with url: "/search", method: :get, local: true do |form| %>
  <%= form.label :query, "Search for:" %>
  <%= form.text_field :query, placeholder: 'search' %>
  <%= form.submit "Search", class: 'btn btn-primary' %>
<% end %>

<%# Multi-lines memo %>
<%= form.text_area :description, rows: 10, cols: 60 %>

<%# Collection Select (field, collection, key, value, label) %>
<%= form.collection_select :category_id, Category.all, :id, :name, {include_blank: '- Select a Category -'}, {class: 'form-select'}

<%# Select %>
<%= form.select :type, Customer.types.keys, prompt: 'Select a type' %>
<%= form.select :rating, (1..5) %>

<%# if form in new or edit mode change submit text %> 
<%= form.submit @product.new_record? ? 'Create' : 'Update', class: "mt-4 btn btn-primary" %>


```
Flash, Session and Cookie
```ruby
# Create flash (reset every new request)
flash[:success] = 'User created with success!'

# Create flash.now (reset every new view render)
flash.now[:error] = 'Please select s user!'

# Create session (reset every browser close)
session[:user_id] = user.id

# Check if session exist
session[:user_id].nil?

# Remove
session.delete(:user_id)

# Remove all
reset_session      

# Create cookie (reset at expiration date)
cookies.permanent[:remember_token] = remember_token

# Encrypted cookie
cookies.permanent.encrypted[:user_id] = user.id

# Delete cookie
cookies.delete(:user_id)
```

Database seed with faker
```ruby
# bundle install 
$ gem 'faker' 

# db/seeds.rb
Customer.delete_all
10.times do |n|
    customer = Customer.new
    customer.name = Faker::Name.name
    customer.phone = Faker::PhoneNumber.phone_number
    customer.save
end

# Run seed script
$ rails db:seed

```
Devise Gem (authentification)
```ruby
# Install
gem devise
bundle install
rails g devise:install # follow on-screen instruction
rails generate devise User
rails db:migrate

# Customize devise login, register, etc. views
rails generate devise:views

# Add in existing controllers
before_action :authenticate_user!

# User shared globals
user_signed_in?
current_user

# Logout link_to exemple
<%= link_to "Logout", destroy_user_session_path, method: :delete %>

```
Trix Editor
```ruby
gem ‘image_processing'
rails action_text:install
bundle install
rails db:migrate

# Add to model class 
has_rich_text :body 

# Template use
<%= form.rich.text_area %>

# actiontext.scss (if using boostrap only)
trix-editor { 
  &.form-control { 
    height: auto; 
  } 
} 

```

MISC
```ruby
# Edit credentials encrypted list (API keys, secrets, etc.)
$ EDITOR="code --wait" bin/rails credentials:edit --environment=development

```
Clone a Rails project from Github
```ruby
$ git clone https://github/myprojet…
$ cd projet_name
$ bundle install
$ yarn install --check-files
$ rails dev:cache # not always needed
$ rails db:migrate
$ rails db:seed
$ rails s
```

Heroku deployment
```ruby

# Change Gemfile production database to Postgress
group :development do
 gem 'sqlite3'
end

group :production do
  gem 'pg'
end

# Create git and push to Heroku
$ heroku create
$ git remote
$ bundle install --without production
$ git status
$ git add -A
$ git commit -m 'heroku deployment'
$ git push origin master
$ git push heroku master
$ heroku run rails db:migrate
$ heroku run rails db:seed

# launch the upload site
$ heroku open 

```