src/derails/action_controller/api.rb
# frozen_string_literal: true

# :markup: markdown

require "action_view"
require "action_controller"
require "action_controller/log_subscriber"
require "action_controller/structured_event_subscriber"

module ActionController
  # # Action Controller API
  #
  # API Controller is a lightweight version of ActionController::Base, created for
  # applications that don't require all functionalities that a complete Rails
  # controller provides, allowing you to create controllers with just the features
  # that you need for API only applications.
  #
  # An API Controller is different from a normal controller in the sense that by
  # default it doesn't include a number of features that are usually required by
  # browser access only: layouts and templates rendering, flash, assets, and so
  # on. This makes the entire controller stack thinner, suitable for API
  # applications. It doesn't mean you won't have such features if you need them:
  # they're all available for you to include in your application, they're just not
  # part of the default API controller stack.
  #
  # Normally, `ApplicationController` is the only controller that inherits from
  # `ActionController::API`. All other controllers in turn inherit from
  # `ApplicationController`.
  #
  # A sample controller could look like this:
  #
  #     class PostsController < ApplicationController
  #       def index
  #         posts = Post.all
  #         render json: posts
  #       end
  #     end
  #
  # Request, response, and parameters objects all work the exact same way as
  # ActionController::Base.
  #
  # ## Renders
  #
  # The default API Controller stack includes all renderers, which means you can
  # use `render :json` and siblings freely in your controllers. Keep in mind that
  # templates are not going to be rendered, so you need to ensure your controller
  # is calling either `render` or `redirect_to` in all actions, otherwise it will
  # return `204 No Content`.
  #
  #     def show
  #       post = Post.find(params[:id])
  #       render json: post
  #     end
  #
  # ## Redirects
  #
  # Redirects are used to move from one action to another. You can use the
  # `redirect_to` method in your controllers in the same way as in
  # ActionController::Base. For example:
  #
  #     def create
  #       redirect_to root_url and return if not_authorized?
  #       # do stuff here
  #     end
  #
  # ## Adding New Behavior
  #
  # In some scenarios you may want to add back some functionality provided by
  # ActionController::Base that is not present by default in
  # `ActionController::API`, for instance `MimeResponds`. This module gives you
  # the `respond_to` method. Adding it is quite simple, you just need to include
  # the module in a specific controller or in `ApplicationController` in case you
  # want it available in your entire application:
  #
  #     class ApplicationController < ActionController::API
  #       include ActionController::MimeResponds
  #     end
  #
  #     class PostsController < ApplicationController
  #       def index
  #         posts = Post.all
  #
  #         respond_to do |format|
  #           format.json { render json: posts }
  #           format.xml  { render xml: posts }
  #         end
  #       end
  #     end
  #
  # Make sure to check the modules included in ActionController::Base if you want
  # to use any other functionality that is not provided by `ActionController::API`
  # out of the box.
  class API < Metal
    abstract!

    # Shortcut helper that returns all the ActionController::API modules except the
    # ones passed as arguments:
    #
    #     class MyAPIBaseController < ActionController::Metal
    #       ActionController::API.without_modules(:UrlFor).each do |left|
    #         include left
    #       end
    #     end
    #
    # This gives better control over what you want to exclude and makes it easier to
    # create an API controller class, instead of listing the modules required
    # manually.
    def self.without_modules(*modules)
      modules = modules.map do |m|
        m.is_a?(Symbol) ? ActionController.const_get(m) : m
      end

      MODULES - modules
    end

    MODULES = [
      AbstractController::Rendering,

      UrlFor,
      Redirecting,
      ApiRendering,
      Renderers::All,
      ConditionalGet,
      BasicImplicitRender,
      StrongParameters,
      RateLimiting,
      Caching,

      DataStreaming,
      DefaultHeaders,
      Logging,

      # Before callbacks should also be executed as early as possible, so also include
      # them at the bottom.
      AbstractController::Callbacks,

      # Append rescue at the bottom to wrap as much as possible.
      Rescue,

      # Add instrumentations hooks at the bottom, to ensure they instrument all the
      # methods properly.
      Instrumentation,

      # Params wrapper should come before instrumentation so they are properly showed
      # in logs
      ParamsWrapper
    ]

    MODULES.each do |mod|
      include mod
    end

    ActiveSupport.run_load_hooks(:action_controller_api, self)
    ActiveSupport.run_load_hooks(:action_controller, self)
  end
end
