cache everything but...

I saw this older post when searching for information:

withfragmentcachingonecancacheparts of a page. However, more often than not what I would need is the exact opposite approach. I would like to be able to use action cashing and have a mechanism for telling Rails to excludeoneor more areas from the cashing and fill them with dynamic content instead. A good example would be a page whereallcontent would be perfectly cachablebutonetiny line saying 'logged in as [username]'.

Wouldn't a mechanism for doing this more elegantly be a great addition to Rails? Or am I missing something and this is in fact currently possible?

I have exactly the same need - a front page that has a bit of text that changes depending on whether the user is logged in or not. Everything else could be cached using page cacheing.

Hrm.... I'll take a look at the code... maybe something like 'cache_except' is possible?

Thanks, Dave

davidnwelton@gmail.com wrote:

I saw this older post when searching for information:

withfragmentcachingonecancacheparts of a page. However, more often than not what I would need is the exact opposite approach. I would like to be able to use action cashing and have a mechanism for telling Rails to excludeoneor more areas from the cashing and fill them with dynamic content instead. A good example would be a page whereallcontent would be perfectly cachablebutonetiny line saying 'logged in as [username]'.

Wouldn't a mechanism for doing this more elegantly be a great addition to Rails? Or am I missing something and this is in fact currently possible?      I have exactly the same need - a front page that has a bit of text that changes depending on whether the user is logged in or not. Everything else could be cached using page cacheing.

Hrm.... I'll take a look at the code... maybe something like 'cache_except' is possible?

Thanks, Dave -- http://www.dedasys.com/davidw/   

Hey

I had the same issue and settled for two cashed versions of my page, one for logged in users and one for visitors. I put the code changes into a plugin you might want to have a look at.

http://rails.co.za/articles/2007/01/10/cachefilter-update and then some bug fixes later... http://rails.co.za/articles/2007/02/07/cachefilter-rails-1-2-and-edge-compatible

HTH

Gustav Paul

> I have exactly the same need - a front page that has a bit of text > that changes depending on whether the user is logged in or not. > Everything else could be cached using page cacheing.

I had the same issue and settled for two cashed versions of my page, one for logged in users and one for visitors. I put the code changes into a plugin you might want to have a look at.

http://rails.co.za/articles/2007/01/10/cachefilter-update and then some bug fixes later...http://rails.co.za/articles/2007/02/07/cachefilter-rails-1-2-and-edge

Looks like a good way of doing it.

I was thinking about something like this:

1) Create a no_cache method as a helper. The trick is that it outputs something like <%= blah blah %> 2) Cache the page. 3) Render the cached version, which does the substitution on what no_cache slipped into the cache.

I don't know whether how efficient that is, since the whole page still has to be parsed up for erb, when there is only one little chunk of it. I also don't know if there are any lurking problems...

Thanks, Dave

I was thinking about something like this:

1) Create a no_cache method as a helper. The trick is that it outputs something like <%= blah blah %> 2) Cache the page. 3) Render the cached version, which does the substitution on what no_cache slipped into the cache.

Ok, some code that seems to work:

class NoCacheFilter

  include ActionController::Caching::Actions

  def initialize(*actions, &block)     @actions = actions   end

  def before(controller)     return unless @actions.include?(controller.action_name.intern)     controller.instance_variable_set '@dont_interpolate_this', true     action_cache_path = ActionCachePath.new(controller)     if cache = controller.read_fragment(action_cache_path.path)       controller.rendered_action_cache = true       set_content_type!(action_cache_path)       controller.send(:render, :inline => cache)       false     end

  end

  def after(controller)     return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache     controller.write_fragment(ActionCachePath.path_for(controller), controller.response.body)     controller.send(:render, :inline => controller.response.body)   end

  private   def set_content_type!(action_cache_path)     if extention = action_cache_path.extension       content_type = Mime::EXTENSION_LOOKUP[extention]       action_cache_path.controller.response.content_type = content_type.to_s     end   end

end

Combined with this helper:

  def no_cache(text)     return eval(text) unless @dont_interpolate_this     return "<%= #{text} %>"   end

Things that could perhaps be improved:

1) I don't like setting the variable in the controller. 2) I don't really like the fact that no_cache takes text as an argument rather than somehow accomplishing the same thing with a block, but there's no way to get a block's text out, so a string it is, as far as I can tell.

  def after(controller)     return if !...@actions.include?(controller.action_name.intern) || controller.rendered_action_cache     controller.write_fragment(ActionCachePath.path_for(controller), controller.response.body)     controller.send(:render, :inline => controller.response.body)   end

Ugh, that isn't quite right, either, because apparently you can't do any sort of render operation in the after filter, not even render_to_string. This is pretty hacky feeling, but if that's what it takes...

  def after(controller)     return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache     controller.write_fragment(ActionCachePath.path_for(controller), controller.response.body)     controller.instance_variable_set '@performed_render', false     controller.response.body = controller.send(:render_to_string, :inline => controller.response.body)   end

Cleaner ideas welcome!