class JSONAPI::Utils::Exceptions::ActiveRecord

Attributes

foreign_keys[R]
object[R]
relationship_names[R]
relationships[R]
resource[R]

Public Class Methods

new(object, resource_klass, context) click to toggle source

Construct an error decorator over ActiveRecord objects.

@param object [ActiveRecord::Base] Invalid ActiveRecord object.

e.g.: User.new(name: nil).tap(&:save)

@param resource_klass [JSONAPI::Resource] Resource class to be used for reflection.

e.g.: UserResuource

@return [JSONAPI::Utils::Exceptions::ActiveRecord]

@api public

# File lib/jsonapi/utils/exceptions/active_record.rb, line 18
def initialize(object, resource_klass, context)
  @object   = object
  @resource = resource_klass.new(object, context)

  # Need to reflect on resource's relationships for error reporting.
  @relationships      = resource_klass._relationships.values
  @relationship_names = @relationships.map(&:name).map(&:to_sym)
  @foreign_keys       = @relationships.map(&:foreign_key).map(&:to_sym)
  @resource_key_for   = {}
  @formatted_key      = {}
end

Public Instance Methods

errors() click to toggle source

Decorate errors for AR invalid objects.

@note That's the method used by formatters to build the response's error body.

@return [Array]

@api public

# File lib/jsonapi/utils/exceptions/active_record.rb, line 37
def errors
  object.errors.messages.flat_map do |field, messages|
    messages.map.with_index do |message, index|
      build_error(field, message, index)
    end
  end
end

Private Instance Methods

build_error(field, message, index = 0) click to toggle source

Turn AR error into JSONAPI::Error.

@param field [Symbol] Name of the invalid field

e.g.: :title

@param message [String] Error message

e.g.: "can't be blank"

@param index [Integer] Index of the error detail

@return [JSONAPI::Error]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 60
def build_error(field, message, index = 0)
  error = error_base
    .merge(
      id: id_member(field, index),
      title: message,
      detail: detail_member(field, message)
    ).merge(source_member(field))
  JSONAPI::Error.new(error)
end
detail_member(field, message) click to toggle source

Build the “detail” member value for the JSON API error object.

e.g.: :first_name, "can't be blank" => "First name can't be blank"

@param field [Symbol] Name of the invalid field

e.g.: :first_name

@return [String]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 148
def detail_member(field, message)
  return message if field == :base
  resource_key = resource_key_for(field)
  [translation_for(resource_key), message].join(' ')
end
error_base() click to toggle source

Return the base data used for all errors of this kind.

@return [Hash]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 188
def error_base
  {
    code: JSONAPI::VALIDATION_ERROR,
    status: :unprocessable_entity
  }
end
id_member(field, index) click to toggle source

Build the “id” member value for the JSON API error object.

e.g.: for :first_name, :too_short => "first-name#too-short"

@note The returned value depends on the key formatter type defined

via configuration, e.g.: config.json_key_format = :dasherized_key

@param field [Symbol] Name of the invalid field

e.g.: :first_name

@param index [Integer] Index of the error detail

@return [String]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 84
def id_member(field, index)
  [
    key_format(field),
    key_format(
      object.errors.details
        .dig(field, index, :error)
        .to_s.downcase
        .split
        .join('_')
    )
  ].join('#')
end
key_format(field) click to toggle source

Bring the formatted resource key for a given field.

e.g.: for :first_name => :"first-name"

@note The returned value depends on the key formatter type defined

via configuration, e.g.: config.json_key_format = :dasherized_key

@param field [Symbol] Name of the invalid field

e.g.: :title

@return [Symbol]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 109
def key_format(field)
  @formatted_key[field] ||= JSONAPI.configuration
    .key_formatter
    .format(resource_key_for(field))
    .to_sym
end
resource_key_for(field) click to toggle source

Return the resource's attribute or relationship key name for a given field name.

e.g.: :title => :title, :user_id => :author

@param field [Symbol] Name of the invalid field

e.g.: :title

@return [Symbol]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 163
def resource_key_for(field)
  @resource_key_for[field] ||= begin
    return field unless foreign_keys.include?(field)
    relationships.find { |r| r.foreign_key == field }.name.to_sym
  end
end
source_member(field) click to toggle source

Build the “source” member value for the JSON API error object.

e.g.: :title => "/data/attributes/title"

@param field [Symbol] Name of the invalid field

e.g.: :title

@return [Hash]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 125
def source_member(field)
  resource_key = resource_key_for(field)
  return {} unless field == :base || resource.fetchable_fields.include?(resource_key)
  id = key_format(field)

  pointer =
    if field == :base                               then '/data'
    elsif relationship_names.include?(resource_key) then "/data/relationships/#{id}"
    else "/data/attributes/#{id}"
    end

  { source: { pointer: pointer } }
end
translation_for(field) click to toggle source

Turn the field name into human-friendly one.

e.g.: :first_name => "First name"

@param field [Symbol] Name of the invalid field

e.g.: :first_name

@return [String]

@api private

# File lib/jsonapi/utils/exceptions/active_record.rb, line 179
def translation_for(field)
  object.class.human_attribute_name(field)
end