Victor Pudeyev
Victor Pudeyev

Reputation: 4538

Share cached fragment across actions and controllers in rails

I'd like the same cached fragment to be used by several actions. I have this in three views:

  - cache( 'categories_tree' ) do
    .categories-tree
      - if 'helper' == categories_tree_type
        = raw categories_tree @categories
      - elsif 'partial' == categories_tree_type
        = render 'categories/tree', :categories => @categories  

but it results in three caches:

piousbox@piousbox-samsung:~/projects/bjjcollective$ tree tmp
tmp
├── cache
│   ├── 2D6
│   │   └── 121
│   │       └── views%2Fcategories_tree%2F3e567c5c7f95eb7a8ccee13e8ba37676
│   ├── 35A
│   │   └── 2F1
│   │       └── views%2Fcategories_tree%2F594ceedfd2b2c5decf4ee905f40a97c5
│   └── 377
│       └── 2F1
│           └── views%2Fcategories_tree%2Fe6d3b9cc3ceea0b8ae33ebffe3006e90
├── pids
│   └── server.pid
├── sessions
└── sockets

How would I make three views share the same cached fragment? I moved them all to the same controller but it didn't help.

Upvotes: 1

Views: 171

Answers (1)

fivedigit
fivedigit

Reputation: 18682

If you URL decode the cache keys Rails uses, you can see which parts they're composed of:

views/categories_tree/3e567c5c7f95eb7a8ccee13e8ba37676
views/categories_tree/594ceedfd2b2c5decf4ee905f40a97c5
views/categories_tree/e6d3b9cc3ceea0b8ae33ebffe3006e90

It includes the categories_tree key you specified.

But Rails by default also includes an MD5 hash of the view file and its dependencies (other views rendered from the view). It does this so that if you change the view or one of its dependent views, the cache key changes and caches are regenerated to make sure the changes in the view are shown.

You can solve this problem in two ways, simply pick your favourite. Note that below I have assumed that @categories will always have the same contents, as suggested by your question. If this is not the case you'd need to include it in your cache key to make sure the correct tree is shown.

Skipping the cache digest

You can tell Rails to not generate that MD5 digest. Then the cache key will be the same for all three partials and there will be only one fragment in the cache.

Simply do this:

- cache('categories_tree', skip_digest: true) do

I would discourage this approach, as it will remove the benefit of caches being automatically busted on a change in the views. If you use this approach, you have to be sure to change the cache key every time you make a change.

Extracting the repeated code into a single partial

You've repeated the code several times in different views, so it would make sense for it to be extracted into its own partial:

# app/views/application/categories_tree
- cache('categories_tree') do
  .categories-tree
    - if 'helper' == categories_tree_type
      = raw categories_tree categories
    - elsif 'partial' == categories_tree_type
      = render 'categories/tree', :categories => categories

Then, render the partial in the view templates that use it:

= render('categories_tree', categories: @categories)

Its cache digest will now be the same and the same fragment will be reused by the cache. You'll still benefit from the automatic cache busting this way.

You can read more on caching in the Rails documentation.

Upvotes: 1

Related Questions