Ask A Question

Notifications

You’re not receiving notifications from this thread.

Advanced Caching with User Permissions and Authorization Discussion

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;"> ?

Reply

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.

Reply

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?

Reply

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.

Reply

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.

Reply

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.

Reply

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.

Reply

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?

Reply

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?

Reply

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

Reply

Curious as to why you couldn't keep the logic in the ERB and cache the list scoped to the role, rather than the user?

Your role counts are going to be very limited, whereas your user count is not. With roles, you'll have a few caches, but not a ton, and you benefit from keeping that logic on the server.

Am I missing something with this approach?

Reply

Hi Chris,
This is the first video on your site, so I don't know if you post another updated video for this topic. Still, for everyone here I'd say, please take in consideration that must implement an authorization of some sorts (e.g. Gem Pundit, CanCanCan) your back-end, because CSS can be easy overwritten, and the user can delete the hidden class, and they will have access to whatever you're trying to hide. If you still want to use JS, instead of hiding the HTML tag, once the user requests a page, we can check for the metatag and maybe remove the HTML tag instead of hiding it. That's my two cents.
Thanks

Reply

Hiding vs removing the tag in the HTML doesn't really make any difference. Just have to make sure that only admins are allowed on the server side of the request.

Reply

Just different point of view. Less options hidding on your html, less opportunies for unaunthorize users to do harm on your site, while adding an authorization option on your back-end tight your security even more your site. On the other side, givin less opportunities to an unthorize user to see a form or edit button less calls to the server and ugly response to the user. like I said just my two cents.

Reply

Hey Chris, great lesson! I'm using Cancancan in a project with fairly complex permissioning and looking to implement a system like you describe at the end where we create a json object based on what will be on the page then use JS to show/hide actions. Do you have any thoughts on how to do this without creating more N+1 problems when creating the json object?
Cancancan don't seem to have any way to easily go from their Ability class to something like a json object so I'm thinking I'll have to create something custom. Although this feels like a common enough problem that an extension for Cancancan may either already exist or be valuable to the community for someone (me?) to create.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 85,376+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.