class JsonapiCompliable::Deserializer
Responsible for parsing incoming write payloads
Given a PUT payload like:
{ data: { id: '1', type: 'posts', attributes: { title: 'My Title' }, relationships: { author: { data: { id: '1', type: 'authors' } } } }, included: [ { id: '1' type: 'authors', attributes: { name: 'Joe Author' } } ] }
You can now easily deal with this payload:
deserializer.attributes # => { id: '1', title: 'My Title' } deserializer.meta # => { type: 'posts', method: :update } deserializer.relationships # { # author: { # meta: { ... }, # attributes: { ... }, # relationships: { ... } # } # }
When creating objects, we accept a temp-id
so that the client can track the object it just created. Expect this in meta
:
{ type: 'authors', method: :create, temp_id: 'abc123' }
Public Class Methods
@param payload [Hash] The incoming payload with symbolized keys @param env [Hash] the Rack env (e.g. request.env
).
# File lib/jsonapi_compliable/deserializer.rb, line 50 def initialize(payload, env) @payload = payload || {} @payload = @payload[:_jsonapi] if @payload.has_key?(:_jsonapi) @env = env validate_content_type end
Public Instance Methods
@return [Hash] the raw :attributes hash + id
# File lib/jsonapi_compliable/deserializer.rb, line 76 def attributes @attributes ||= raw_attributes.tap do |hash| hash[:id] = id if id end end
Override the attributes # @see attributes
# File lib/jsonapi_compliable/deserializer.rb, line 84 def attributes=(attrs) @attributes = attrs end
@return [Hash] the raw :data value of the payload
# File lib/jsonapi_compliable/deserializer.rb, line 66 def data @payload[:data] || {} end
@return [String] the raw :id value of the payload
# File lib/jsonapi_compliable/deserializer.rb, line 71 def id data[:id] end
Parses the relationships
recursively and builds an all-hash include directive like
{ posts: { comments: {} } }
Relationships that have been marked for destruction will NOT be part of the include directive.
@return [Hash] the include directive
# File lib/jsonapi_compliable/deserializer.rb, line 117 def include_directive(memo = {}, relationship_node = nil) relationship_node ||= relationships relationship_node.each_pair do |name, relationship_payload| merge_include_directive(memo, name, relationship_payload) end memo end
'meta' information about this resource. Includes:
type
: the jsonapi type method
: create/update/destroy/disassociate. Based on the request env or the method
within the relationships
hash temp_id
: the temp-id
, if specified
@return [Hash]
# File lib/jsonapi_compliable/deserializer.rb, line 95 def meta { type: data[:type], temp_id: data[:'temp-id'], method: method } end
@return [Hash] the relationships hash
# File lib/jsonapi_compliable/deserializer.rb, line 104 def relationships @relationships ||= process_relationships(raw_relationships) end
checks Content-Type header and prints a warning if it doesn't seem correct
# File lib/jsonapi_compliable/deserializer.rb, line 58 def validate_content_type content_type = @env['CONTENT_TYPE'] || "" if !(content_type.include?("application/json") || content_type.include?("application/vnd.api+json")) print("WARNING - JSONAPI Compliable :: Content-Type header appears to be set to an invalid value: #{content_type}\n") end end
Private Instance Methods
# File lib/jsonapi_compliable/deserializer.rb, line 164 def deep_merge!(a, b) JsonapiCompliable::Util::Hash.deep_merge!(a, b) end
# File lib/jsonapi_compliable/deserializer.rb, line 138 def included @payload[:included] || [] end
# File lib/jsonapi_compliable/deserializer.rb, line 129 def merge_include_directive(memo, name, relationship_payload) arrayified = [relationship_payload].flatten return if arrayified.all? { |rp| removed?(rp) } memo[name] ||= {} deep_merge!(memo[name], sub_directives(memo[name], arrayified)) memo end
# File lib/jsonapi_compliable/deserializer.rb, line 142 def method case @env['REQUEST_METHOD'] when 'POST' then :create when 'PUT', 'PATCH' then :update when 'DELETE' then :destroy end end
# File lib/jsonapi_compliable/deserializer.rb, line 180 def process_relationship(relationship_data) if relationship_data.is_a?(Array) relationship_data.map do |rd| process_relationship_datum(rd) end else process_relationship_datum(relationship_data) end end
# File lib/jsonapi_compliable/deserializer.rb, line 190 def process_relationship_datum(datum) temp_id = datum[:'temp-id'] included_object = included.find do |i| next unless i[:type] == datum[:type] (i[:id] && i[:id] == datum[:id]) || (i[:'temp-id'] && i[:'temp-id'] == temp_id) end included_object ||= {} included_object[:relationships] ||= {} attributes = included_object[:attributes] || {} attributes[:id] = datum[:id] if datum[:id] relationships = process_relationships(included_object[:relationships] || {}) method = datum[:method] method = method.to_sym if method { meta: { jsonapi_type: datum[:type], temp_id: temp_id, method: method }, attributes: attributes, relationships: relationships } end
# File lib/jsonapi_compliable/deserializer.rb, line 168 def process_relationships(relationship_hash) {}.tap do |hash| relationship_hash.each_pair do |name, relationship_payload| name = name.to_sym if relationship_payload[:data] hash[name] = process_relationship(relationship_payload[:data]) end end end end
# File lib/jsonapi_compliable/deserializer.rb, line 218 def raw_attributes if data data[:attributes] || {} else {} end end
# File lib/jsonapi_compliable/deserializer.rb, line 226 def raw_relationships if data data[:relationships] || {} else {} end end
# File lib/jsonapi_compliable/deserializer.rb, line 150 def removed?(relationship_payload) method = relationship_payload[:meta][:method] [:disassociate, :destroy].include?(method) end
# File lib/jsonapi_compliable/deserializer.rb, line 155 def sub_directives(memo, relationship_payloads) {}.tap do |subs| relationship_payloads.each do |rp| sub_directive = include_directive(memo, rp[:relationships]) deep_merge!(subs, sub_directive) end end end