timed_fragment_cache on Rails 2.1

With Rails 2.1 cache_erb_fragment has been replaced by write_fragment. This breaks a plugin I really enjoy to use: timed_fragment_cache by Richard Livsey.

It basically allows you to specify an expiry for the fragments you cache, e.g. inside memcache, by adding a "meta" fragment for the fragment you store containing the expiry time.

Some usage examples - pretty self-explaining:

In your view:

<%- cache 'my_key', 1.hour.from_now do -%>
	...
<%- end -%>

With extensive queries - your view ...:

<%- cache 'some_posts' -%>
	<%- @posts.each do |post| -%>
		<%= @post.title %>
	<%- end -%>
<%- end -%>

... and your controller:

when_fragment_expired 'some_posts', 1.hour.from_now do
	@posts = Post.find( :all )
end

Since timed_fragment_cache is so convient to use and essentially because I simply cannot live without it, I've fixed Richards version to work with Rails 2.1, i.e. let it use write_fragment instead of cache_erb_fragment.

Download version 0.2.

Expiring cache

Do you know how could I explicit expire this cache?

There are some situations that I cant wait for it timeout.
Im trying expiring it from another controller using the expire_fragment :key, but it isn't working.

Thanks

This now at least fulfills

This now at least fulfills the "flag" function that the variable had, in order to let the cached version of the feed be shown. The results of this -- the feed, can be seen in the right side-bar of my site that I mentioned above.

Rails 2.3.3 tested & compatible version of timed_fragment_cache

I had to fix up a few things to make sure itw orked with rails 2.3.3

http://wiki.github.com/kshaikh/timed_fragment_cache

Pingback

[...] Use Timed Fragment Caching to simplify caching a page fragement for particular time periods e.g. 1.hour.from.now (rather event-driven expiration e.g. when a new post is added, when a user signs in) - http://www.ruzee.com/blog/2008/07/timed_fragment_cache-on-rails-21 [...]

Issue with Rails timezone support

The above (using 1.hour.from_now.time) didn't work for me when I used rails timezone support. It's fine if you have config.time_zone = 'UTC' in your environment file (the default), but after I changed it to a separate zone, caching was no longer working.

The issue was with what type of object you pass in to the when_fragment_expired function - pass in a Time object (adding .time does this), and everythings great. Not adding the .time causes an ActiveSupport::TimeWithZone object. The gsub error pops up. On my system, adding .time causes the time zone to be dropped.

In other words, my timezone was set to eastern. So when I passed in 1.hour.from_now.time, it was taking an hour from the SYSTEM time, which was utc. This would cause it to always recache - not desired obviously.

The easiest solution I found that seems to be working is to pass in a Time object to that block.
Time.parse(1.hour.from_now.to_s)
This sets the cached fragment to expire in 1 hour using your local time zone.

If you send a patch over with

If you send a patch over with the fix in (or fork and issue a pull request), I'll merge the changes in there so that everyone can benefit.

Thanks.

Good work this is a great plugin. I've

Good work this is a great plugin. I've forked the original in github with your changes (http://github.com/GeorgePalmer/timed_fragment_cache/tree/master)

[...] patches on the

[...] patches on the timed_fragment_cache [...]

Hmm... looks like the template tags got

Hmm... looks like the template tags got swallowed up. Here's the template code that failed:

elsif @aggregate_feed
cache 'aggregate_feed', Time.now + 15.minutes do
What we're blogging about
...etc.

Hi, Thanks for the plugin and the

Hi,

Thanks for the plugin and the notes from everybody. I just installed it into my site, and it's awesome to experience the 100x speedup one can get by caching database hits. (My home page has lots of dynamic content, but it's all cached depending on the type of data: http://greenfabric.com/ )

I have a couple of notes about the package:

1) The package has a stray ".svn" directory in /lib, which I didn't notice at first, and which caused havoc with my svn set-up, until I figured out what was going on. This should probably be deleted.

2) A note for other beginners, like me, maybe: With Rails 2.1, the statements in the templates ALSO need to have that "Time +" syntax described above. For example, I cache an aggregate rss feed with this code in my template:

cache 'aggregate_feed', Time.now + 15.minutes do

...and this code in my controller:

when_fragment_expired 'aggregate_feed', Time.now + 15.minutes do

I saw in the docs that it's possible to leave out the time in the template if it's being used in the controller, but I haven't tested it.

3) Another note for beginners - not about this change to the plugin, per se. I had a problem with the RSS feed because I don't display it on every page, and my template checks for the presence of this @aggregate_feed variable. This template code:

What we're blogging about

...failed, displaying nothing, because the @aggregate_feed was no longer being set by the controller (since the controller saw it was still cached.)

My solution was to change the controller code to this:

@aggregate_feed = true
when_fragment_expired 'aggregate_feed', Time.now + 15.minutes do
@aggregate_feed = CreativeRole.aggregate_feed
end

This now at least fulfills the "flag" function that the variable had, in order to let the cached version of the feed be shown. The results of this -- the feed, can be seen in the right side-bar of my site that I mentioned above.

[...]

Sorry, but the above is not a good

Sorry, but the above is not a good workround since the conversion from TimeZone to Time causes trouble when it comes to calculate if the cache is expired. The best solution I can think of is to use:

when_fragment_expired ‘tags_home_page’, Time.now + 10.minutes do

That way it is just the Time class being used as in Rails 2.0.

A workround seems to be that instead of

A workround seems to be that instead of in your controller having:

when_fragment_expired 'tags_home_page', 10.minutes.from_now do

we put:

when_fragment_expired 'tags_home_page', 10.minutes.from_now.time do

So instead of passing a TimeZone object, we pass Time as normal. Not elegant I know...

Excellent, that's a great fix. The

Excellent, that's a great fix. The plugin is brilliant unfortunately there is one other issue with Rails 2.1. It occurs if config.time_zone is set in the environment.rb and results in an error:

private method gsub called for StringIO
time_with_zone.rb:118:in to_yaml

If I find a fix for this, I'll re-post. Cheers.

Time zone fix

If config.time_zone is set, then the time objects you are passing to cache are actually ActiveSupport::TimeWithZone objects. If you call localtime on them before passing them in, that fixes the problem.

Or the following line could be added to the first line of the cache_with_expiry method in the plugin:

expires = expires.localtime if expires.kind_of?(ActiveSupport::TimeWithZone)

Easier way

Hi,

I tried the above fix, but it still did not get around the gsub error message for Rails 2.2.

A better way to fix this is to make sure you are passing in a Time object.

So instead of doing this:


<%- cache 'my_key', 1.hour.from_now do -%>

You do this:

<%- cache 'my_key', 1.hour.from_now.time do -%>

Hamza

This is great! Has this been submitted

This is great! Has this been submitted to Richard for inclusion into the plugin?

Glad you find it useful! The plugins

Glad you find it useful!

The plugins are all on github now (http://github.com/rlivsey) which should help them stay updated a bit easier.

If you send a patch over with the fix in (or fork and issue a pull request), I'll merge the changes in there so that everyone can benefit.

Thanks.