module RESTFramework::BaseControllerMixin

This module provides the common functionality for any controller mixins, a `root` action, and the ability to route arbitrary actions with `extra_actions`. This is also where `api_response` is defined.

Public Class Methods

included(base) click to toggle source
# File lib/rest_framework/controller_mixins/base.rb, line 32
def self.included(base)
  if base.is_a? Class
    base.extend ClassMethods

    # Add class attributes (with defaults) unless they already exist.
    {
      filter_pk_from_request_body: true,
      exclude_body_fields: [:created_at, :created_by, :updated_at, :updated_by],
      accept_generic_params_as_body_params: true,
      extra_actions: nil,
      extra_member_actions: nil,
      filter_backends: nil,
      paginator_class: nil,
      page_size: 20,
      page_query_param: 'page',
      page_size_query_param: 'page_size',
      max_page_size: nil,
      serializer_class: nil,
      serialize_to_json: true,
      serialize_to_xml: true,
      singleton_controller: nil,
      skip_actions: nil,
    }.each do |a, default|
      unless base.respond_to?(a)
        base.class_attribute(a)

        # Set default manually so we can still support Rails 4. Maybe later we can use the default
        # parameter on `class_attribute`.
        base.send(:"#{a}=", default)
      end
    end

    # Alias `extra_actions` to `extra_collection_actions`.
    unless base.respond_to?(:extra_collection_actions)
      base.alias_method(:extra_collection_actions, :extra_actions)
      base.alias_method(:extra_collection_actions=, :extra_actions=)
    end

    # Skip csrf since this is an API.
    base.skip_before_action(:verify_authenticity_token) rescue nil

    # Handle some common exceptions.
    base.rescue_from(ActiveRecord::RecordNotFound, with: :record_not_found)
    base.rescue_from(ActiveRecord::RecordInvalid, with: :record_invalid)
    base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
    base.rescue_from(ActiveRecord::RecordNotDestroyed, with: :record_not_destroyed)
  end
end

Public Instance Methods

root() click to toggle source

Default action for API root.

# File lib/rest_framework/controller_mixins/base.rb, line 11
def root
  api_response({message: "This is the root of your awesome API!"})
end

Protected Instance Methods

api_response(payload, html_kwargs: nil, **kwargs) click to toggle source

Helper to render a browsable API for `html` format, along with basic `json`/`xml` formats, and with support or passing custom `kwargs` to the underlying `render` calls.

# File lib/rest_framework/controller_mixins/base.rb, line 127
def api_response(payload, html_kwargs: nil, **kwargs)
  html_kwargs ||= {}
  json_kwargs = kwargs.delete(:json_kwargs) || {}
  xml_kwargs = kwargs.delete(:xml_kwargs) || {}

  # Raise helpful error if payload is nil. Usually this happens when a record is not found (e.g.,
  # when passing something like `User.find_by(id: some_id)` to `api_response`). The caller should
  # actually be calling `find_by!` to raise ActiveRecord::RecordNotFound and allowing the REST
  # framework to catch this error and return an appropriate error response.
  if payload.nil?
    raise RESTFramework::NilPassedToAPIResponseError
  end

  respond_to do |format|
    if payload == ''
      format.json {head :no_content} if self.serialize_to_json
      format.xml {head :no_content} if self.serialize_to_xml
    else
      format.json {
        jkwargs = kwargs.merge(json_kwargs)
        render(json: payload, layout: false, **jkwargs)
      } if self.serialize_to_json
      format.xml {
        xkwargs = kwargs.merge(xml_kwargs)
        render(xml: payload, layout: false, **xkwargs)
      } if self.serialize_to_xml
      # TODO: possibly support more formats here if supported?
    end
    format.html {
      @payload = payload
      if payload == ''
        @json_payload = '' if self.serialize_to_json
        @xml_payload = '' if self.serialize_to_xml
      else
        @json_payload = payload.to_json if self.serialize_to_json
        @xml_payload = payload.to_xml if self.serialize_to_xml
      end
      @template_logo_text ||= "Rails REST Framework"
      @title ||= self.controller_name.camelize
      @route_groups ||= RESTFramework::Utils::get_routes(Rails.application.routes, request)
      hkwargs = kwargs.merge(html_kwargs)
      begin
        render(**hkwargs)
      rescue ActionView::MissingTemplate  # fallback to rest_framework layout
        hkwargs[:layout] = "rest_framework"
        hkwargs[:html] = ''
        render(**hkwargs)
      end
    }
  end
end
get_filter_backends() click to toggle source

Helper to get filtering backends, defaulting to no backends.

# File lib/rest_framework/controller_mixins/base.rb, line 89
def get_filter_backends
  return self.class.filter_backends || []
end
get_filtered_data(data) click to toggle source

Helper to filter an arbitrary data set over all configured filter backends.

# File lib/rest_framework/controller_mixins/base.rb, line 94
def get_filtered_data(data)
  self.get_filter_backends.each do |filter_class|
    filter = filter_class.new(controller: self)
    data = filter.get_filtered_data(data)
  end

  return data
end
get_serializer_class() click to toggle source

Helper to get the configured serializer class.

# File lib/rest_framework/controller_mixins/base.rb, line 84
def get_serializer_class
  return self.class.serializer_class
end
record_invalid(e) click to toggle source
# File lib/rest_framework/controller_mixins/base.rb, line 103
def record_invalid(e)
  return api_response({
    message: "Record invalid.", exception: e, errors: e.record&.errors
  }, status: 400)
end
record_not_destroyed(e) click to toggle source
# File lib/rest_framework/controller_mixins/base.rb, line 119
def record_not_destroyed(e)
  return api_response({
    message: "Record not destroyed.", exception: e, errors: e.record&.errors
  }, status: 406)
end
record_not_found(e) click to toggle source
# File lib/rest_framework/controller_mixins/base.rb, line 109
def record_not_found(e)
  return api_response({message: "Record not found.", exception: e}, status: 404)
end
record_not_saved(e) click to toggle source
# File lib/rest_framework/controller_mixins/base.rb, line 113
def record_not_saved(e)
  return api_response({
    message: "Record not saved.", exception: e, errors: e.record&.errors
  }, status: 406)
end