Skip to content

Components should play nicely with Rails caching mechanisms #234

Open
@ozzyaaron

Description

@ozzyaaron

Feature request

Components should provide a digest to Rails views, or be able to use Rails explicit caching directive comments or be able to provide a cache_key.

Essentially we should be able to invalidate a cache based on whether the Component class or template has changed.

Motivation

I'm just building on some previous reports primarily around where templates live and the filenames of the template/s. My concerns come down to integrating with Rails caching basics.

The issue that we run into is basically summed up by

## In any view/partial
cache[object, another_object] do
  render(MyComponent.new(attribute: value))
end

In this case if the template for MyComponent is changed the cache will not be invalidated. I tried to add MyComponent to the array used by cache and provide a self.cache_key method that used ActionView::Digestor but without having some further mechanism to invalidate the digestor's cache for the template/s when the template changes this didn't work.

Rails allows for explicit directives to be used (https://guides.rubyonrails.org/caching_with_rails.html#explicit-dependencies) but by default this will only look inside app/views. You can add a view path append_view_path("app/components") but the digestor is expecting a partial and so a template filename would have to be prefixed with underscore.

The way we are currently working around this is to have an ApplicationComponent that our components inherit from and where we override ActionView::Component::Base.matching_views_in_source_location so that templates are now found in app/views/component_templates and where the files are prefixed with underscore.

  def self.matching_views_in_source_location
    return [] unless source_location

    *path, file_name = source_location.chomp(File.extname(source_location)).split("/")
    path.pop # remove components
    path.concat(["views", "component_templates"])
    path_to_template = path.push("_#{ file_name }").join("/")

    Dir["#{ path_to_template }.*{#{ActionView::Template.template_handler_extensions.join(',')}}"]
  end

Then in the above example where cache invalidation does not work via template digest changes we can do

## In any view/partial
# Template Dependency: component_templates/my_component %>
cache[object, another_object] do
  render(MyComponent, attribute: value)
end

I think if AV::C is going to be core or integrate with Rails it would need a way to expose template digests nicely, or provide cache_key probably at the class level. If not then as a last resort we would need a way to use explicit directives in templates that use ActionView::Components internally.

I'd also be all 👂 to people who have solved this already or if I've missed something about the features ActionView::Component provides!

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions