module JSONAPI::ActsAsResourceController

Constants

ALL_MEDIA_TYPES
MEDIA_TYPE_MATCHER

Attributes

response_document[R]

Public Class Methods

included(base) click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 8
def self.included(base)
  base.extend ClassMethods
  base.include Callbacks
  base.cattr_reader :server_error_callbacks
  base.define_jsonapi_resources_callbacks :process_operations,
                                          :transaction
end

Public Instance Methods

create() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 30
def create
  process_request
end
create_relationship() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 34
def create_relationship
  process_request
end
destroy() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 46
def destroy
  process_request
end
destroy_relationship() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 50
def destroy_relationship
  process_request
end
index() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 18
def index
  process_request
end
process_operation(operation) click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 139
def process_operation(operation)
  result = operation.process
  response_document.add_result(result, operation)
end
process_operations(transactional) { || ... } click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 123
def process_operations(transactional)
  if transactional
    run_callbacks :transaction do
      ActiveRecord::Base.transaction do
        yield
      end
    end
  else
    begin
      yield
    rescue ActiveRecord::Rollback
      # Can't rollback without transaction, so just ignore it
    end
  end
end
process_request() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 78
def process_request
  @response_document = create_response_document

  unless verify_content_type_header && verify_accept_header
    render_response_document
    return
  end

  request_parser = JSONAPI::RequestParser.new(
      params,
      context: context,
      key_formatter: key_formatter,
      server_error_callbacks: (self.class.server_error_callbacks || []))

  transactional = request_parser.transactional?

  begin
    process_operations(transactional) do
      run_callbacks :process_operations do
        request_parser.each(response_document) do |op|
          op.options[:serializer] = resource_serializer_klass.new(
              op.resource_klass,
              include_directives: op.options[:include_directives],
              fields: op.options[:fields],
              base_url: base_url,
              key_formatter: key_formatter,
              route_formatter: route_formatter,
              serialization_options: serialization_options,
              controller: self
          )
          op.options[:cache_serializer_output] = !JSONAPI.configuration.resource_cache.nil?

          process_operation(op)
        end
      end
      if response_document.has_errors?
        raise ActiveRecord::Rollback
      end
    end
  rescue => e
    handle_exceptions(e)
  end
  render_response_document
end
show() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 22
def show
  process_request
end
show_relationship() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 26
def show_relationship
  process_request
end
update() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 42
def update
  process_request
end
update_relationship() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 38
def update_relationship
  process_request
end

Private Instance Methods

base_meta() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 226
def base_meta
  base_response_meta
end
base_response_meta() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 222
def base_response_meta
  {}
end
base_url() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 154
def base_url
  @base_url ||= "#{request.protocol}#{request.host_with_port}#{Rails.application.config.relative_url_root}"
end
context() click to toggle source

override to set context

# File lib/jsonapi/acts_as_resource_controller.rb, line 200
def context
  {}
end
create_response_document() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 258
def create_response_document
  JSONAPI::ResponseDocument.new(
      key_formatter: key_formatter,
      base_meta: base_meta,
      base_links: base_response_links,
      request: request
  )
end
handle_exceptions(e) click to toggle source

override this to process other exceptions Note: Be sure to either call super(e) or handle JSONAPI::Exceptions::Error and raise unhandled exceptions

# File lib/jsonapi/acts_as_resource_controller.rb, line 269
def handle_exceptions(e)
  case e
    when JSONAPI::Exceptions::Error
      errors = e.errors
    when ActionController::ParameterMissing
      errors = JSONAPI::Exceptions::ParameterMissing.new(e.param).errors
    else
      if JSONAPI.configuration.exception_class_whitelisted?(e)
        raise e
      else
        if self.class.server_error_callbacks
          self.class.server_error_callbacks.each { |callback|
            safe_run_callback(callback, e)
          }
        end

        # Store exception for other middlewares
        request.env['action_dispatch.exception'] ||= e

        internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
        Rails.logger.error { "Internal Server Error: #{e.message} #{e.backtrace.join("\n")}" }
        errors = internal_server_error.errors
      end
  end

  response_document.add_result(JSONAPI::ErrorsOperationResult.new(errors[0].status, errors), nil)
end
key_formatter() click to toggle source

Control by setting in an initializer:

JSONAPI.configuration.json_key_format = :camelized_key
JSONAPI.configuration.route = :camelized_route

Override if you want to set a per controller key format. Must return an instance of a class derived from KeyFormatter.

# File lib/jsonapi/acts_as_resource_controller.rb, line 214
def key_formatter
  JSONAPI.configuration.key_formatter
end
media_types_for(header) click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 192
def media_types_for(header)
  (request.headers[header] || '')
    .scan(MEDIA_TYPE_MATCHER)
    .to_a
    .map(&:strip)
end
render_response_document() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 234
def render_response_document
  content = response_document.contents

  render_options = {}
  if response_document.has_errors?
    render_options[:json] = content
  else
    # Bypassing ActiveSupport allows us to use CompiledJson objects for cached response fragments
    render_options[:body] = JSON.generate(content)

    if (response_document.status == 201 && content[:data].class != Array) &&
        content['data'] && content['data']['links'] && content['data']['links']['self']
      render_options[:location] = content['data']['links']['self']
    end
  end

  # For whatever reason, `render` ignores :status and :content_type when :body is set.
  # But, we can just set those values directly in the Response object instead.
  response.status = response_document.status
  response.headers['Content-Type'] = JSONAPI::MEDIA_TYPE

  render(render_options)
end
resource_klass() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 146
def resource_klass
  @resource_klass ||= resource_klass_name.safe_constantize
end
resource_klass_name() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 158
def resource_klass_name
  @resource_klass_name ||= "#{self.class.name.underscore.sub(/_controller$/, '').singularize}_resource".camelize
end
resource_serializer_klass() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 150
def resource_serializer_klass
  @resource_serializer_klass ||= JSONAPI::ResourceSerializer
end
route_formatter() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 218
def route_formatter
  JSONAPI.configuration.route_formatter
end
safe_run_callback(callback, error) click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 297
def safe_run_callback(callback, error)
  begin
    callback.call(error)
  rescue => e
    Rails.logger.error { "Error in error handling callback: #{e.message} #{e.backtrace.join("\n")}" }
    internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
    return JSONAPI::ErrorsOperationResult.new(internal_server_error.errors[0].code, internal_server_error.errors)
  end
end
serialization_options() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 204
def serialization_options
  {}
end
valid_accept_media_type?() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 184
def valid_accept_media_type?
  media_types = media_types_for('Accept')

  media_types.blank? || media_types.any? do |media_type|
    (media_type == JSONAPI::MEDIA_TYPE || media_type.start_with?(ALL_MEDIA_TYPES))
  end
end
verify_accept_header() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 174
def verify_accept_header
  unless valid_accept_media_type?
    fail JSONAPI::Exceptions::NotAcceptableError.new(request.accept)
  end
  true
rescue => e
  handle_exceptions(e)
  false
end
verify_content_type_header() click to toggle source
# File lib/jsonapi/acts_as_resource_controller.rb, line 162
def verify_content_type_header
  if ['create', 'create_relationship', 'update_relationship', 'update'].include?(params[:action])
    unless request.content_type == JSONAPI::MEDIA_TYPE
      fail JSONAPI::Exceptions::UnsupportedMediaTypeError.new(request.content_type)
    end
  end
  true
rescue => e
  handle_exceptions(e)
  false
end