So maybe this has been done before, but this is something I wrote that I found useful. Many of the ''fragments'' I wanted to cache differed only by some small value like the :id of the :url, but because this changed every request, the options were to either store a fragment for every :id, or to not cache. So I wrote a helper I call ''dynamic_cache''. It works by using escaped Erb INSIDE of an ERb fragment, and calling render :inline on the cached fragment before returning. So for instance: <% cache do %> <%= link_to_remote ''My Link'', :url => { :id => @my_id } %> <% end %> This doesn''t cache, since the :id is variable. So with dynamic_cache it would look like the following: <% dynamic_cache do %> <%= link_to_remote ''My Link'', :url => { :id => ''\<\%= @my_id \%\>'' } %> <% end %> @my_id doesn''t get evaluated and the escaped ERb delimiters get cleaned up to just ''<%= @my_id %>''. The cache then calls render :inline on the cached fragment which evaluates any of the newly un-escaped ERb with the current values at the time of the render. Add the following to your application.rb for this to work.... module ActionController #:nodoc: module Caching module Fragments def dynamic_cache_erb_fragment(block, name = {}, options = nil) # this grabs the _erbout variable from the blocks scope buffer = eval(''_erbout'', block.binding) if ! cache = read_fragment(name, options) # by default the block.call will write everything to the _erbout # this is an ugly way to get around that, and is the same method used in capture_erb_with_buffer # but it is only called the first time, so not a big deal pos = buffer.length block.call cache = buffer[pos..-1] buffer[pos..-1] = '''' # save it to the cache if we are caching write_fragment(name, cache, options) if perform_caching end buffer.concat(render :inline => cache) end end end end module ActionView module Helpers module CacheHelper def dynamic_cache(name = {}, &block) @controller.dynamic_cache_erb_fragment(block, name) end end end end -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
You can also do some more complicated caching than would otherwise be allowed (ie, caching larger blocks of code...it gets a little ugly, but may be useful. In the following example, condtional is executed at the time of rendering, while the links are cached. <% dynamic_cache do %> <%= ''\<\% if @my_test_value == "HURRAY" \%\>'' %> <%= link_to "My Test2", "HURRAY" %> <%= ''\<\% else \%\>'' %> <%= link_to "My Test3", "HIDEYHO" %> <%= ''\<\% end \%\>'' %> <% end %> PLEASE NOTE: THERE IS A BUG HERE CURRENTLY I NEED TO FIX. If you use more than one dynamic_cache request you get the a double render error. I need to figure out how to get the effect of render :inline with actually using render. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
OK, here is a fixed version (just added ''@template.'' to the render : module ActionController #:nodoc: module Caching module Fragments def dynamic_cache_erb_fragment(block, name = {}, options = nil) # this grabs the _erbout variable from the blocks scope buffer = eval(''_erbout'', block.binding) if ! cache = read_fragment(name, options) # by default the block.call will write everything to the _erbout # this is an ugly way to get around that, and is the same method used in capture_erb_with_buffer # but it is only called the first time, so not a big deal pos = buffer.length block.call cache = buffer[pos..-1] buffer[pos..-1] = '''' # save it to the cache if we are caching write_fragment(name, cache, options) if perform_caching end logger.error cache buffer.concat(@template.render(:inline => cache)) end end end end module ActionView module Helpers module CacheHelper def dynamic_cache(name = {}, &block) @controller.dynamic_cache_erb_fragment(block, name) end end end end Here is a fixed example of conditionals. Notice the use of double quotes to wrap the escaped ERb. <% dynamic_cache do %> <%= "\<\% if @my_test_value == \''BOOOO\'' \%\>" %> <%= link_to "My Test", "/\<\%= @my_test_value \%\>" %> <%= "\<\% else \%\>" %> <%= link_to "My Test", "/\<\%= @my_test_value \%\>2" %> <%= "\<\% end \%\>" %> <% end %> Obviously, another option would be to use a separate delimiter and replace it with ''<%'' and ''%>'' in the dynamic_cache method when it initially creates the cache record. Like, <% dynamic_cache do %> <%= "#% if @my_test_value == \''BOOOO\'' %#" %> <%= link_to "My Test", "/#%= @my_test_value %#" %> <%= "#% else \%\>" %#> <%= link_to "My Test", "/#%= @my_test_value %#>2" %> <%= "#% end %#>" %> <% end %> You would just need to cache.gsub for ''%#'' and ''#%'' before write_fragment. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Sorry, left a logging statement in there, which shouldnt be: module ActionController #:nodoc: module Caching module Fragments def dynamic_cache_erb_fragment(block, name = {}, options = nil) # this grabs the _erbout variable from the blocks scope buffer = eval(''_erbout'', block.binding) if ! cache = read_fragment(name, options) # by default the block.call will write everything to the _erbout # this is an ugly way to get around that, and is the same method used in capture_erb_with_buffer # but it is only called the first time, so not a big deal pos = buffer.length block.call cache = buffer[pos..-1] buffer[pos..-1] = '''' # save it to the cache if we are caching write_fragment(name, cache, options) if perform_caching end buffer.concat(@template.render(:inline => cache)) end end end end module ActionView module Helpers module CacheHelper def dynamic_cache(name = {}, &block) @controller.dynamic_cache_erb_fragment(block, name) end end end end -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---