Ruby on Rails Framework
Introduction to Rails
Ruby on Rails is a web application framework written in Ruby that follows the Model-View-Controller (MVC) pattern and emphasizes convention over configuration.
Core Principles:
- Don't Repeat Yourself (DRY)
- Convention over Configuration
- REST architectural style
- Rapid development
- Testing is built-in
Key Features:
- Active Record ORM
- Action Pack for handling requests
- Action Mailer for emails
- Active Storage for file uploads
- Action Cable for WebSockets
MVC Architecture
Model
# app/models/user.rb
class User < ApplicationRecord
has_many :posts
has_many :comments
validates :email, presence: true, uniqueness: true
validates :username, presence: true, length: { minimum: 3 }
before_save :normalize_email
private
def normalize_email
self.email = email.downcase
end
end
Controller
# app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
@users = User.all
end
def show
end
def create
@user = User.new(user_params)
if @user.save
redirect_to @user, notice: 'User created successfully'
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :username, :password)
end
end
Active Record
Associations
class User < ApplicationRecord
has_many :posts
has_many :comments
has_one :profile
has_and_belongs_to_many :roles
end
class Post < ApplicationRecord
belongs_to :user
has_many :comments
has_many :tags, through: :post_tags
end
Querying
# Find records
User.find(1)
User.find_by(email: 'user@example.com')
User.where(active: true)
# Complex queries
User.joins(:posts)
.where(posts: { published: true })
.group('users.id')
.having('count(posts.id) > ?', 5)
# Scopes
class Post < ApplicationRecord
scope :published, -> { where(published: true) }
scope :recent, -> { order(created_at: :desc).limit(5) }
end
Routing
RESTful Routes
# config/routes.rb
Rails.application.routes.draw do
root 'home#index'
resources :users do
resources :posts
member do
get 'profile'
end
collection do
get 'search'
end
end
namespace :api do
namespace :v1 do
resources :users, only: [:index, :show]
end
end
end
Custom Routes
# Custom routes with constraints
get 'posts/:year/:month/:day' => 'posts#show',
constraints: {
year: /\d{4}/,
month: /\d{1,2}/,
day: /\d{1,2}/
}
# Route concerns
concern :commentable do
resources :comments
end
resources :posts, concerns: :commentable
resources :photos, concerns: :commentable
Views and Templates
ERB Templates
Users
Username
Email
Actions
<% @users.each do |user| %>
<%= user.username %>
<%= user.email %>
<%= link_to 'Show', user %>
<%= link_to 'Edit', edit_user_path(user) %>
<% end %>
Layouts and Partials
<%= content_for?(:title) ? yield(:title) : "Default" %>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application' %>
<%= render 'shared/header' %>
<%= yield %>
<%= render 'shared/footer' %>
<%= form_with(model: @user, local: true) do |f| %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.submit %>
<% end %>
Testing
Model Tests
# test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
test "should not save user without email" do
user = User.new
assert_not user.save, "Saved user without email"
end
test "should validate email format" do
user = users(:valid)
user.email = "invalid_email"
assert_not user.valid?
end
end
Controller Tests
# test/controllers/users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get users_url
assert_response :success
end
test "should create user" do
assert_difference('User.count') do
post users_url, params: {
user: { email: 'test@example.com', username: 'test' }
}
end
assert_redirected_to user_url(User.last)
end
end
Security
Authentication
# Using Devise
# Gemfile
gem 'devise'
# app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable
def active_for_authentication?
super && active?
end
end
Authorization
# Using Pundit
class PostPolicy < ApplicationPolicy
def update?
user.admin? || record.author == user
end
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(author: user)
end
end
end
end
Deployment
Deployment Checklist:
- Configure production environment
- Set up database
- Configure web server (Nginx/Apache)
- Set up SSL certificates
- Configure background jobs
Production Configuration
# config/environments/production.rb
Rails.application.configure do
config.cache_classes = true
config.eager_load = true
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
config.public_file_server.enabled = true
config.assets.compile = false
config.active_storage.service = :amazon
config.log_level = :info
config.force_ssl = true
end