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(source, include_related = nil, options = nil) ⇒ ResourceSet

Returns a new instance of ResourceSet.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/jsonapi/resource_set.rb', line 10

def initialize(source, include_related = nil, options = nil)
  @populated = false
  tree = if source.is_a?(JSONAPI::ResourceTree)
           source
         elsif source.class < JSONAPI::BasicResource
           JSONAPI::PrimaryResourceTree.new(resource: source, include_related: include_related, options: options)
         elsif source.is_a?(Array)
           JSONAPI::PrimaryResourceTree.new(resources: source, include_related: include_related, options: options)
         end

  if tree
    @resource_klasses = flatten_resource_tree(tree)
  end
end

Instance Attribute Details

#populatedObject (readonly)

Returns the value of attribute populated.



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

def populated
  @populated
end

#resource_klassesObject (readonly)

Returns the value of attribute resource_klasses.



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

def resource_klasses
  @resource_klasses
end

Instance Method Details

#mark_populated!Object



146
147
148
# File 'lib/jsonapi/resource_set.rb', line 146

def mark_populated!
  @populated = true
end

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



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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/jsonapi/resource_set.rb', line 25

def populate!(serializer, context, options)
  return if @populated

  # 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|
    missed_resource_ids[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
      @resource_klasses[resource_klass].keys.each do |k|
        if @resource_klasses[resource_klass][k][:resource].nil?
          missed_resource_ids[resource_klass] << k
        else
          register_resource(resource_klass, @resource_klasses[resource_klass][k][:resource])
        end
      end
    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].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|
    next if ids.empty?

    find_opts = {context: context, fields: 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



150
151
152
153
154
# File 'lib/jsonapi/resource_set.rb', line 150

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