lib/jsonapi_compliable/deserializer.rb in jsonapi_compliable-0.6.4 vs lib/jsonapi_compliable/deserializer.rb in jsonapi_compliable-0.6.5

- old
+ new

@@ -1,67 +1,136 @@ +# 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' } class JsonapiCompliable::Deserializer + # @param payload [Hash] The incoming payload with symbolized keys + # @param env [Hash] the Rack env (e.g. +request.env+). def initialize(payload, env) @payload = payload @env = env end + # @return [Hash] the raw :data value of the payload def data @payload[:data] end + # @return [String] the raw :id value of the payload def id data[:id] end + # @return [Hash] the raw :attributes hash + +id+ def attributes @attributes ||= raw_attributes.tap do |hash| hash.merge!(id: id) if id end end - def attributes=(attrs) - @attributes = attrs - end - - def method - case @env['REQUEST_METHOD'] - when 'POST' then :create - when 'PUT', 'PATCH' then :update - when 'DELETE' then :destroy - end - 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] def meta { type: data[:type], temp_id: data[:'temp-id'], method: method } end + # @return [Hash] the relationships hash def relationships @relationships ||= process_relationships(raw_relationships) end - def included - @payload[:included] || [] - 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 def include_directive(memo = {}, relationship_node = nil) relationship_node ||= relationships relationship_node.each_pair do |name, relationship_payload| - arrayified = [relationship_payload].flatten - next if arrayified.all? { |rp| removed?(rp) } - - memo[name] ||= {} - deep_merge!(memo[name], sub_directives(memo[name], arrayified)) + merge_include_directive(memo, name, relationship_payload) end memo end private + + 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 + + def included + @payload[:included] || [] + end + + def method + case @env['REQUEST_METHOD'] + when 'POST' then :create + when 'PUT', 'PATCH' then :update + when 'DELETE' then :destroy + end + end def removed?(relationship_payload) method = relationship_payload[:meta][:method] [:disassociate, :destroy].include?(method) end