Rails
Guides
http://guides.rubyonrails.org/
Primary
Secondary
- Active Support
- Active Model
- Asset Pipeline
- Autoloading
- Command Line
- Configuration
- Debugging
- Engines
- Generators
- Initialization
- Internationalization
- JavaScript
- Jobs
- Mailers
- Plugins
- Security
- Rack
New With a Specific Rails Version
$ rails _6.0.3.7_ new …
Generating Models
rails generate model Name field:type
rails generate scaffold Name field:type
- :primary_key - note that an
id:integerprimary key column is added by default if this isn't specified - :string - i.e. of limited short length
- :text - i.e. of indeterminate length
- :integer
- :bigint
- :float
- :decimal
- :numeric
- :datetime
- :time
- :date
- :binary
- :boolean
- :references or :belongs_to - creates an ID column referencing another model table by name, along with a foreign key constraint
Suffixes
:uniq- unique index:index- non-unique index
Create Table Migrations
Whether a create_table migration is generated or written by hand, column modifiers can be added to the t.column lines to further configure the column creation. The most common are:
- default: the default value
- index: whether to create an index for the column
- limit: max characters
- null: whether the column is nullable
Migrations
Writing
def change
create_table :tablename do |t|
t.string :field, null: false, default: ''
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
add_column :todos, :completed_at, :datetime, precision: 6
remove_column :ideas, :quote, :text
add_reference :todos, :category, type: :uuid, null: true, foreign_key: true
add_index :users, :email, unique: true
create_join_table :sources, :tags do |t|
t.index :source_id
t.index :tag_id
end
Running
rails db:setup
rails db:reset
rails db:migrate
rails db:rollback [STEP=N] [VERSION=0]
Modifying Columns
Outside of a create_table migration, columns on existing tables can be added, removed, or changed.
- Change type:
change_column - Change nullability:
change_column_null - Change default value:
change_column_default add_foreign_keyremove_foreign_keyadd_indexremove_index
Models
belongs_to :model
has_many :models
validates :field, presence: true
Model.create(attributes) # Boolean
Model.create!(attributes) # raises
Model.find(id)
Model.find_by(field: value)
Model.where(field: value)
model.update(attributes) # Boolean
model.update!(attributes) # raises
Routes
resources :model
Nesting
Path and module
namespace :admin do
resources :articles # /admin/articles, Admin::ArticlesController
end
Path only
scope module: 'admin' do
resources :articles # /articles, Admin::ArticlesController
end
Module only
scope '/admin/' do
resources :articles # /admin/articles, ArticlesController
end
Controllers
| Path | Helper | HTTP Method | Controller Method | |-------------------------------------------- | models | models_path | GET | #index | | | POST | #create | models/new | new_model_path | GET | #new | models/:id | model_path(id) | GET | #show | | | PUT/PATCH | #update | | | DELETE | #destroy | models/:id/edit | edit_model_path(id) | GET | #edit
before_action :populate_model
def populate_model
@model = Model.find_by(slug: params[:model_slug])
end
params.require(:model).permit(:field, …)
redirect_to path, notice: string
Disabling CSRF
skip_forgery_protection
Rendering
render plain: "hello world"
render json: object.to_json, status: :something
head :no_content
Templates
<%= link_to label, path, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= form_with model: @model, local: true do |f| %>
<%= f.label :field %>
<%= f.text_field :field %>
<% @model.errors.full_messages_for(:field).each do |message| %>
<p><%= message %>
<% end %>
<%= f.submit 'Create' %>
<% end %>
<%= yield :head %>
<% content_for :head do %>
<title>A simple page</title>
<% end %>
Time Zones
https://thoughtbot.com/blog/its-about-time-zones
ActiveSupport::TimeWithZone supplements Time so it is fancy
Application time zone configured in config/application.rb
In a Rails app, we have three different time zones:
- system time
- application time
- database time
Time.nowis system timeTime.parsegives system time
Doing Time.zone then calling methods on that gives application time instead:
Time.zone.now
Time.zone.parse
Time Helpers
https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
require 'active_support/testing/time_helpers'
RSpec.describe "whatev" do
include ActiveSupport::Testing::TimeHelpers
#freeze_time(can take a block)#unfreeze_time#travel_to- stubs, so shouldn't change as the test runs
UUIDs
class EnableUuids < ActiveRecord::Migration[6.0]
def change
enable_extension 'uuid-ossp'
enable_extension 'pgcrypto'
end
end
class CreateTodos < ActiveRecord::Migration[6.0]
def change
create_table :todos, id: :uuid do |t|
# ...
end
end
end
class AddCategoryToTodos < ActiveRecord::Migration[6.0]
def change
add_reference :todos, :category, type: :uuid, foreign_key: true
end
end
Cache
https://guides.rubyonrails.org/caching_with_rails.html
config.cache_store = :null_store
config.cache_store = :memory_store
config.cache_store = :file_store
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] } # not TLS
group :production do
gem 'hiredis'
gem 'redis'
end
Rails.cache.fetch('key', expires_in: 12.hours) do
# calculate the value
end
Autoloading Lib
In config/application.rb:
config.autoload_paths << Rails.root.join('lib')
Rails Tests
Request (via RSpec-Rails)
get "/widgets/#{widget.id}", headers: headers
post '/categories', headers: {'Content-Type' => 'application/json'}, params: body_hash.to_json
Slapdash