Class: JSONAPI::ResourceSet

Inherits:
Object
  • Object
show all
Defined in:
lib/jsonapi/resource_set.rb

Overview

Contains a hash of resource types which contain a hash of resources, relationships and primary status keyed by resource id.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(resource_id_tree = nil) ⇒ ResourceSet

Returns a new instance of ResourceSet.



8
9
10
11
# File 'lib/jsonapi/resource_set.rb', line 8

def initialize(resource_id_tree = nil)
  @populated = false
  @resource_klasses = resource_id_tree.nil? ? {} : flatten_resource_id_tree(resource_id_tree)
end

Instance Attribute Details

#populatedObject (readonly)

Returns the value of attribute populated.



6
7
8
# File 'lib/jsonapi/resource_set.rb', line 6

def populated
  @populated
end

#resource_klassesObject (readonly)

Returns the value of attribute resource_klasses.



6
7
8
# File 'lib/jsonapi/resource_set.rb', line 6

def resource_klasses
  @resource_klasses
end

Instance Method Details

#mark_populated!Object



127
128
129
# File 'lib/jsonapi/resource_set.rb', line 127

def mark_populated!
  @populated = true
end

#populate!(serializer, context, find_options) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/jsonapi/resource_set.rb', line 13

def populate!(serializer, context, find_options)
  # For each resource klass we want to generate the caching key

  # Hash for collecting types and ids
  # @type [Hash<Class<Resource>, Id[]]]
  missed_resource_ids = {}

  # Array for collecting CachedResponseFragment::Lookups
  # @type [Lookup[]]
  lookups = []


  # Step One collect all of the lookups for the cache, or keys that don't require cache access
  @resource_klasses.each_key do |resource_klass|

    serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
    context_json = resource_klass.attribute_caching_context(context).to_json
    context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
    context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"

    if resource_klass.caching?
      cache_ids = @resource_klasses[resource_klass].map do |(k, v)|
        # Store the hashcode of the cache_field to avoid storing objects and to ensure precision isn't lost
        # on timestamp types (i.e. string conversions dropping milliseconds)
        [k, resource_klass.hash_cache_field(v[:cache_id])]
      end

      lookups.push(
        CachedResponseFragment::Lookup.new(
          resource_klass,
          serializer_config_key,
          context,
          context_key,
          cache_ids
        )
      )
    else
      missed_resource_ids[resource_klass] = @resource_klasses[resource_klass].keys
    end
  end

  if lookups.any?
    raise "You've declared some Resources as caching without providing a caching store" if JSONAPI.configuration.resource_cache.nil?

    # Step Two execute the cache lookup
    found_resources = CachedResponseFragment.lookup(lookups, context)
  else
    found_resources = {}
  end


  # Step Three collect the results and collect hit/miss stats
  stats = {}
  found_resources.each do |resource_klass, resources|
    resources.each do |id, cached_resource|
      stats[resource_klass] ||= {}

      if cached_resource.nil?
        stats[resource_klass][:misses] ||= 0
        stats[resource_klass][:misses] += 1

        # Collect misses
        missed_resource_ids[resource_klass] ||= []
        missed_resource_ids[resource_klass].push(id)
      else
        stats[resource_klass][:hits] ||= 0
        stats[resource_klass][:hits] += 1

        register_resource(resource_klass, cached_resource)
      end
    end
  end

  report_stats(stats)

  writes = []

  # Step Four find any of the missing resources and join them into the result
  missed_resource_ids.each_pair do |resource_klass, ids|
    find_opts = {context: context, fields: find_options[:fields]}
    found_resources = resource_klass.find_to_populate_by_keys(ids, find_opts)

    found_resources.each do |resource|
      relationship_data = @resource_klasses[resource_klass][resource.id][:relationships]

      if resource_klass.caching?

        serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
        context_json = resource_klass.attribute_caching_context(context).to_json
        context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
        context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"

        writes.push(CachedResponseFragment::Write.new(
          resource_klass,
          resource,
          serializer,
          serializer_config_key,
          context,
          context_key,
          relationship_data
        ))
      end

      register_resource(resource_klass, resource)
    end
  end

  # Step Five conditionally write to the cache
  CachedResponseFragment.write(writes) unless JSONAPI.configuration.resource_cache.nil?

  mark_populated!
  self
end

#register_resource(resource_klass, resource, primary = false) ⇒ Object



131
132
133
134
135
# File 'lib/jsonapi/resource_set.rb', line 131

def register_resource(resource_klass, resource, primary = false)
  @resource_klasses[resource_klass] ||= {}
  @resource_klasses[resource_klass][resource.id] ||= {primary: resource.try(:primary) || primary, relationships: {}}
  @resource_klasses[resource_klass][resource.id][:resource] = resource
end