class Onsi::ParamsParseOperation

@private

Parse a root object.

Constants

MULTI_SOURCE_PATH
Result

Attributes

attributes[R]
data[R]
included[R]
relationships[R]
results[R]

Public Class Methods

new(data, attributes, relationships, included) click to toggle source
# File lib/onsi/params_parse_operation.rb, line 19
def initialize(data, attributes, relationships, included)
  @data = data
  data.require(:type)
  @attributes = Array(attributes).map(&:to_s)
  @relationships = Array(relationships)
  @included = included
  @results = Result.new(data.fetch(:id, 'ambiguous'), {}, {})
end

Public Instance Methods

perform() click to toggle source
# File lib/onsi/params_parse_operation.rb, line 28
def perform
  parse_attributes!
  parse_relationships!
  results
end

Private Instance Methods

fetch_relationship(optional, name, relationships) click to toggle source
# File lib/onsi/params_parse_operation.rb, line 167
def fetch_relationship(optional, name, relationships)
  if optional
    payload = relationships.require(name).permit!.to_h
    return [] if payload[:data].is_a?(Array)

    nil
  else
    relationships.require(name).require(:data)
  end
end
find_included(type, id) click to toggle source
# File lib/onsi/params_parse_operation.rb, line 123
def find_included(type, id)
  included.select do |param|
    next false unless param.require(:type).to_sym == type.to_sym
    next true unless id.present?
    next true if id == MULTI_SOURCE_PATH

    param.require(:id) == id
  end
end
parse_attributes!() click to toggle source
# File lib/onsi/params_parse_operation.rb, line 36
def parse_attributes!
  return if attributes.empty?

  attrs = data.require(:attributes).permit!
  permitted = attrs.to_h.select { |key, _| attributes.include?(key.to_s) }
  results.attributes.merge!(permitted)
end
parse_included(relationship_key, relationship_params) click to toggle source

Parse Included Relationships

# File lib/onsi/params_parse_operation.rb, line 65
def parse_included(relationship_key, relationship_params)
  relationship_key.each do |key, value|
    error_message = "Must specify an array of permitted values for relationship #{key}"
    raise ArgumentError, error_message unless value.is_a?(Array)

    parse_included_relationship(key, value, relationship_params)
  end
end
parse_included_relationship(key, value, params) click to toggle source
# File lib/onsi/params_parse_operation.rb, line 74
def parse_included_relationship(key, value, params)
  relationship = params.fetch(key.to_sym, nil)
  return if relationship.nil?

  included_path = relationship.require(:data).fetch(:source, nil)
  return if included_path.nil?

  root, type, id = included_path.split('/', 4).reject(&:empty?).map(&:presence)

  unless root.present?
    raise Onsi::Errors::IncludedParamError.new("Invalid Source: /#{root}", included_path)
  end

  unless root == 'included'
    raise Onsi::Errors::IncludedParamError.new("Invalid Source: /#{root} is not included", included_path)
  end

  included_objects = find_included(type, id)

  if included_objects.nil? || included_objects.empty?
    raise Onsi::Errors::IncludedParamError.new("Invalid Source: Unable to find included.", included_path)
  end

  if included_objects.count != 1 && id != MULTI_SOURCE_PATH
    raise Onsi::Errors::IncludedParamError.new("Invalid Source: Unable to disambiguate included. #{type}", included_path)
  end

  attributes = value.reject { |val| val.is_a?(Hash) }.map(&:to_s)
  relationships = value.select { |val| val.is_a?(Hash) }.map { |hash| hash[:relationships] }.flatten

  parsed_included = included_objects.map do |object|
    self.class.new(object, attributes, relationships, included).perform
  end

  results.relationships[type] = {}

  case id
  when nil
    results.relationships[type] = parsed_included.first.flattened
  when MULTI_SOURCE_PATH
    results.relationships[type] = {}
    parsed_included.each do |included|
      results.relationships[type][included.id] = included.flattened
    end
  else
    results.relationships[type] = parsed_included.first.flattened
  end
end
parse_relationship(relationship_key, relationship_params) click to toggle source

Parse Simple Relationships

# File lib/onsi/params_parse_operation.rb, line 137
def parse_relationship(relationship_key, relationship_params)
  optional, name = parse_relationship_name(relationship_key)
  return unless relationship_params.key?(name)

  relationship_data = fetch_relationship(optional, name, relationship_params)

  case relationship_data
  when nil
    results.relationships["#{name}_id".to_sym] = nil
  when Array
    ids = relationship_data.map { |r| parse_relationship_data(r).last }
    results.relationships["#{name.to_s.singularize}_ids".to_sym] = ids
  else
    _type, id = parse_relationship_data(relationship_data)
    results.relationships["#{name}_id".to_sym] = id
  end
end
parse_relationship_data(data) click to toggle source
# File lib/onsi/params_parse_operation.rb, line 160
def parse_relationship_data(data)
  [
    data.require(:type),
    data.require(:id)
  ]
end
parse_relationship_name(name) click to toggle source
# File lib/onsi/params_parse_operation.rb, line 155
def parse_relationship_name(name)
  name = name.to_s
  [name.start_with?('?'), name.gsub(/\A\?/, '')]
end
parse_relationships!() click to toggle source
# File lib/onsi/params_parse_operation.rb, line 44
def parse_relationships!
  return if relationships.empty?

  relationship_params = data.require(:relationships)

  relationships.each do |relationship_key|
    case relationship_key
    when String, Symbol
      parse_relationship(relationship_key, relationship_params)
    when Hash
      parse_included(relationship_key, relationship_params)
    else
      raise ArgumentError, "Unexpected type for relationship #{relationship_key}"
    end
  end
end