Skip to main content

Subscribe to GoRails to get access to this episode and all other pro episodes, and new awesome content every month.

Subscribe Now
Only $19/month

Unlimited access. Cancel anytime.

14 Improve Performance With Caching:

Advanced Caching with User Permissions and Authorization

Episode 115 · April 26, 2016

Learn how to use Javascript to enable user permissions and authorization in generic caches

Authorization Performance


Transcripts

Subscribe or login to view the transcript for this episode.

Discussion


Fallback

A great episode again Chris! I wish you had released this one a week earlier though :). It would have made my life way easier. Could you tell me if there is any advantage of using meta tag like
<meta content="&lt;%= current_user.id % &gt;">
over just adding the data attr to the body like
<body data-currentuserid="&lt;%= current_user.id %&gt;"> ?

Fallback

No real advantage other than meta tags are a bit nicer to organize your HTML with. You can absolutely put that on your body tag just the same.


Fallback

Hey Chris. Very interesting episode and solution.
I am trying to implement something like this (because you gave me an idea), but would like to extend it much further.

I am going with Gon gem, which is basically the same you are doing, but with different approach.
What I would like to do actually is a wee bit harder than what is shown here, because a proper authorization is needed, not just role based.

I would like to do this with CanCanCan, but I don't relly know how to do it properly and maintainable.
Currently I have a user's ability permissions hash, but how to actually do it in JS - here i have problems.

Any ideas? Do you think this is too much and doesn't really weight out caching?
Caching is great to make a site much faster, but with the cost of simplicity. Everything is ballooning in complexity.

Any thoughts?

Fallback

It definitely does balloon in complexity which is why this is a tradeoff that often isn't valuable until your servers are falling down because you have too much traffic. It's a serious investment coding wise and requires a lot of engineering time to improve performance.

I'm not familiar with the Gon gem, but I would say that if you've got a resource like a BlogPost, you could take your CanCanCan or Pundit scopes and then just simply ask for all the things your view needs (show, edit, update, destroy, whatever you need) and convert that to a JSON object that you can pass to your JS. This way you've got a simple JSON object with a bunch of booleans that can be easily evaluated client side without having to do much logic duplicated on both the client and server.


Fallback

I had to do something similar. One tip/hack to handle the delay you mention around ~13:44 is to write inline-javascript immediately after the element you're hiding/showing is declared in your HTML. I had to do this because waiting for jQuery to load and for DOM to be ready caused a flicker above-the-fold. I would not recommend this approach in general as it pauses the browser from parsing HTML to create the DOM, but it's a handy approach if above-the-fold page loads matter more than our "best practices". Another con is that you have to use vanilla javascript to add/remove classes, which will suck if you have to support IE < 8.


Fallback

Hey Chris,
So you can ignore my comment on the other thread because I found this.

One thing I am running into is cached results when I use filters in my params.

For instance, using your List example. Say your root_path is List#Index. But you also could tag your lists. So say you had a tag for 'rails', which basically just re-rendered your List#Index but changed the value of the `@lists` collection to just include the lists related to the tag `rails`.

How do you tackle Russian Doll caching in a scenario like that, where the views are identical, no partials or any data changes, the only thing that changes is the filter param in the URL?

Thanks again for all the wonderful work you do.

Edit: Oh and to make my scenario a bit more complicated, what actually happens is when a filter param is present, the results it shows on the `List#Index` is actually different based on the role of the user requesting. So just adding the `edit` link to the `rails` list, if the user is an admin, but if the user is a regular user that `edit` link on the `rails` list doesn't show up.

Fallback

You could include the filter and role in your cache key and save them as separate caches.

I would be somewhat careful with that because if you have a lot, then you're just filling up the cache storage with stuff that's likely to get blown away and you won't get any benefits from caching. Compiling a list of small cache snippets is super fast, so you might consider just not caching the list, but cache each individual item instead. The compilation of everything will be fast enough and can save you on some cache storage space if you notice bloat saving all the different copies.

Fallback

How would I save them as separate caches? My partial is `cache profile do`, would I change that to `cache [params[:rating], profile do`? Or did you have something else in mind?

I have tried the above version and it doesn't work properly. It still messes up the views when I switch between my `/profiles` and `/profiles?rating=speed`.

Edit: For more details, here is my Stack Overflow question: http://stackoverflow.com/qu...

That guy had the same suggestion you did, but that doesn't work :(

Fallback

Yeah that should work. What cache key is that referencing in your logs?

Fallback

Chris, I have replied twice and both times Disqus has marked it as spam. Should I go ahead and put the response to this question in my SO question?

Edit: I just added the logs it to the SO question, so you can refresh it.

Fallback

Doh, yeah they do that sometimes. At some point soon I'm going to swap out Disqus with my own comment system.

Fallback

Got a chance to check out the SO link?

Fallback

Hmm, I can't quite tell where you put it. Did you put it on your Index? <% cache [params[:rating], @profiles] do %>

Fallback

Sorry, I put it around the local variable when the partial renders each time:

`<% cache [params[:rating], profile] do %>`

Around the collection, the index is just `<% cache @profiles do %>`

Fallback

I believe you're going to need it on the collection too because that's what the filter affects.

Fallback

I just did that, so now I have it on both and no dice. I updated the question with the logs of the profile#index page refresh when it is added to both.

Fallback

Any further ideas Chris? Still no dice :(

Fallback

Nothing really. I tried building a sample app to cache in Rails 5 with what we talked about and everything works as expected there. :\


Fallback

Hi Chris. Would be great to see more of your caching / performance videos as there seems like there isn't enough information out there and its a massive part of a web site. For example, what happens if you have a comments list and you need to find out if the current_user has already commented so don't show the "add comment" button. You won't have the users ID if the comment is on another page, so how would you get the users ID? Would you do an API call to the server via ajax, this seems like it could get sluggish OR maybe not?


Fallback

Hi! chris. Im using Roles and Cancancan all over my app. I was thinking of adding the role record to the cache, has it will create a cache base on the role and not the current_user. What do you think about this approach?

Fallback

<% cache [record_list, current_user.role.id, current_user.role.updated_at ,current_company.id ] do %>
stuff
<%end%>


Login or create an account to join the conversation.