Claude
Skills
Sign in
Back

rails-action-controller-patterns

Included with Lifetime
$97 forever

Use when action Controller patterns including routing, filters, strong parameters, and REST conventions.

General

What this skill does


# Rails Action Controller Patterns

Master Action Controller patterns for building robust Rails controllers with
proper routing, filters, parameter handling, and RESTful design.

## Overview

Action Controller is the component that handles web requests in Rails. It
processes incoming requests, interacts with models, and renders responses.
Controllers follow the MVC pattern and implement REST conventions by default.

## Installation and Setup

### Generating Controllers

```bash
# Generate a resource controller
rails generate controller Posts index show new create edit update destroy

# Generate a namespaced controller
rails generate controller Admin::Posts index show

# Generate an API-only controller
rails generate controller Api::V1::Posts --no-helper --no-assets
```

### Routing Configuration

```ruby
# config/routes.rb
Rails.application.routes.draw do
  # RESTful resources
  resources :posts

  # Nested resources
  resources :posts do
    resources :comments
  end

  # Namespaced routes
  namespace :admin do
    resources :posts
  end

  # Custom routes
  get 'about', to: 'pages#about'
  root 'posts#index'
end
```

## Core Patterns

### 1. Basic Controller Structure

```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :authenticate_user!, except: [:index, :show]
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  before_action :authorize_post, only: [:edit, :update, :destroy]

  # GET /posts
  def index
    @posts = Post.includes(:user)
                 .order(created_at: :desc)
                 .page(params[:page])
  end

  # GET /posts/:id
  def show
    @comments = @post.comments.includes(:user)
  end

  # GET /posts/new
  def new
    @post = Post.new
  end

  # POST /posts
  def create
    @post = current_user.posts.build(post_params)

    if @post.save
      redirect_to @post, notice: 'Post was successfully created.'
    else
      render :new, status: :unprocessable_entity
    end
  end

  # GET /posts/:id/edit
  def edit
  end

  # PATCH/PUT /posts/:id
  def update
    if @post.update(post_params)
      redirect_to @post, notice: 'Post was successfully updated.'
    else
      render :edit, status: :unprocessable_entity
    end
  end

  # DELETE /posts/:id
  def destroy
    @post.destroy
    redirect_to posts_url, notice: 'Post was successfully deleted.'
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  def authorize_post
    unless @post.user == current_user
      redirect_to posts_path, alert: 'Not authorized'
    end
  end

  def post_params
    params.require(:post).permit(:title, :body, :status, tag_ids: [])
  end
end
```

### 2. Strong Parameters

```ruby
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  # Basic strong parameters
  def user_params
    params.require(:user).permit(:name, :email, :password)
  end

  # Nested attributes
  def user_params_with_profile
    params.require(:user).permit(
      :name, :email,
      profile_attributes: [:bio, :avatar, :website]
    )
  end

  # Arrays of permitted attributes
  def post_params
    params.require(:post).permit(
      :title, :body,
      tag_ids: [],
      images: []
    )
  end

  # Conditional parameters
  def user_params
    permitted = [:name, :email]
    permitted << :admin if current_user.admin?
    params.require(:user).permit(*permitted)
  end

  # Deep nested attributes
  def organization_params
    params.require(:organization).permit(
      :name,
      departments_attributes: [
        :id, :name, :_destroy,
        employees_attributes: [:id, :name, :role, :_destroy]
      ]
    )
  end

  # JSON parameters
  def config_params
    params.require(:config).permit(
      settings: [:theme, :notifications, :language],
      preferences: {}
    )
  end
end
```

### 3. Filters and Callbacks

```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # Before filters
  before_action :authenticate_user!
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :set_time_zone, if: :user_signed_in?

  # After filters
  after_action :log_activity
  after_action :set_cache_headers

  # Around filters
  around_action :measure_action_time

  private

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
  end

  def set_time_zone
    Time.zone = current_user.time_zone
  end

  def log_activity
    ActivityLogger.log(controller_name, action_name, current_user)
  end

  def set_cache_headers
    response.headers['Cache-Control'] = 'no-cache, no-store'
  end

  def measure_action_time
    start = Time.current
    yield
    duration = Time.current - start
    Rails.logger.info "Action took #{duration}s"
  end
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  skip_before_action :authenticate_user!, only: [:index, :show]
  before_action :set_post, only: [:show, :edit, :update]
  before_action :verify_ownership, only: [:edit, :update]

  prepend_before_action :load_categories
  append_before_action :track_view, only: [:show]

  private

  def verify_ownership
    redirect_to root_path unless @post.user == current_user
  end

  def load_categories
    @categories = Category.all
  end

  def track_view
    @post.increment!(:views_count)
  end
end
```

### 4. RESTful Conventions

```ruby
# config/routes.rb
Rails.application.routes.draw do
  resources :posts do
    # Collection routes (no ID)
    collection do
      get :drafts
      get :search
    end

    # Member routes (with ID)
    member do
      post :publish
      patch :archive
    end

    # Nested resources
    resources :comments, only: [:create, :destroy]
  end

  # Shallow nesting
  resources :authors do
    resources :books, shallow: true
  end

  # Only/except options
  resources :users, only: [:index, :show]
  resources :sessions, except: [:edit, :update]

  # Custom resource names
  resources :posts, path: 'articles'
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  # GET /posts/drafts
  def drafts
    @posts = current_user.posts.draft
    render :index
  end

  # GET /posts/search
  def search
    @posts = Post.search(params[:q])
    render :index
  end

  # POST /posts/:id/publish
  def publish
    @post = Post.find(params[:id])
    if @post.publish!
      redirect_to @post, notice: 'Post published'
    else
      redirect_to @post, alert: 'Could not publish post'
    end
  end
end
```

### 5. Rendering Responses

```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])

    respond_to do |format|
      format.html # Renders show.html.erb by default
      format.json { render json: @post }
      format.xml { render xml: @post }
      format.pdf { render pdf: @post }
    end
  end

  def create
    @post = Post.new(post_params)

    if @post.save
      # Redirect to a URL
      redirect_to @post, notice: 'Created'

      # Redirect back with fallback
      redirect_back fallback_location: root_path, notice: 'Created'
    else
      # Render a template with status
      render :new, status: :unprocessable_entity
    end
  end

  def export
    # Render text
    render plain: 'Export complete'

    # Render JSON with status
    render json: { status: 'ok' }, status: :ok

    # Render nothing
    head :no_content

    # Render file
    send_file '/path/to/file.pdf',
      filename: 'document.pdf',
      type: 'application/pdf',
      disposition: 'attachment'

    # Stream file
    send_data generate_csv, filename: 'report.csv',
      type: 'text/csv',
      disposition: 'inline'
  end

  def partial_update
    # Render partial
    render partial: 'post', locals: { post: @post }

    # Render collection
    render partial: 'post', collection: @posts

    # Render with layout
    render 'special_layout', 

Related in General