module ActiveModelSerializers::Adapter::JsonApi::Deserialization
NOTE(Experimental): This is an experimental feature. Both the interface and internals could be subject to changes.
Constants
- InvalidDocument
Public Instance Methods
@api private
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 154 def field_key(field, options) (options[:keys] || {}).fetch(field.to_sym, field).to_sym end
@api private
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 145 def filter_fields(fields, options) if (only = options[:only]) fields.slice!(*Array(only).map(&:to_s)) elsif (except = options[:except]) fields.except!(*Array(except).map(&:to_s)) end end
Same as parse!, but returns an empty hash instead of raising InvalidDocument
on invalid payloads.
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 85 def parse(document, options = {}) document = document.dup.permit!.to_h if document.is_a?(ActionController::Parameters) validate_payload(document) do |invalid_document, reason| yield invalid_document, reason if block_given? return {} end primary_data = document['data'] attributes = primary_data['attributes'] || {} attributes['id'] = primary_data['id'] if primary_data['id'] relationships = primary_data['relationships'] || {} filter_fields(attributes, options) filter_fields(relationships, options) hash = {} hash.merge!(parse_attributes(attributes, options)) hash.merge!(parse_relationships(relationships, options)) hash end
Transform a JSON API document, containing a single data object, into a hash that is ready for ActiveRecord::Base.new() and such. Raises InvalidDocument
if the payload is not properly formatted.
@param [Hash|ActionController::Parameters] document @param [Hash] options
only: Array of symbols of whitelisted fields. except: Array of symbols of blacklisted fields. keys: Hash of translated keys (e.g. :author => :user). polymorphic: Array of symbols of polymorphic fields.
@return [Hash]
@example
document = { data: { id: 1, type: 'post', attributes: { title: 'Title 1', date: '2015-12-20' }, associations: { author: { data: { type: 'user', id: 2 } }, second_author: { data: nil }, comments: { data: [{ type: 'comment', id: 3 },{ type: 'comment', id: 4 }] } } } } parse(document) #=> # { # title: 'Title 1', # date: '2015-12-20', # author_id: 2, # second_author_id: nil # comment_ids: [3, 4] # } parse(document, only: [:title, :date, :author], keys: { date: :published_at }, polymorphic: [:author]) #=> # { # title: 'Title 1', # published_at: '2015-12-20', # author_id: '2', # author_type: 'people' # }
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 77 def parse!(document, options = {}) parse(document, options) do |invalid_payload, reason| fail InvalidDocument, "Invalid payload (#{reason}): #{invalid_payload}" end end
@api private
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 159 def parse_attributes(attributes, options) transform_keys(attributes, options) .map { |(k, v)| { field_key(k, options) => v } } .reduce({}, :merge) end
Given an association name, and a relationship data attribute, build a hash mapping the corresponding ActiveRecord attribute to the corresponding value.
@example
parse_relationship(:comments, [{ 'id' => '1', 'type' => 'comments' }, { 'id' => '2', 'type' => 'comments' }], {}) # => { :comment_ids => ['1', '2'] } parse_relationship(:author, { 'id' => '1', 'type' => 'users' }, {}) # => { :author_id => '1' } parse_relationship(:author, nil, {}) # => { :author_id => nil }
@param [Symbol] assoc_name @param [Hash] assoc_data @param [Hash] options @return [Hash{Symbol, Object}]
@api private
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 183 def parse_relationship(assoc_name, assoc_data, options) prefix_key = field_key(assoc_name, options).to_s.singularize hash = if assoc_data.is_a?(Array) { "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } } else { "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil } end polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym) if polymorphic hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'].classify : nil end hash end
@api private
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 201 def parse_relationships(relationships, options) transform_keys(relationships, options) .map { |(k, v)| parse_relationship(k, v['data'], options) } .reduce({}, :merge) end
@api private
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 208 def transform_keys(hash, options) transform = options[:key_transform] || :underscore CaseTransform.send(transform, hash) end
Checks whether a payload is compliant with the JSON API spec.
@api private rubocop:disable Metrics/CyclomaticComplexity
# File lib/active_model_serializers/adapter/json_api/deserialization.rb, line 112 def validate_payload(payload) unless payload.is_a?(Hash) yield payload, 'Expected hash' return end primary_data = payload['data'] unless primary_data.is_a?(Hash) yield payload, { data: 'Expected hash' } return end attributes = primary_data['attributes'] || {} unless attributes.is_a?(Hash) yield payload, { data: { attributes: 'Expected hash or nil' } } return end relationships = primary_data['relationships'] || {} unless relationships.is_a?(Hash) yield payload, { data: { relationships: 'Expected hash or nil' } } return end relationships.each do |(key, value)| unless value.is_a?(Hash) && value.key?('data') yield payload, { data: { relationships: { key => 'Expected hash with :data key' } } } end end end