class Railsful::Deserializer

The deserializer class handles the “unwrapping” of incoming parameters. It translates jsonapi compliant params to those that Rails understands.

Attributes

params[R]

Public Class Methods

new(params) click to toggle source
# File lib/railsful/deserializer.rb, line 12
def initialize(params)
  @params = params
end

Public Instance Methods

attributes(params) click to toggle source

First level attributes from data object.

@return [Hash]

:reek: FeatureEnvy

# File lib/railsful/deserializer.rb, line 35
def attributes(params)
  data = params.fetch(:data, {})

  # Merge the resources attributes. Also merge the id since jsonapi does
  # not allow ids in the attribute body.
  attrs = data.fetch(:attributes, {}).merge(id: data[:id])

  # Get the already existing relationships
  data.fetch(:relationships, {}).each do |type, payload|
    attrs.merge!(relationship(type, payload))
  end

  attrs.compact
end
belongs_to_relationship(type, data) click to toggle source

rubocop:enable Naming/PredicateName

# File lib/railsful/deserializer.rb, line 131
def belongs_to_relationship(type, data)
  # Fetch a possible id from the data.
  relation_id = data[:id]

  # If no ID is provided skip it.
  return {} unless relation_id

  # Build the relationship hash.
  { :"#{type}_id" => relation_id }
end
deserialize() click to toggle source

Deserializes the given params.

:reek: FeatureEnvy

# File lib/railsful/deserializer.rb, line 19
def deserialize
  # Fetch attributes including resource id.
  deserialized = attributes(params)

  # Get the included elements.
  deserialized.deeper_merge!(included_hash(params))

  # Return the deserialized params.
  ActionController::Parameters.new(deserialized)
end
get_included(relation, included) click to toggle source

Fetch the included object for a given relationship.

@return [Hash, NilClass] The extracted included hash.

:reek: UtilityFunction

# File lib/railsful/deserializer.rb, line 98
def get_included(relation, included)
  # Return the attributes of the last found element. But there SHOULD only
  # be one element with the same tempid. If there is a mistake by the client
  # we always take the last.
  found = included.reverse
                  .detect { |inc| inc[:tempid] == relation[:tempid] }

  return nil unless found

  # Return the attributes of the found include hash or an empty hash.
  found.fetch(:attributes, {})
end
has_many_relationship(type, data) click to toggle source

rubocop:disable Naming/PredicateName

# File lib/railsful/deserializer.rb, line 120
def has_many_relationship(type, data)
  return {} unless data.is_a?(Array)

  ids = data.map { |relation| relation[:id] }.compact

  return {} if ids.empty?

  { :"#{type}_ids" => ids }
end
included_hash(params) click to toggle source

Fetches all included associations/relationships from the included hash.

@return [Hash]

:reek: UtilityFunction :reek: FeatureEnvy

# File lib/railsful/deserializer.rb, line 57
def included_hash(params)
  # Gather all necessary data we are working on.
  included = params.fetch(:included, [])
  relationships = params.fetch(:data, {}).fetch(:relationships, {})

  result = {}

  # Make sure that both +included+ and +relationships+ are given.
  # Otherwise we can't do anything and return an empty hash.
  return result if included.empty? || relationships.empty?

  # Iterate over all relationships.
  relationships.each do |type, payload|
    # Get the data value.
    data = payload[:data]

    # Check if we are dealing with a +has_many+ (Array) or +belongs_to+
    # (Hash) relationship.
    if data.is_a?(Array)
      result["#{type}_attributes"] = []

      data.each do |element|
        result["#{type}_attributes"] << get_included(element, included)
      end

      # Remove all nil includes.
      result["#{type}_attributes"].compact!
    else
      result["#{type}_attributes"] = get_included(data, included)
    end
  end

  # Remove all nil includes.
  result.compact
end
relationship(type, payload) click to toggle source
# File lib/railsful/deserializer.rb, line 111
def relationship(type, payload)
  data = payload[:data]

  return has_many_relationship(type, data) if data.is_a?(Array)

  belongs_to_relationship(type, data)
end