ruby 51 lines · 9 steps

Validating and serving avatars with Active Storage in Rails

A Rails model attaches an image, validates its size and type, and exposes processed variants while a controller handles upload and removal.

Explained by highlit
1class User < ApplicationRecord
2 has_one_attached :avatar
3 
4 validate :acceptable_avatar
5 
6 def avatar_thumbnail
7 avatar.variant(resize_to_fill: [100, 100], format: :webp).processed
8 end
9 
10 def avatar_medium
11 avatar.variant(resize_to_limit: [400, 400], format: :webp, saver: { quality: 80 }).processed
12 end
13 
14 private
15 
16 def acceptable_avatar
17 return unless avatar.attached?
18 
19 unless avatar.blob.byte_size <= 5.megabytes
20 errors.add(:avatar, "is too big (maximum is 5MB)")
21 end
22 
23 acceptable_types = %w[image/png image/jpeg image/webp]
24 unless acceptable_types.include?(avatar.blob.content_type)
25 errors.add(:avatar, "must be a PNG, JPEG, or WebP")
26 end
27 end
28end
29 
30class AvatarsController < ApplicationController
31 before_action :authenticate_user!
32 
33 def update
34 if current_user.update(avatar_params)
35 redirect_to edit_profile_path, notice: "Avatar updated."
36 else
37 render "profiles/edit", status: :unprocessable_entity
38 end
39 end
40 
41 def destroy
42 current_user.avatar.purge_later
43 redirect_to edit_profile_path, notice: "Avatar removed."
44 end
45 
46 private
47 
48 def avatar_params
49 params.require(:user).permit(:avatar)
50 end
51end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Active Storage attachments need explicit validations since the framework won't enforce size or content type for you.
  2. 2Defining variants as model methods keeps image-processing rules in one place and reusable across views.
  3. 3Use purge_later to remove blobs asynchronously rather than blocking the request on deletion.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Validating and serving avatars with Active Storage in Rails — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code