Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
210: Active Storage for File Uploads
I remember the first time I tried to handle file uploads in a Rails app before Active Storage existed. I did what seemed logical: I added a profile_picture_url string column to my users table, wrote some custom code to move files from a temp folder to a /public/uploads directory, and spent an entire weekend fighting with permissions and broken links. It was a nightmare.
The String Column Trap
Let's say we're building a simple member directory. I'll start the way I used to, just to show you why it's a bad idea. I might try to generate a migration like this:
rails generate migration AddAvatarToUsers avatar:string
The logic here is that I'll just store the filename (like "me.jpg") in the database and look it up in a folder. But wait—what happens when I move the app to a different server? Or what if I want to move my images to Amazon S3 or Google Cloud Storage? I'd have to rewrite my entire file-handling logic. It's brittle. This is exactly why Rails gave us Active Storage.
Letting Rails Handle the Heavy Lifting
Instead of managing columns ourselves, Active Storage uses a polymorphic approach. It creates its own tables to track files, meaning our users table stays clean. First, I need to actually install the framework:
rails active_storage:install
rails db:migrate
If you look at your schema.rb now, you'll see active_storage_blobs and active_storage_attachments. I don't need to touch these; Rails uses them as a "join table" between my model and the actual file data.
Now, I'll go into my User model and tell Rails that a user can have one avatar:
class User < ApplicationRecord
has_one_attached :avatar
end
Notice I didn't run a migration to add an avatar column to the users table. That's the part that usually trips people up. The "attachment" exists in those separate Active Storage tables, not in the user record itself.
The Parameter Wall
I've got the model set up, so now I'll add a file field to my form:
<%= form_with model: @user do |f| %>
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.submit %>
<% end %>
I'll try to upload a photo, hit submit, and... nothing. The user saves, but the avatar is still empty. I check my server logs and see this classic warning: Unpermitted parameter: :avatar.
Right. I forgot that since avatar is now a parameter being passed to the controller, I have to explicitly allow it in my Strong Parameters. I'll head over to the users_controller.rb and update the permit list:
def user_params
params.require(:user).permit(:name, :email, :avatar)
end
Now when I refresh and upload, the file actually sticks. Rails handles the upload to the local disk (by default) and creates the necessary entries in the blobs table automatically.
Actually Seeing the Image
Now for the final hurdle: displaying it. I can't just do @user.avatar because that returns an Attached::One object, not a URL. If I try to put that in an image tag, it'll just crash or show nothing.
I'll try using the url_for helper, which tells Rails to generate a temporary, signed URL for the file:
<% if @user.avatar.attached? %>
<%= image_tag @user.avatar %>
<% else %>
<%= image_tag "default-avatar.png" %>
<% end %>
Wait, I noticed the image is huge—like, 4000 pixels wide. My layout is completely broken. I can't just rely on CSS for this; I need to actually resize the image. This is where I'd usually reach for a gem like CarrierWave, but Rails has built-in variants now.
I'll change my image tag to use a variant:
<%= image_tag @user.avatar.variant(resize_to_limit: [100, 100]) %>
The first time I load this page, it might be a bit slow because Rails is processing the image. But after that, it caches the resized version. Now we have a clean, managed upload system without a single custom column in our users table.
📋 Practical Task
Implementing a Project Portfolio Header
You are building a portfolio app where each Project needs a header image. Currently, the Project model only has a title and description.
- Install Active Storage in the application.
- Update the
Projectmodel to allow a single attached file namedheader_image. - Update the
ProjectsControllerto permit theheader_imageparameter. - Modify the project "New" and "Edit" forms to include a file upload field for the header image.
- In the project "Show" view, display the uploaded image, but ensure it is resized to a maximum of 800x400 pixels using a variant. Provide a fallback placeholder image if no header is attached.
There are no comments for now.