1. Pengenalan Ruby on Rails
Ruby on Rails (atau biasa disebut Rails) adalah framework web open-source yang dibangun dengan bahasa Ruby oleh David Heinemeier Hansson pada tahun 2004. Rails mengikuti filosofi Convention over Configuration (CoC) dan Don't Repeat Yourself (DY), sehingga developer bisa fokus pada fitur alih-alih konfigurasi.
Rails digunakan oleh perusahaan besar seperti GitHub, Shopify, Airbnb, Basecamp, Twitter (awalnya), dan ribuan startup lainnya. Rails memungkinkan pengembangan web yang sangat cepat β Anda bisa membuat prototipe aplikasi web dalam hitungan jam.
Mengapa Menggunakan Rails?
| Keunggulan | Penjelasan |
|---|---|
| Convention over Configuration | Tidak perlu menulis banyak konfigurasi β Rails sudah mengatur semuanya |
| Don't Repeat Yourself | Setiap logika hanya ditulis sekali β mengurangi duplikasi |
| Generator yang Powerful | rails generate membuat model, controller, views, migration otomatis |
| Active Record (ORM) | Basis data yang elegan tanpa menulis SQL |
| Scaffolding | CRUD lengkap dibuat hanya dengan satu command |
| Ekosistem Gems | Ribuan library untuk autentikasi, API, background job, dll |
Rails vs Framework Lain
| Aspek | Rails | Django | Laravel |
|---|---|---|---|
| Bahasa | Ruby | Python | PHP |
| ORM | Active Record | Django ORM | Eloquent |
| Templating | ERB, Haml, Slim | Jinja2 | Blade |
| CLI | rails generate | django-admin | artisan |
| Filosofi | CoC + DRY | Battery Included | Elegant Syntax |
| Scaffolding | π’ Built-in | π‘ Perlu extension | π’ Built-in |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β RUBY ON RAILS ECOSYSTEM β β β β ββββββββββββ ββββββββββββ ββββββββββββββββββββ β β β Core β β Tools β β Database β β β β Rails β β Rake β β PostgreSQL β β β β Rack β β Bundler β β MySQL/SQLite β β β β Sprocketsβ β Foreman β β Active Record β β β ββββββββββββ ββββββββββββ ββββββββββββββββββββ β β β β ββββββββββββ ββββββββββββ ββββββββββββββββββββ β β β Testing β β Deploy β β Testing β β β β RSpec β β Heroku β β RSpec β β β β Capybara β β Docker β β Capybara β β β β FactoryBotβ β Fly.io β β FactoryBot β β β ββββββββββββ ββββββββββββ ββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Instalasi dan Setup
Untuk menggunakan Rails, Anda memerlukan Ruby, RubyGems, dan Node.js (untuk Asset Pipeline).
Prasyarat
# 1. Pastikan Ruby sudah terinstal (minimal 3.0+) ruby --version # ruby 3.3.3 (2024-06-12 revision e12f4fa7f9) # 2. Pastikan Node.js terinstal (untuk Asset Pipeline) node --version # v20.10.0 # 3. Pastikan Yarn atau npm tersedia yarn --version # 1.22.19 # 4. Pastikan database tersedia (PostgreSQL atau SQLite) psql --version # PostgreSQL sqlite3 --version # SQLite
Instalasi Rails
# Instal Rails gem terbaru gem install rails # Verifikasi instalasi rails --version # Rails 7.1.3 # Jika menggunakan rbenv, rehash setelah instalasi rbenv rehash # Instal bundler jika belum terinstal gem install bundler # Cek semua gem Rails gem list | grep rails
Konfigurasi Database PostgreSQL
# Di Ubuntu/Debian: sudo apt install postgresql postgresql-contrib libpq-dev # Buat user PostgreSQL sudo -u postgres createuser --superuser $USER # Set password untuk user sudo -u postgres psql \password your_username \q # Di macOS (Homebrew): brew install postgresql@16 brew services start postgresql@16 # Verifikasi koneksi psql postgres # Anda sekarang di PostgreSQL console \q # Keluar
Untuk pengembangan lokal, Anda bisa menggunakan SQLite (default Rails) tanpa instalasi database server tambahan. Namun untuk production, sangat disarankan menggunakan PostgreSQL yang lebih robust dan mendukung fitur-fitur lanjutan seperti JSON columns, full-text search, dan banyak lagi.
3. Arsitektur MVC
Rails menggunakan pola arsitektur MVC (Model-View-Controller) yang memisahkan logika aplikasi menjadi tiga komponen utama. Pemisahan ini membuat kode lebih terorganisir, mudah di-maintain, dan scalable.
Komponen MVC
| Komponen | Lokasi Folder | Tanggung Jawab | Contoh |
|---|---|---|---|
| Model | app/models/ | Data, business logic, validasi, relasi database | user.rb, post.rb |
| View | app/views/ | Tampilan HTML yang dilihat user | users/index.html.erb |
| Controller | app/controllers/ | Menerima request, memproses, mengirim ke view | users_controller.rb |
ββββββββββββ Request ββββββββββββββββ
β ββββββββββββββββ>β β
β Browser β GET /users β Router β
β (Client)β β (routes.rb) β
β β β β
ββββββββββββ ββββββββ¬ββββββββ
β² β
β β Match route
β βΌ
β ββββββββββββββββ
β β β
β HTML Response β Controller β
β<ββββββββββββββββββββ UsersControllerβ
β β #index β
β ββββββββ¬ββββββββ
β β
β β Query data
β βΌ
β ββββββββββββββββ
β β β
β β Model β
β β User.all β
β β β
β ββββββββ¬ββββββββ
β β
β β Return data
β βΌ
β ββββββββββββββββ
β β β
β Rendered HTML β View β
β<ββββββββββββββββββββ users/index β
β β .html.erb β
βββββββββββββββββββββββ β
ββββββββββββββββ
Alur Request Lengkap
# 1. Browser mengirim request: GET /users # 2. Router (routes.rb) mencocokkan URL dengan controller action # get '/users', to: 'users#index' # 3. Controller menerima request dan memanggil Model # class UsersController < ApplicationController # def index # @users = User.all # Query ke database via Model # end # end # 4. Model mengambil data dari database # SELECT * FROM users; # 5. Controller mengirim data ke View # @users tersedia di view # 6. View merender HTML dengan data yang diterima # <% @users.each do |user| %> # <p><%= user.name %></p> # <% end %> # 7. HTML dikembalikan ke browser
4. Membuat Proyek Rails Baru
Rails menyediakan generator yang sangat powerful untuk membuat proyek baru, model, controller, views, dan banyak lagi.
Membuat Aplikasi Baru
# Buat aplikasi Rails baru dengan PostgreSQL rails new blog_app -d postgresql # Atau dengan SQLite (untuk pengembangan lokal) rails new blog_app # Masuk ke folder proyek cd blog_app # Lihat struktur folder yang dibuat ls -la # app/ - Kode utama (MVC) # config/ - Konfigurasi aplikasi # db/ - Database migrations & seeds # public/ - File statis (CSS, JS, gambar) # test/ atau spec/ - Unit test # Gemfile - Dependency Ruby # config.ru - Rack configuration # Rakefile - Rake tasks # Konfigurasi database di config/database.yml # Jalankan setup database rails db:create rails db:migrate # Jalankan server Rails rails server # => Booting Puma # => Rails 7.1.3 application starting in development # => Listening on http://127.0.0.1:3000 # Buka browser: http://localhost:3000 # Anda akan melihat halaman selamat datang Rails!
Folder Structure Rails
| Folder | Isi | Penjelasan |
|---|---|---|
app/models/ | user.rb | Model untuk interaksi database |
app/views/ | users/ | Template HTML untuk setiap resource |
app/controllers/ | users_controller.rb | Logika penanganan request |
config/routes.rb | Routing | URL mapping ke controller#action |
db/migrate/ | 20240101_create_users.rb | Skema database (migrations) |
config/database.yml | Konfigurasi | Pengaturan koneksi database |
Gemfile | Dependencies | Daftar gem yang dibutuhkan proyek |
Generator Commands
# Generator sangat penting di Rails # Menunjukkan semua generator yang tersedia rails generate --help # Generate model rails generate model User name:string email:string age:integer # Menghasilkan: # - app/models/user.rb # - db/migrate/20240101000000_create_users.rb # - test/models/user_test.rb # Generate controller rails generate controller Users index show new edit # Menghasilkan: # - app/controllers/users_controller.rb # - app/views/users/index.html.erb # - app/views/users/show.html.erb # - test/controllers/users_controller_test.rb # Generate scaffold (semua sekaligus: model + controller + views + tests) rails generate scaffold Post title:string body:text user:references # Ini menghasilkan CRUD lengkap! # Generate migration tunggal rails generate migration AddRoleToUsers role:string # Generate resource (model + controller + routes) rails generate resource Article title:string content:text # Jalankan migration setelah generate rails db:migrate # Rollback migration jika salah rails db:rollback # Undo generate rails destroy model User rails destroy controller Users
5. Routing
Routing di Rails didefinisikan di config/routes.rb. Routing menentukan URL mana yang diarahkan ke controller action mana.
Basic Routing
# config/routes.rb
Rails.application.routes.draw do
# Root route (homepage)
root "pages#home"
# Basic routes
get '/about', to: 'pages#about'
get '/contact', to: 'pages#contact'
post '/contact', to: 'pages#submit_contact'
# RESTful routes dengan resource
resources :users
# Menghasilkan 7 route:
# GET /users => users#index
# GET /users/new => users#new
# POST /users => users#create
# GET /users/:id => users#show
# GET /users/:id/edit => users#edit
# PATCH /users/:id => users#update
# DELETE /users/:id => users#destroy
# Resources dengan pilihan tertentu
resources :posts, only: [:index, :show, :new, :create]
# Hanya membuat 4 route yang dipilih
resources :articles, except: [:destroy]
# Semua route kecuali destroy
# Nested resources
resources :users do
resources :posts
# Menghasilkan:
# GET /users/:user_id/posts
# GET /users/:user_id/posts/new
# GET /users/:user_id/posts/:id
# dll...
end
# Member routes (memerlukan ID)
resources :posts do
member do
post :publish
# POST /posts/:id/publish
post :archive
# POST /posts/:id/archive
end
end
# Collection routes (tidak memerlukan ID)
resources :posts do
collection do
get :search
# GET /posts/search
get :drafts
# GET /posts/drafts
end
end
# Namespace untuk API
namespace :api do
namespace :v1 do
resources :posts
# /api/v1/posts
end
end
end
Named Routes dan Path Helpers
# Ketika Anda mendefinisikan routes, Rails otomatis membuat # helper methods: # resources :users menghasilkan: users_path # "/users" user_path(@user) # "/users/1" new_user_path # "/users/new" edit_user_path(@user) # "/users/1/edit" # Di controller: redirect_to users_path redirect_to user_path(@user) redirect_to new_user_path # Di views (ERB): # <%= link_to "Semua User", users_path %> # <%= link_to "Lihat User", user_path(@user) %> # <%= link_to "Edit", edit_user_path(@user) %> # <%= link_to "Hapus", user_path(@user), method: :delete %> # Custom named route get '/dashboard', to: 'pages#dashboard', as: :dashboard # Menghasilkan: dashboard_path = "/dashboard" # Cek semua routes yang tersedia: # rails routes # Atau di Rails 7+: # rails routes --controller users
6. Controller dan Action
Controller menangani request HTTP dan mengirim response. Setiap controller action biasanya mengambil data dari model dan mengirimkannya ke view.
Controller Lengkap
# app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
def index
@users = User.all.order(created_at: :desc)
@user_count = @users.count
end
# GET /users/:id
def show
# @user sudah diset oleh set_user (before_action)
end
# GET /users/new
def new
@user = User.new
end
# POST /users
def create
@user = User.new(user_params)
if @user.save
redirect_to @user, notice: "User berhasil dibuat!"
else
render :new, status: :unprocessable_entity
end
end
# GET /users/:id/edit
def edit
# @user sudah diset oleh set_user
end
# PATCH /users/:id
def update
if @user.update(user_params)
redirect_to @user, notice: "User berhasil diupdate!"
else
render :edit, status: :unprocessable_entity
end
end
# DELETE /users/:id
def destroy
@user.destroy
redirect_to users_path, notice: "User berhasil dihapus!"
end
private
def set_user
@user = User.find(params[:id])
end
# Strong Parameters β whitelist parameter yang diizinkan
def user_params
params.require(:user).permit(:name, :email, :age, :role)
end
end
Application Controller (Base Controller)
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_locale
# Helper method untuk semua controllers & views
helper_method :current_user, :logged_in?
private
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
def logged_in?
current_user.present?
end
def require_login
unless logged_in?
redirect_to login_path, alert: "Anda harus login terlebih dahulu!"
end
end
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
7. Views dan ERB Template
Views di Rails menggunakan template engine ERB (Embedded Ruby) yang memungkinkan Anda menulis Ruby code di dalam HTML. Ada juga alternatif seperti Haml dan Slim.
ERB Template
<%# app/views/users/index.html.erb %>
<h1>Daftar User (<%= @user_count %>)</h1>
<%# Flash messages %>
<% if notice %>
<div class="alert alert-success">
<%= notice %>
</div>
<% end %>
<% if @users.any? %>
<table class="table">
<thead>
<tr>
<th>Nama</th>
<th>Email</th>
<th>Umur</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= user.age %></td>
<td>
<%= link_to "Lihat", user_path(user), class: "btn btn-info" %>
<%= link_to "Edit", edit_user_path(user), class: "btn btn-warning" %>
<%= link_to "Hapus", user_path(user),
data: { turbo_method: :delete, turbo_confirm: "Yakin?" },
class: "btn btn-danger" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>Belum ada user.</p>
<% end %>
<%= link_to "Buat User Baru", new_user_path, class: "btn btn-primary" %>
Partials (Template Terpisah)
# Partial diawali dengan underscore: _user.html.erb
# app/views/users/_user.html.erb
<div class="user-card">
<h3><%= user.name %></h3>
<p>Email: <%= user.email %></p>
<p>Umur: <%= user.age %> tahun</p>
<%= link_to "Lihat Detail", user_path(user) %>
</div>
# Menggunakan partial di view utama:
# app/views/users/index.html.erb
<% @users.each do |user| %>
<%= render partial: "user", locals: { user: user } %>
<%# Atau shorthand: %>
<%= render user %>
<% end %>
# Partial dengan collection
<%= render partial: "user", collection: @users %>
# Setiap item otomatis tersedia sebagai 'user' di partial
# Layout partial (shared/_header.html.erb)
<%= render "shared/header" %>
# Yield untuk content_for di layout
<% content_for :title, "Daftar User" %>
# app/views/layouts/application.html.erb
# <title><%= content_for?(:title) ? yield(:title) : "BeebaneLabs" %></title>
Helper Methods
# app/helpers/users_helper.rb
module UsersHelper
def user_avatar(user, size: 50)
if user.avatar.attached?
image_tag user.avatar, size: "#{size}x#{size}", class: "avatar"
else
image_tag "default_avatar.png", size: "#{size}x#{size}", class: "avatar"
end
end
def format_umur(umur)
case umur
when 0..12 then "#{umur} tahun (Anak-anak)"
when 13..17 then "#{umur} tahun (Remaja)"
when 18..64 then "#{umur} tahun (Dewasa)"
else "#{umur} tahun (Lansia)"
end
end
def status_badge(user)
if user.active?
content_tag :span, "Aktif", class: "badge badge-success"
else
content_tag :span, "Nonaktif", class: "badge badge-danger"
end
end
end
# Menggunakan di view:
# <%= user_avatar(@user, size: 100) %>
# <%= format_umur(@user.age) %>
# <%= status_badge(@user) %>
8. Active Record
Active Record adalah ORM (Object-Relational Mapping) bawaan Rails yang memungkinkan Anda berinteraksi dengan database menggunakan Ruby objects. Anda bisa melakukan operasi CRUD tanpa menulis SQL!
Model Dasar
# app/models/user.rb
class User < ApplicationRecord
# Validations
validates :name, presence: true, length: { minimum: 2, maximum: 100 }
validates :email, presence: true, uniqueness: true,
format: { with: URI::MailTo::EMAIL_REGEXP }
validates :age, numericality: { greater_than: 0, less_than: 150 }
# Associations
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
# Scopes
scope :active, -> { where(active: true) }
scope :recent, -> { order(created_at: :desc).limit(10) }
scope :by_role, ->(role) { where(role: role) }
# Callbacks
before_save :downcase_email
# Instance methods
def full_info
"#{name} (#{email})"
end
def post_count
posts.count
end
# Class methods
def self.search(keyword)
where("name LIKE ? OR email LIKE ?", "%#{keyword}%", "%#{keyword}%")
end
private
def downcase_email
self.email = email.downcase
end
end
CRUD Operations
# Buka Rails console:
# rails console (atau: rails c)
# ===== CREATE =====
# Method 1: new + save
user = User.new(name: "Budi", email: "budi@mail.com", age: 25)
user.save # => true
# Method 2: create (new + save sekaligus)
User.create(name: "Rina", email: "rina@mail.com", age: 22)
# Method 3: create! (raises error jika gagal)
User.create!(name: "Andi", email: "andi@mail.com", age: 30)
# ===== READ =====
User.all # Semua user
User.find(1) # Cari berdasarkan ID
User.find_by(email: "budi@mail.com") # Cari berdasarkan atribut
User.where(age: 25) # Filter
User.where("age > ?", 20) # Filter dengan raw SQL
User.order(name: :asc) # Urutkan
User.limit(5) # Batasi 5 record
User.first # Record pertama
User.last # Record terakhir
User.count # Jumlah record
User.exists?(email: "budi@mail.com") # Cek keberadaan
# Chaining queries
User.active.recent.by_role("admin").where("age > ?", 18)
# ===== UPDATE =====
user = User.find(1)
user.name = "Budi Santoso"
user.save # => true
# Atau dengan update
user.update(name: "Budi Santoso", age: 26)
# Atau dengan update! (raises error)
user.update!(name: "Budi Santoso")
# Update multiple records
User.where(active: false).update_all(active: true)
# ===== DELETE =====
user = User.find(1)
user.destroy # Menghapus record dan memanggil callbacks
# Delete by condition
User.where("created_at < ?", 1.year.ago).destroy_all
9. Database Migrations
Migration di Rails adalah cara untuk mengubah skema database secara terstruktur dan reversible. Migration ditulis dalam Ruby, bukan SQL!
Membuat Migration
# Generate migration
rails generate migration CreateUsers name:string email:string age:integer
# Hasilnya: db/migrate/20240101120000_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false
t.integer :age
t.boolean :active, default: true
t.string :role, default: "user"
t.timestamps # created_at dan updated_at otomatis
end
# Tambahkan index
add_index :users, :email, unique: true
add_index :users, :role
end
end
# Jalankan migration
rails db:migrate
# == 20240101120000 CreateUsers: migrating ===================================
# -- create_table(:users)
# -> 0.0123s
# == 20240101120000 CreateUsers: migrated (0.0123s) =========================
Migration Commands
# Jalankan semua migration yang belum dijalankan rails db:migrate # Rollback migration terakhir rails db:rollback # Rollback 3 migration terakhir rails db:rollback STEP=3 # Jalankan migration ke versi tertentu rails db:migrate VERSION=20240101120000 # Reset database (drop + create + migrate + seed) rails db:reset # Seed data dari db/seeds.rb rails db:seed # Lihat status migration rails db:migrate:status # Cek skema database saat ini cat db/schema.rb
Menambah Kolom Baru
# Generate migration untuk menambah kolom
rails generate migration AddPhoneToUsers phone:string
# Hasil: db/migrate/20240102120000_add_phone_to_users.rb
class AddPhoneToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :phone, :string
add_column :users, :address, :text
add_column :users, :date_of_birth, :date
end
end
# Migration untuk menambah foreign key
rails generate migration AddUserRefToPosts user:references
# Hasil: db/migrate/20240103120000_add_user_ref_to_posts.rb
class AddUserRefToPosts < ActiveRecord::Migration[7.1]
def change
add_reference :posts, :user, null: false, foreign_key: true
# Atau:
# add_column :posts, :user_id, :bigint, null: false
# add_foreign_key :posts, :users
end
end
# Migration untuk mengubah kolom
class ChangeAgeInUsers < ActiveRecord::Migration[7.1]
def up
change_column :users, :age, :integer, default: 0
end
def down
change_column :users, :age, :integer, default: nil
end
end
# Migration untuk rename kolom
class RenameNameToFullNameInUsers < ActiveRecord::Migration[7.1]
def change
rename_column :users, :name, :full_name
end
end
10. Model Associations
Active Record Associations mendefinisikan hubungan antar model. Ada tiga jenis association utama: has_many, belongs_to, dan has_many :through.
Association Types
# User β has_many Posts class User < ApplicationRecord has_many :posts, dependent: :destroy has_many :comments, dependent: :destroy has_many :post_comments, through: :posts, source: :comments has_one :profile, dependent: :destroy has_and_belongs_to_many :roles end # Post β belongs_to User, has_many Comments class Post < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy has_many :tags, dependent: :destroy has_one_attached :image end # Comment β belongs_to User and Post class Comment < ApplicationRecord belongs_to :user belongs_to :post end # Profile β belongs_to User class Profile < ApplicationRecord belongs_to :user end # Tag β belongs_to Post class Tag < ApplicationRecord belongs_to :post end
Menggunakan Associations
# Mendapatkan relasi
user = User.find(1)
user.posts # Semua post milik user ini
user.comments # Semua comment milik user ini
user.profile # Profile milik user ini
post = Post.find(1)
post.user # User yang memiliki post ini
post.comments # Semua comment pada post ini
# Membuat relasi baru
user.posts.create(title: "Post Baru", body: "Isi post")
user.posts.build(title: "Draft", body: "Draft post") # Tanpa save
# Mengakses data relasi dengan chaining
user.posts.published.recent # Post published milik user, urut terbaru
# Eager loading β menghindari N+1 query problem
# BAD β akan menjalankan N+1 queries:
users = User.all
users.each { |u| puts u.posts.count } # 1 query per user!
# GOOD β eager loading dengan includes:
users = User.includes(:posts).all
users.each { |u| puts u.posts.count } # Hanya 2 queries!
# Preload dan eager_load
User.preload(:posts) # 2 terpisah queries
User.eager_load(:posts) # 1 query dengan LEFT JOIN
User.includes(:posts).where(posts: { published: true }) # Hybrid
11. Validations dan Callbacks
Validations memastikan data yang masuk ke database valid dan konsisten. Callbacks memungkinkan Anda menjalankan kode sebelum/sesudah event tertentu pada model.
Validations
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
# Validasi dasar
validates :title, presence: true,
length: { minimum: 5, maximum: 200 }
validates :body, presence: true,
length: { minimum: 10 }
validates :status, inclusion: { in: %w[draft published archived] }
validates :slug, uniqueness: true, allow_nil: true
# Custom validation
validate :title_not_profanity
# Conditional validation
validates :publish_date, presence: true, if: :published?
# Numericality
validates :view_count, numericality: {
greater_than_or_equal_to: 0,
only_integer: true
}
# Format validation
validates :slug, format: {
with: /\A[a-z0-9\-]+\z/,
message: "hanya boleh huruf kecil, angka, dan dash"
}
private
def title_not_profanity
bad_words = ["spam", "scam"]
if bad_words.any? { |word| title.downcase.include?(word) }
errors.add(:title, "mengandung kata yang tidak diizinkan")
end
end
end
Callbacks
# Callbacks tersedia dalam urutan berikut:
# before_validation β sebelum validasi
# after_validation β setelah validasi
# before_save β sebelum save (create & update)
# after_save β setelah save
# before_create β sebelum create
# after_create β setelah create
# before_update β sebelum update
# after_update β setelah update
# before_destroy β sebelum destroy
# after_destroy β setelah destroy
class Post < ApplicationRecord
before_save :generate_slug, :set_published_at
after_create :notify_followers
before_destroy :archive_content
private
def generate_slug
self.slug = title.parameterize if title.present? && slug.blank?
end
def set_published_at
if status_changed? && status == "published"
self.published_at = Time.current
end
end
def notify_followers
# Kirim notifikasi ke followers
NotificationService.new_post(self)
end
def archive_content
ArchivedPost.create!(
title: title,
body: body,
user_id: user_id,
archived_at: Time.current
)
end
end
12. Form dan CRUD
Rails memiliki helper form yang sangat powerful untuk membuat form HTML yang terintegrasi dengan model.
Form dengan form_with
<%# app/views/users/_form.html.erb %>
<%= form_with(model: @user, local: true) do |form| %>
<%# Tampilkan error jika ada %>
<% if @user.errors.any? %>
<div class="error-messages">
<h3><%= pluralize(@user.errors.count, "error") %> terjadi:</h3>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= form.label :name, "Nama Lengkap" %>
<%= form.text_field :name, class: "form-control",
placeholder: "Masukkan nama..." %>
</div>
<div class="form-group">
<%= form.label :email, "Email" %>
<%= form.email_field :email, class: "form-control",
placeholder: "contoh@email.com" %>
</div>
<div class="form-group">
<%= form.label :age, "Umur" %>
<%= form.number_field :age, class: "form-control", min: 1, max: 150 %>
</div>
<div class="form-group">
<%= form.label :role, "Role" %>
<%= form.select :role,
options_for_select([
["User", "user"],
["Admin", "admin"],
["Moderator", "moderator"]
], @user.role),
{}, { class: "form-control" } %>
</div>
<div class="form-group">
<%= form.label :bio, "Bio" %>
<%= form.text_area :bio, class: "form-control", rows: 5 %>
</div>
<div class="form-group">
<%= form.label :active, "Aktif?" %>
<%= form.check_box :active %>
</div>
<div class="form-actions">
<%= form.submit "Simpan", class: "btn btn-primary" %>
<%= link_to "Batal", users_path, class: "btn btn-secondary" %>
</div>
<% end %>
Show View
<%# app/views/users/show.html.erb %>
<div class="user-detail">
<div class="user-header">
<h1><%= @user.name %></h1>
<%= status_badge(@user) %>
</div>
<div class="user-info">
<p><strong>Email:</strong> <%= @user.email %></p>
<p><strong>Umur:</strong> <%= format_umur(@user.age) %></p>
<p><strong>Role:</strong> <%= @user.role.capitalize %></p>
<p><strong>Bergabung:</strong> <%= l(@user.created_at, format: :long) %></p>
</div>
<% if @user.posts.any? %>
<h2>Posts (<%= @user.posts.count %>)</h2>
<% @user.posts.each do |post| %>
<div class="post-card">
<h3><%= post.title %></h3>
<p><%= truncate(post.body, length: 200) %></p>
<%= link_to "Baca selengkapnya", post_path(post) %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= link_to "Edit", edit_user_path(@user), class: "btn btn-warning" %>
<%= link_to "Hapus", user_path(@user),
data: { turbo_method: :delete, turbo_confirm: "Yakin?" },
class: "btn btn-danger" %>
<%= link_to "Kembali", users_path, class: "btn btn-secondary" %>
</div>
</div>
13. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Ruby on Rails: