Missing rails feature: cache everything but this

It’s pretty common to have a front page that has content that is very cacheable, except for a little section with something like “signup” or “my page”, depending on whether the user is logged in or not. It would be very nice to have a handy solution in rails for this problem, which seems to be a fairly common question.

Here’s my stab at an answer:

cache everything but…

However, I’m not entirely happy with it because of the very hacky approach taken to make the render_as_string work – I have to fiddle with an instance variable in the controller:

controller.instance_variable_set '@performed_render', false 

Not good. In the thread linked to above, Paul Gustav posts a link about a system for having two different cached pages, but that’s not entirely satisfying either, because it gets you from 1 version to 2 versions, but going beyond doesn’t look real appetizing.

It would be a very nice problem to solve once and for all with a clean solution.

Simple item-based collaborative filtering with Rails

I suspect there is room for improvements with this, since it is really simplistic, and I certainly am not an expert in the subject. But it works ok for small numbers of items:

def Pair.from_items(items)
  Pair.transaction do
    res = []
    all = items.reject { |i| i.name == "" }.collect { |i|
            i.name.downcase }.sort
    all.collect { |i| ([i]*all.length).zip(all) }.each { |i| 
            i.each {|j| res << j} }
    res = res.each { |i| i.sort! }.uniq.reject { |p| 
            p[0] == p[1] }
    res.each do |pair|
      p = Pair.find(:first, :conditions => 
                            ['val1 = ? and val2 = ?', 
                             pair[0], pair[1]])
      p ||= Pair.new(:val1 => pair[0], :val2 => pair[1],
                     :num => 0)
      p.num += 1
      p.save!
    end
  end
end

Basically what happens is that for a list of items [A, B, C, D], it calculates and inserts into the database that A and B have been purchased/rated/viewed/whatevered together once. Same for A and C, A and D, B and C, and so on. After accumulating enough of these pairs, you can query the datbase for an item – B for instance – and see what it’s been purchased with the most times, and recommend that to people who have shown an interest in B. When someone purchases a group of items togehter, you call Pair.from_items(the_items) and it takes care of inserting the various combinations.