Class: ViewModel::ActiveRecord::Cache::CacheWorker

Inherits:
Object
  • Object
show all
Defined in:
lib/view_model/active_record/cache.rb

Defined Under Namespace

Classes: WorklistEntry

Constant Summary collapse

SENTINEL =
Object.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(serialize_context:) ⇒ CacheWorker

Returns a new instance of CacheWorker.



103
104
105
106
107
# File 'lib/view_model/active_record/cache.rb', line 103

def initialize(serialize_context:)
  @worklist            = {}
  @resolved_references = {}
  @serialize_context   = serialize_context
end

Instance Attribute Details

#resolved_referencesObject (readonly)

Returns the value of attribute resolved_references.



101
102
103
# File 'lib/view_model/active_record/cache.rb', line 101

def resolved_references
  @resolved_references
end

#serialize_contextObject (readonly)

Returns the value of attribute serialize_context.



101
102
103
# File 'lib/view_model/active_record/cache.rb', line 101

def serialize_context
  @serialize_context
end

Instance Method Details

#add_refs_to_worklist(cacheable_references) ⇒ Object



213
214
215
216
217
218
# File 'lib/view_model/active_record/cache.rb', line 213

def add_refs_to_worklist(cacheable_references)
  cacheable_references.each do |ref_name, (type, id)|
    next if resolved_references.has_key?(ref_name)
    (@worklist[type] ||= {})[id] = WorklistEntry.new(ref_name, nil)
  end
end

#add_viewmodels_to_worklist(referenced_viewmodels) ⇒ Object



220
221
222
223
224
225
# File 'lib/view_model/active_record/cache.rb', line 220

def add_viewmodels_to_worklist(referenced_viewmodels)
  referenced_viewmodels.each do |ref_name, viewmodel|
    next if resolved_references.has_key?(ref_name)
    (@worklist[viewmodel.class.view_name] ||= {})[viewmodel.id] = WorklistEntry.new(ref_name, viewmodel)
  end
end

#cacheable_reference(viewmodel) ⇒ Object

Store VM references in the cache as viewmodel name + id pairs.



209
210
211
# File 'lib/view_model/active_record/cache.rb', line 209

def cacheable_reference(viewmodel)
  [viewmodel.class.view_name, viewmodel.id]
end

#find_and_preload_viewmodels(viewmodel_class, ids, available_viewmodels: nil) ⇒ Object

Resolves viewmodels for the provided ids from the database or available_viewmodels and shallowly preloads them.



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/view_model/active_record/cache.rb', line 181

def find_and_preload_viewmodels(viewmodel_class, ids, available_viewmodels: nil)
  viewmodels = []

  if available_viewmodels.present?
    ids = ids.reject do |id|
      if (vm = available_viewmodels[id])
        viewmodels << vm
      end
    end
  end

  if ids.present?
    found = viewmodel_class.find(ids,
                                 eager_include: false,
                                 lock: "FOR SHARE",
                                 serialize_context: serialize_context)
    viewmodels.concat(found)
  end

  ViewModel.preload_for_serialization(viewmodels,
                                      include_referenced: false,
                                      lock: "FOR SHARE",
                                      serialize_context: serialize_context)

  viewmodels
end

#load_from_cache(viewmodel_cache, ids) ⇒ Object

Loads the specified entities from the cache and returns a hash of id=>serialized_view. Any references encountered are added to the worklist.



147
148
149
150
151
152
153
154
# File 'lib/view_model/active_record/cache.rb', line 147

def load_from_cache(viewmodel_cache, ids)
  cached_serializations = viewmodel_cache.load(ids, serialize_context: serialize_context)

  cached_serializations.each_with_object({}) do |(id, cached_serialization), result|
    add_refs_to_worklist(cached_serialization[:ref_cache])
    result[id] = cached_serialization[:data]
  end
end

#resolve_references!Object



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
# File 'lib/view_model/active_record/cache.rb', line 109

def resolve_references!
  @serialize_context = serialize_context.for_references

  while @worklist.present?
    type_name, required_entries = @worklist.shift
    viewmodel_class = ViewModel::Registry.for_view_name(type_name)

    required_entries.each do |_id, entry|
      @resolved_references[entry.ref_name] = SENTINEL
    end

    if viewmodel_class < CacheableView
      cached_serializations = load_from_cache(viewmodel_class.viewmodel_cache, required_entries.keys)
      cached_serializations.each do |id, data|
        ref_name = required_entries.delete(id).ref_name
        @resolved_references[ref_name] = data
      end
    end

    # Load remaining entries from database
    available_viewmodels = required_entries.each_with_object({}) do |(id, entry), h|
      h[id] = entry.viewmodel if entry.viewmodel
    end

    viewmodels = find_and_preload_viewmodels(viewmodel_class, required_entries.keys,
                                             available_viewmodels: available_viewmodels)

    loaded_serializations = serialize_and_cache(viewmodels)
    loaded_serializations.each do |id, data|
      ref_name = required_entries[id].ref_name
      @resolved_references[ref_name] = data
    end
  end
end

#serialize_and_cache(viewmodels) ⇒ Object

Serializes the specified preloaded viewmodels and returns a hash of id=>serialized_view. If the viewmodel type is cacheable, it will be added to the cache. Any references encountered during serialization are added to the worklist.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/view_model/active_record/cache.rb', line 160

def serialize_and_cache(viewmodels)
  viewmodels.each_with_object({}) do |viewmodel, result|
    data_serialization = Jbuilder.encode do |json|
      ViewModel.serialize(viewmodel, json, serialize_context: serialize_context)
    end

    referenced_viewmodels = serialize_context.extract_referenced_views!
    add_viewmodels_to_worklist(referenced_viewmodels)

    if viewmodel.class < CacheableView
      cacheable_references = referenced_viewmodels.transform_values { |vm| cacheable_reference(vm) }
      viewmodel.class.viewmodel_cache.store(viewmodel.id, data_serialization, cacheable_references,
                                            serialize_context: serialize_context)
    end

    result[viewmodel.id] = data_serialization
  end
end