On one of our projects we needed to do some caching for an action with an expensive db query. Fragment caching took care of the rendering but we needed a way to skip the db if we have a cache hit. And checking for an existence of the fragment file in the controller just didn’t seem right.
Lazy evaluation to the rescue.
controller
@items = Item.lazy.paginate
view
<% cache ("fragment...") do %>
<%= render :collection => @items %>
<% end %>
@items
is a placeholder for the result of calling paginate which will
not be actually executed until @items
is used in any way (calling any method on
it, like each
, or to_s
). And if the fragment is cached then @items is not
accessed and so no db query is made.
We re-implemented the lazy evaluation recently for our new project with a much cleaner syntax and simpler implementation. We packaged it as lazyeval gem.
Sources can be found at github. The interesting part is in lazyeval.rb which is just 28 lines long. Check it out …
The basic idea is to return a placeholder object that ‘remembers’ what needs to be done once it is used.
2 options:
foo.lazy.bar(params)
- this will return a placeholder that will call methodbar
on the objectfoo
withparams
once used.foo.lazy {|o| o.bar }
- this will return a placeholder that will call the block passing the objectfoo
as a parameter.