Tổng hợp 71 câu hỏi phỏng vấn Rails kèm đáp án chi tiết, song ngữ Việt–Anh — sắp xếp từ cơ bản đến nâng cao. Ôn luyện miễn phí, tự động lưu tiến độ.
Ruby on Rails (Rails) là web framework viết bằng Ruby, do David Heinemeier Hansson (DHH) tạo ra năm 2004. Rails nổi tiếng với hai triết lý cốt lõi: - Convention over Configuration (CoC): Rails đặt ra quy ước mặc định — tên file, tên bảng…
Rails chia ứng dụng thành 3 tầng: Tầng Trách nhiệm Ví dụ --------- Model Dữ liệu + business logic; giao tiếp DB qua Active Record app/models/user.rb View Trình bày HTML cho người dùng (ERB/Haml/Slim) app/views/users/show.html.erb Controller Nhận request, gọi model, chọn view để render app/controllers/userscontroller.rb…
Các thư mục quan trọng cần biết: CoC quy định: model User → file app/models/user.rb → bảng DB users. Không cần khai báo mapping — Rails suy ra từ tên.
Gem là thư viện (package) Ruby được phân phối qua RubyGems.org. Bản thân Rails cũng là một gem. Trong project, dependency được khai báo trong Gemfile và do Bundler quản lý. Gemfile.lock lưu chính xác phiên bản đang dùng — commit file này vào git…
resources :posts tự động tạo 7 route RESTful chuẩn: HTTP Verb Path Action Helper ------------ GET /posts index postspath GET /posts/new new newpostpath POST /posts create postspath GET /posts/:id show postpath(id) GET /posts/:id/edit edit editpostpath(id) PATCH/PUT /posts/:id update postpath(id) DELETE /posts/:id destroy postpath(id) Dùng only:…
render dựng view ngay trong request hiện tại — không tạo request mới, URL giữ nguyên, instance variables vẫn còn. redirectto gửi HTTP 302, bảo browser tạo request mới đến URL khác — controller hiện tại kết thúc, action mới chạy từ đầu. Lỗi thường…
beforeaction là callback chạy trước một hoặc nhiều action của controller — dùng để factoring logic dùng chung (authentication, authorization, load record). Nếu beforeaction gọi render hoặc redirectto, chuỗi callback dừng và action gốc không chạy. Các loại khác: afteraction, aroundaction.
Nested resources biểu diễn quan hệ cha-con trong URL: /posts/:postid/comments — comment luôn thuộc về 1 post cụ thể. Route được tạo: GET /posts/:postid/comments, POST /posts/:postid/comments, ... Trong CommentsController, lấy post qua Post.find(params[:postid]) rồi build comment từ @post.comments. Best practice: chỉ nest 1 cấp —…
Strong Parameters (Rails 4+) bắt controller khai báo tường minh những tham số nào được phép gán vào model — ngăn mass assignment vulnerability (attacker gửi param ẩn như admin=true để leo quyền). Nếu params chứa field không được permit, nó bị bỏ qua. Nếu…
Active Record là ORM (Object-Relational Mapper) của Rails — mỗi model Ruby class tương ứng với 1 bảng DB, mỗi instance tương ứng với 1 row. Quy ước đặt tên (CoC): Model class Bảng DB ------ User users BlogPost blogposts OrderItem orderitems Model tự hiểu…
Active Record hỗ trợ 6 loại association: Quy tắc foreign key: belongsto bên nào thì bảng đó chứa foreign key. hasmany :through (3 bảng) dùng khi join table cần thêm attributes. hasandbelongstomany (2 bảng) dùng khi join table thuần tuý không cần id/attrs.
N+1 xảy ra khi load 1 collection rồi lặp để lấy association của mỗi item → 1 query lấy list + N query cho từng item. includes vs eagerload vs preload: - includes → Rails tự chọn strategy (preload hoặc eagerload). - preload → luôn…
Validations chạy trước save / create / update. Nếu fail, record không được lưu và lỗi nằm trong record.errors. Dùng save! / create! để raise ActiveRecord::RecordInvalid thay vì trả false — tốt hơn trong business logic cần fail-fast.
Callbacks là hooks vào lifecycle của model — chạy tự động khi object được create/update/destroy. Thứ tự khi save một record mới: aftercommit chạy sau khi transaction commit — dùng cho side-effects (gửi mail, enqueue job). Dùng aftersave có thể gửi mail dù transaction sau…
Scope là named query được định nghĩa trong model, trả về ActiveRecord::Relation → chainable. Scope vs class method: scope luôn trả về ActiveRecord::Relation (kể cả khi condition false → trả toàn bộ). Class method có thể trả nil → chain bị gãy. Với logic phức…
Migration là Ruby class mô tả thay đổi schema DB theo thời gian — có thể chạy lên (up/change) hoặc rollback xuống (down). Tại sao không viết SQL trực tiếp: - Version control được (mỗi migration là 1 file commit-able). - Portable: change method tự…
db/schema.rb là snapshot toàn bộ schema hiện tại — được Rails tự sinh sau mỗi lần db:migrate. Nó không phải migration, mà là trạng thái cuối cùng của DB. Tại sao quan trọng: - rails db:schema:load tạo DB mới từ schema.rb (nhanh hơn replay toàn…
change dùng cho các operations tự reversible — Rails biết cách đảo ngược: Một số operations không reversible → phải viết up/down rõ ràng: Operations không reversible: removecolumn (mất data type), changecolumn, execute raw SQL data.
Index là cấu trúc dữ liệu phụ (B-tree) giúp DB tìm kiếm nhanh theo column mà không phải scan toàn bảng. Khi nào cần index: - Foreign key columns (userid, postid) → luôn cần để JOIN nhanh. - Columns thường xuất hiện trong WHERE /…
Partial là fragment ERB tái sử dụng — file tên bắt đầu bằng (underscore). Khi truyền collection (render @posts), Rails tự lặp và dùng local variable khớp tên partial (post cho post.html.erb) — không cần .each trong view.
flash lưu thông báo ngắn cho request tiếp theo — thường dùng sau redirectto. flash.now lưu cho request hiện tại — dùng khi render (không redirect). Flash tự xoá sau khi được đọc — không cần clean up thủ công. Trong layout, đọc bằng flash[:notice]…
CSRF (Cross-Site Request Forgery) là tấn công khiến trình duyệt nạn nhân gửi request đến server thay mặt họ. Rails bảo vệ bằng cách nhúng authenticity token (random string unique per session) vào mọi form. Controller kiểm tra token trước mọi non-GET request: Request từ…
Concern là module được include vào nhiều class để tái sử dụng logic. Rails cung cấp ActiveSupport::Concern để tránh boilerplate. Model Concern hoạt động tương tự — thường dùng để share scope, callback, hay helper method giữa nhiều model. Concern tốt cho logic dùng ở…
Cookie Session --------- Lưu ở đâu Trình duyệt Server (hoặc encrypted cookie) Xem được Có (dễ đọc) Không (mã hoá hoặc server-side) Kích thước 4KB Không giới hạn (server-side) Cookies lưu plaintext hoặc signed (cookies.signed[:userid]). Session mặc định dùng CookieStore — toàn bộ session được…
Rails tích hợp sẵn Minitest. Hầu hết project thực tế dùng RSpec thay thế. Loại test Phạm vi Tool --------- Unit (model) Test 1 model, không browser type: :model (RSpec) / ActiveSupport::TestCase Controller/Request Test action, params, response code type: :request (RSpec) Integration Test nhiều layer,…
Active Job là interface thống nhất để tạo và enqueue background jobs — có thể chạy với nhiều backend (Sidekiq, Resque, GoodJob) mà không đổi code job. Tại sao cần: một số tác vụ không nên chạy trong HTTP request vì tốn thời gian hoặc…
Active Job là abstraction layer — định nghĩa interface chung, Rails tích hợp sẵn. Sidekiq là backend cụ thể dùng Redis để store và process job — nổi tiếng nhất cho Rails. Sidekiq dùng multi-threading (concurrency ~25 mặc định) → xử lý nhiều job song…
Rails cung cấp ActiveJob::TestHelper với matchers tích hợp. Dùng performenqueuedjobs để flush queue ngay trong test. Để test job perform trực tiếp, gọi describedclass.performnow(id) và assert side-effect (mail gửi, record thay đổi, v.v.).
Action Mailer cho phép tạo và gửi email từ Rails, tương tự như Controller nhưng cho email. View nằm ở app/views/usermailer/welcome.html.erb. Test với ActionMailer::Base.deliveries (test mode tích hợp sẵn).
Asset Pipeline (Sprockets) là framework gom, minify và fingerprint static assets (JS, CSS, images) trước khi deploy. Vai trò chính: - Concatenation: gộp nhiều file JS/CSS thành 1 file → giảm HTTP requests. - Minification: xoá whitespace/comments → file nhỏ hơn. - Fingerprinting: thêm hash…
Helper method là Ruby method available trong view/template để tạo HTML tags và link. formwith model: @post tự biết dùng POST /posts (tạo mới) hay PATCH /posts/:id (cập nhật) dựa vào @post.persisted?. Từ Rails 5.1+ formwith thay thế formfor và formtag.
namespace và scope đều nhóm route, nhưng ảnh hưởng khác nhau: namespace :admin scope :admin --------- URL prefix /admin/posts /admin/posts Controller Admin::PostsController PostsController Named helper adminpostspath postspath Dùng namespace cho admin panel (muốn tất cả đều tách biệt). Dùng scope cho API versioning khi muốn…
Polymorphic association cho phép một model belongsto nhiều model khác nhau qua cùng một association. Ví dụ: Comment có thể thuộc về Post hoặc Video. Cột commentabletype lưu tên class, commentableid lưu id. Rails tự handle việc lọc.
STI lưu nhiều subclass khác nhau vào một bảng DB dùng cột type để phân biệt. Dùng khi: các subclass có phần lớn attribute giống nhau, chỉ khác behavior. Không dùng khi: subclass có nhiều attribute riêng → bảng sẽ có nhiều cột bỏ trống…
Transaction đảm bảo các thao tác DB hoặc commit hết, hoặc không commit gì — nếu bất kỳ bước nào fail, toàn bộ rollback. Transaction tự động bao quanh mỗi save / create / destroy. Cần explicit transaction khi: - Nhiều thao tác DB phụ…
Gọi model class (VD: User.updateall(...)) trong migration nguy hiểm vì model thay đổi theo thời gian — migration cũ sẽ fail khi replay (setup fresh DB) vì model không còn method/association đó nữa. Sai: Đúng — dùng anonymous model hoặc raw SQL:
Layout là "wrapper" HTML chứa phần chung của trang (header, footer, nav). Mặc định mọi action dùng app/views/layouts/application.html.erb. Chọn layout theo controller: Dùng render layout: false để render không có layout (API response JSON).
Caching giảm số lần tính toán/query tốn kém bằng cách lưu kết quả HTML. Fragment cache — cache một đoạn view: Russian-doll cache — cache lồng nhau: outer cache bao inner cache. Outer chỉ expired khi inner thay đổi. Khi 1 comment thay đổi →…
Content negotiation cho phép một action phản hồi theo format khác nhau (HTML, JSON, XML) tuỳ Accept header hoặc URL extension. Request GET /posts/1 → HTML. GET /posts/1.json hoặc header Accept: application/json → JSON. respondwith (từ gem responders) ngắn gọn hơn cho CRUD chuẩn nhưng…
Factory Bot (trước là FactoryGirl) là gem tạo test data linh hoạt — thay thế fixtures (file YAML static) bằng Ruby code. Fixture load toàn bộ vào DB trước khi test — nhanh nhưng cứng, khó hiểu quan hệ. Factory Bot tạo khi cần —…
Stub thay thế method thật bằng kết quả giả — kiểm soát đầu ra, không quan tâm có gọi hay không. Mock là stub + có kỳ vọng rằng method phải được gọi — test sẽ fail nếu không gọi. Khi nào dùng: - Stub:…
DatabaseCleaner đảm bảo mỗi test bắt đầu với DB sạch — tránh test state rò rỉ sang test khác. Ba strategy chính: Strategy Cơ chế Tốc độ Dùng khi ------------ transaction Wrap test trong transaction, rollback sau Nhanh nhất Unit/request test không cần JS truncation…
Cả hai đều xoá bản ghi, nhưng đi qua hai đường khác nhau: - destroy: nạp object lên, chạy callbacks (beforedestroy, afterdestroy, aftercommit) và xử lý association khai báo dependent: :destroy. Đây là đường "an toàn" khi có logic phụ thuộc. - delete: bắn thẳng…
Khác biệt nằm ở quan hệ với transaction của database: - aftersave: chạy bên trong transaction, ngay sau khi INSERT/UPDATE được gửi nhưng trước khi COMMIT. Nếu về sau transaction rollback, mọi thứ aftersave làm cũng bị huỷ theo nếu nằm trong DB — nhưng…
Cả hai mô hình quan hệ nhiều-nhiều, khác nhau ở chỗ có model trung gian hay không: - hasandbelongstomany (HABTM): chỉ cần một bảng nối (vd authorsbooks), không có model cho nó. Gọn khi quan hệ thuần tuý là "nối", không có dữ liệu kèm…
Active Storage là framework tích hợp sẵn để đính kèm file (ảnh, PDF...) và upload lên cloud (S3, GCS, Azure) hoặc disk local. Cần chạy rails activestorage:install một lần để tạo các bảng activestorageblobs, activestorageattachments. Khai báo trên model và attach: Điểm cốt lõi: -…
Scaffolding là generator sinh sẵn toàn bộ stack CRUD cho một resource chỉ bằng một lệnh: model + migration + controller + views + route + test. Sau lệnh này bạn có ngay: - app/models/post.rb và migration tạo bảng posts. - PostsController với 7 action…
Khác nhau ở hai trục: có chạy validation không và thất bại thì báo bằng cách nào. Method Chạy validation Khi thất bại --- --- --- save Có Trả false save! Có Ném ActiveRecord::RecordInvalid update Có Trả false update! Có Ném exception updateattribute Không Bỏ…
pluck lấy thẳng cột từ DB và trả về mảng các giá trị, không dựng object Active Record. - map tải toàn bộ cột và khởi tạo đầy đủ model cho mỗi dòng — tốn RAM nhất. - select vẫn dựng model nhưng chỉ nạp…
Relation là một mô tả query chưa chạy. Rails chỉ gửi SQL xuống DB khi bạn thực sự cần tới dữ liệu — gọi là loading. Kích hoạt query: toa, each, first, last, find, count, sum, pluck, any?, hoặc khi render trong view. Không kích…
Dùng findeach. User.all.each nạp toàn bộ 5 triệu dòng vào RAM trước khi lặp — process rất dễ bị OOM kill. findeach chia thành từng lô (mặc định 1000 record), nạp lô nào xử lý lô đó rồi giải phóng: Lưu ý: findeach ép sắp…
Cả ba đều nạp trước association để tránh N+1, khác ở SQL sinh ra. - preload — luôn chạy query riêng cho mỗi association (SELECT ... WHERE postid IN (...)). Không thể lọc theo cột của bảng liên kết. - eagerload — gộp thành một…
Callback hợp lý khi việc cần làm gắn liền với chính bản ghi và phải đúng trong mọi ngữ cảnh: chuẩn hoá dữ liệu (beforesave { self.email = email.downcase }), sinh slug, dọn file đính kèm khi destroy. Nên tránh khi callback gây side effect…
Rails.cache.fetch đọc cache theo key; nếu chưa có (cache miss) thì chạy block, lưu kết quả rồi trả về. Key phải chứa mọi thứ ảnh hưởng tới kết quả — id, locale, phiên bản dữ liệu. Cách thông dụng là dùng chính record làm key…
Được, nhưng chỉ vì Active Job không gửi object đi — nó serialize thành GlobalID (chuỗi dạng gid://app/User/42) rồi khi worker chạy mới find lại từ DB. Hệ quả cần biết: - Nếu record bị xoá trước khi job chạy, job ném ActiveJob::DeserializationError (bọc RecordNotFound).…
Middleware Rack là một object có call(env) trả về mảng [status, headers, body], và giữ tham chiếu tới middleware kế tiếp (@app). Đăng ký trong config/application.rb: Vị trí quan trọng: đặt càng gần đầu stack thì càng đo được nhiều (kể cả thời gian của…
Khác ở chỗ có chạy SQL không và có nạp record vào bộ nhớ không. - count — luôn chạy SELECT COUNT(), kể cả khi association đã được nạp. Không giữ record. - length — luôn nạp toàn bộ record vào mảng rồi đếm trong…