askill
active-storage

active-storageSafety 95Repository

This skill should be used when the user asks about "file uploads", "Active Storage", "attachments", "has_one_attached", "has_many_attached", "image variants", "S3", "cloud storage", "direct uploads", "file processing", "image transformations", or needs guidance on handling file uploads in Rails applications.

2 stars
1.2k downloads
Updated 1/24/2026

Package Files

Loading files...
SKILL.md

Active Storage

Comprehensive guide to file uploads and cloud storage in Rails.

Setup

rails active_storage:install
rails db:migrate

This creates:

  • active_storage_blobs - File metadata
  • active_storage_attachments - Polymorphic join table
  • active_storage_variant_records - Cached variant info

Model Configuration

Single Attachment

class User < ApplicationRecord
  has_one_attached :avatar
end

Multiple Attachments

class Article < ApplicationRecord
  has_many_attached :images
end

With Service Selection

class Document < ApplicationRecord
  has_one_attached :file, service: :amazon
end

With Variants

class User < ApplicationRecord
  has_one_attached :avatar do |attachable|
    attachable.variant :thumb, resize_to_limit: [100, 100]
    attachable.variant :medium, resize_to_limit: [300, 300]
  end
end

Attaching Files

From Form Upload

# Controller
def create
  @user = User.new(user_params)
  @user.save
end

def user_params
  params.require(:user).permit(:name, :avatar, images: [])
end
<%# Form %>
<%= form_with model: @user do |form| %>
  <%= form.file_field :avatar %>
  <%= form.file_field :images, multiple: true %>
  <%= form.submit %>
<% end %>

Programmatically

# From file
user.avatar.attach(io: File.open("/path/to/photo.jpg"), filename: "photo.jpg")

# From uploaded file
user.avatar.attach(params[:avatar])

# From URL (downloaded)
user.avatar.attach(
  io: URI.open("https://example.com/photo.jpg"),
  filename: "downloaded.jpg",
  content_type: "image/jpeg"
)

# From string content
user.avatar.attach(
  io: StringIO.new(pdf_content),
  filename: "document.pdf",
  content_type: "application/pdf"
)

Check Attachment Status

user.avatar.attached?  # true/false
user.images.attached?  # true/false
user.images.any?       # true/false
user.images.count      # number of attachments

Displaying Files

URLs

<%# Direct URL %>
<%= url_for(@user.avatar) %>

<%# Download URL %>
<%= rails_blob_path(@user.avatar, disposition: "attachment") %>

<%# Image tag %>
<%= image_tag @user.avatar %>

<%# With fallback %>
<%= image_tag(@user.avatar.attached? ? @user.avatar : "default_avatar.png") %>

Variants (Image Transformations)

<%# On-the-fly transformation %>
<%= image_tag @user.avatar.variant(resize_to_limit: [100, 100]) %>

<%# Named variant %>
<%= image_tag @user.avatar.variant(:thumb) %>

<%# Complex transformations %>
<%= image_tag @user.avatar.variant(
  resize_to_fill: [200, 200],
  format: :webp,
  saver: { quality: 80 }
) %>

Common Transformations

MethodDescription
resize_to_limit: [w, h]Resize to fit within bounds
resize_to_fill: [w, h]Resize and crop to fill
resize_to_fit: [w, h]Resize to fit exactly
resize_and_pad: [w, h]Resize and pad to fill
crop: "100x100+10+10"Crop specific area
rotate: 90Rotate degrees
format: :webpConvert format

Preview (PDFs, Videos)

<%# PDF preview (first page) %>
<%= image_tag @document.file.preview(resize_to_limit: [200, 200]) %>

<%# Video preview (frame) %>
<%= image_tag @article.video.preview(resize_to_limit: [300, 200]) %>

Direct Uploads

Setup

<%# Include JS %>
<%= javascript_include_tag "activestorage" %>

<%# Form with direct upload %>
<%= form_with model: @user do |form| %>
  <%= form.file_field :avatar, direct_upload: true %>
<% end %>

JavaScript Events

// Listen for upload events
addEventListener("direct-upload:initialize", event => {
  const { target, detail } = event
  const { id, file } = detail
  // Show upload UI
})

addEventListener("direct-upload:progress", event => {
  const { id, progress } = event.detail
  // Update progress bar
})

addEventListener("direct-upload:error", event => {
  const { id, error } = event.detail
  // Handle error
})

addEventListener("direct-uploads:end", event => {
  // All uploads complete
})

Storage Services

Configuration

# config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: us-east-1
  bucket: my-app-<%= Rails.env %>

google:
  service: GCS
  project: my-project
  credentials: <%= Rails.root.join("path/to/keyfile.json") %>
  bucket: my-app-<%= Rails.env %>

azure:
  service: AzureStorage
  storage_account_name: myaccount
  storage_access_key: <%= Rails.application.credentials.dig(:azure, :storage_access_key) %>
  container: my-container

mirror:
  service: Mirror
  primary: amazon
  mirrors: [ google, azure ]

Environment Selection

# config/environments/development.rb
config.active_storage.service = :local

# config/environments/production.rb
config.active_storage.service = :amazon

Public Files

# config/storage.yml
amazon_public:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: us-east-1
  bucket: my-public-bucket
  public: true
class Article < ApplicationRecord
  has_many_attached :images, service: :amazon_public
end

Validations

Using active_storage_validations Gem

# Gemfile
gem "active_storage_validations"

# Model
class User < ApplicationRecord
  has_one_attached :avatar

  validates :avatar,
    attached: true,
    content_type: ["image/png", "image/jpeg"],
    size: { less_than: 5.megabytes }
end

class Article < ApplicationRecord
  has_many_attached :images

  validates :images,
    content_type: /\Aimage\/.*\z/,
    size: { less_than: 10.megabytes },
    limit: { max: 10 }
end

Custom Validation

class User < ApplicationRecord
  has_one_attached :avatar

  validate :acceptable_avatar

  private

  def acceptable_avatar
    return unless avatar.attached?

    unless avatar.blob.byte_size <= 5.megabyte
      errors.add(:avatar, "is too large (max 5MB)")
    end

    acceptable_types = ["image/jpeg", "image/png", "image/gif"]
    unless acceptable_types.include?(avatar.content_type)
      errors.add(:avatar, "must be JPEG, PNG, or GIF")
    end
  end
end

Downloading and Processing

# Download content
binary = user.avatar.download

# Open as tempfile
user.avatar.open do |file|
  # file is a Tempfile
  ImageProcessor.process(file.path)
end

# Access metadata
user.avatar.blob.byte_size
user.avatar.blob.content_type
user.avatar.blob.filename
user.avatar.blob.created_at

Removing Attachments

# Remove single attachment
user.avatar.purge        # Sync
user.avatar.purge_later  # Async (recommended)

# Remove specific from many
user.images.find(attachment_id).purge_later

# Remove all
user.images.purge_later

Eager Loading

# Avoid N+1 queries
@users = User.with_attached_avatar
@articles = Article.with_attached_images

# Named scope automatically created
User.with_attached_avatar.where(active: true)

Testing

# test/models/user_test.rb
require "test_helper"

class UserTest < ActiveSupport::TestCase
  test "can attach avatar" do
    user = users(:one)

    user.avatar.attach(
      io: file_fixture("avatar.png").open,
      filename: "avatar.png",
      content_type: "image/png"
    )

    assert user.avatar.attached?
  end
end

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

94/100Analyzed 2/23/2026

Comprehensive Active Storage skill with excellent coverage of setup, model configuration, file attachment, display, variants, direct uploads, storage services (S3, GCS, Azure), validations, and testing. Contains clear code examples and structured guidance. The description serves as an effective trigger/when-to-use section. Located in a dedicated skills folder within a marketplace repository, suggesting reusability is intended. Minor issue: 'testing' tag is narrow compared to the broader content."

95
95
95
95
95

Metadata

Licenseunknown
Version1.0.0
Updated1/24/2026
Publisherbastos

Tags

testing