#169 Dynamic Page Caching (revised)
Page caching is great for speeding up the performance of a page, but what if it contains user-specific content? Learn how to load content in dynamically through JavaScript in this episode.
- Download:
- source code
- mp4
- m4v
- webm
- ogv
Nice episode, but I have a doubt. If I have the Edit|Destroy links available at all times, but hidden using CSS, doesn't it mean that if I disable CSS from my browser I'll be able to "at as an admin"? (I mean, see the full page, including those links and all the other hidden "actions").
You would be able to see them, yes. That's why it's best to secure your controller actions as well as your views to ensure that, even if someone disabled styling or if they typed in the path to those actions explicitly, they would be unable to access that functionality.
I prefer not hiding things with CSS, myself; using JavaScript instead to add those dynamic elements to the DOM after the cached page has loaded.
Hi Federico, I am interpreting your concern in two different ways. One, in plain words, is that you are concerned people can do admin stuff simply by turning off CSS. Correct me if I am ignorant that you actually already know that's not possible IF you do something like before_filter authorize_user! with Devise in your controllers. - Which brings me to a second possible interpretation of your concern: you simply are talking about that there is one more scenario where people might SEE the Edit/Destroy links, a purely interface concern, not one of security. I mean, MOST people don't disable their CSS, so the small bunch that happen to, you can NOT render the Edit links and send a javascript request to check for Admin, then return a evaluated template to each place needing these links and grab the proper ID as you loop along doing template inserting. That way, it's definitely not a problem if they do $('*:hidden').toggle(). But then again, if you don't have CSS turned on, you might also have Javascript turned off as well. And in that case, I guess you might have to resort to some trickier routing conditions and serve two different html files?
Thanks Mike ang Nik for your responses...
Nik's first interpretation was what I was reffering to, but I didn't consider the security of the controller (that I actually do in my projects). Feeling such a fool now! haha
Thanks
I'm really confused with the idea to send partial HTML each time trough AJAX. The partial itself may become heavy to load, and every time the page loads, header may appear with the unpleasant delay.
Yeah, I was thinking about the delay in loading different elements. So I am guessing the benefit of caching outweighs the milisecond-ugliness for mostly the admin, like the edit/destroy buttons, headers or anything else, I suppose admins can tolerate that. But if it does happen on the client facing elements, I guess a one can do a complete $('#container').show() preceded by #container{display:none} css with an "Loading" HTML message. That way, it looks a bit more professional and certainly is going to be lighter for the server still (just serving HTML files like the 90's)
And all unobtrusive javascript things doesn't work anymore in the partial (jquery-ui autocomplete, $(".dynamic_header a").click(function()...)
I have no heavy traffic so I'm back to fragments caching instead of full page
I can't imagine this to be a feasible solutions for more complex applications or pages.
It's difficult to maintain. Just imagine you are using the same method on tens or hundreds of pages in your application. The javascript will get convoluted. You'd probably use different JS files for each page but that would force you to put all your user-specific code into partials, meaning you'd have hundreds of partial files lying around. It will be a mess.
Performance. Sure, as long as you are on localhost everything will be blazingly fast. However, once you are on a remote server and the pages are a bit more complex the user will inevitably experience delays before the elements pop up. One should also consider the extra times it takes to render the partials, especially when rendering collections.
I am sure that there exist some scenarios where this approach is appropriate. For example, if you have a very large page with mainly static content. Still, for most cases, I think the drawbacks of this approach by far outweigh the benefits and that action or fragement caching would probably a better solution.
If I am not mistaken Grupon uses a similar technique to this, probably not this exact technique, but a similar concept, show a static cached page, then fill in any user info with ajax calls.
Thanks Ryan, this is perfect for me. My pages are very complex involving multiple models and they only change once per day. I only have a couple user-specific elements to load via JS. Time to get to work implementing this.
Best thing I learnt in this screencast - Rails doesn't scale if you have sleep calls in your controllers. :) lol
That was a good one Ryan. :)
Maybe good to mention that with the getScript function the cache is disabled by default (http://api.jquery.com/jQuery.getScript/)
For non user specific content you may consider to enable this to get even a better performance due to http caching
You'll probably want to add a
rel="nofollow"
to those admin links so Googlebot doesn't try to follow them.You should of course protect your controller actions with a before_filter, but even then your application error log is not going to be pretty if the bot starts crawling those links :)
ryan I was trying to do the same thing with my current proyect, and I have working the dynamic content, but I have problems with the flash messages, I have the same escenario as you, I send a submit action to a users#edit with a :notice => "hello" that redirects to deals#index (which have the notice message).... but when the callback is called the flash message is gone, it's a separate request to deals#current_user (I had trouble with using my users_controller given of decent_exposure gem)...anyway, I can do dynamic content, but the flash messages are lost and I don't know how to fix that. Please help
I didn't know but you can manage more the flash hash...with a flash method and an after_filter I solved it...here are the flash methods if someone has a similar problem some day http://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html
I just read a 37signals post on their caching technique in Basecamp Next and came here for some informations about partial-caching etc.
I think David explains very well, how you can use a technique like this very smart, to get fast responses: http://37signals.com/svn/posts/3112-how-basecamp-next-got-to-be-so-damn-fast-without-using-much-client-side-ui
I would like to know (links) how to keep user logged in info inside cookies safety.
I know this sounds stupid by why not add a class and forget the js.
At first I had the same thought, but it's not going to work. The css classes are cached too, for all the users, so if the first user happens to be admin, the next users will see the cached admin version of the page. Vice versa, admins might be seeing just the user version of the page.
good job