I was told this joke from my brother:
There are three problems in programming -- First is counting. Second is expiring caches.
Me being the idiot that I am asked, what's the third one?
Sigh.
So page caching in Rails is *simple*.
go to Controller, add:
caches_page :show
Clearing the cache was an entirely different proposition, but I'll talk about that later.
So here's the scenario. I have a simple CMS system in place for my project. it allows the user to create, edit the content for their website. It also has special editable content pages that are secured using Devise (an awesome gem btw).
The problem was that the secure pages were being cached as well. Having a html file created in the public directory is a double edged sword. Having it there meant that the Rails stack (which is slow) never gets called, Yay! But it also meant that the secure information was now accessible in the public if anyone knew the path to the file.
The only way to authorize those pages was to have it run through the Rails stack. So I needed to
not cache those pages.
On Apidock I found an interesting little snippet using a Proc to prevent caching based on the response type. Not quite what I needed, but it was on the right track.
1: # cache the index action
2: caches_page :index
3: # cache the index action except for JSON requests
4: caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
5: # don't gzip images
6: caches_page :image, :gzip => false
My standard learning/exploration tool is the debugger tool. I toss a debugger in the code and see what is available in that specific scope. Inspecting c was unmanageable as page after page of code screamed past. What I needed was a way to walk through it in smaller steps. Google to the rescue! I found that I could list the params of the object [ c.params]. Ah hah! Now, I could determine which page was being shown based on the id and from that it was a relatively simple matter of creating an array to check against.
caches_page :show, :unless => Proc.new { |c|
secure_pages = Page.find_by_path("secure").pages.map { |page| page.id }
secure_pages.include? c.params["id"]
}
To my eyes, it looks simple and clean. But as a novice, I'm still always worried that I'm doing things the hard way, or worse, the wrong way.
---------------------------
So, I ran into a little glitch.
Having it find_by_path returns a nil when there are no secure pages but a .where('path = ?', 'secure') return an []
The former blows up when I don't have a secure page, the latter gracefully just doesn't find anything.
Gotta love those subtle differences.