Class: Railsful::Deserializer

Inherits:
Object
  • Object
show all
Defined in:
lib/railsful/deserializer.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params) ⇒ Deserializer

Returns a new instance of Deserializer.



12
13
14
# File 'lib/railsful/deserializer.rb', line 12

def initialize(params)
  @params = params
end

Instance Attribute Details

#paramsObject (readonly)

Returns the value of attribute params.



10
11
12
# File 'lib/railsful/deserializer.rb', line 10

def params
  @params
end

Instance Method Details

#attributes(params) ⇒ Hash

First level attributes from data object.

:reek:FeatureEnvy

Returns:

  • (Hash)


35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 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) ⇒ Object

rubocop:enable Naming/PredicateName



131
132
133
134
135
136
137
138
139
140
# 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

#deserializeObject

Deserializes the given params.

:reek:FeatureEnvy



19
20
21
22
23
24
25
26
27
28
# 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) ⇒ Hash, NilClass

Fetch the included object for a given relationship.

:reek:UtilityFunction

Returns:

  • (Hash, NilClass)

    The extracted included hash.



98
99
100
101
102
103
104
105
106
107
108
109
# 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) ⇒ Object

rubocop:disable Naming/PredicateName



120
121
122
123
124
125
126
127
128
# 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) ⇒ Hash

Fetches all included associations/relationships from the included hash.

:reek:UtilityFunction :reek:FeatureEnvy

Returns:

  • (Hash)


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 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) ⇒ Object



111
112
113
114
115
116
117
# 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