views:

180

answers:

1

For somewhat complicated reasons, I would like to create something that works like this:

# Controller:
@comments = @page.comments # comments are threaded
# child comments still belong to @page
...  
# View:

@comments.each_root {
  display @comment {
    indent & recurse on @comment.children 
} }

# alternatives for how recursion call might work:     

# first searches @comments, then actually goes to SQL
comment.in_memory.children

# only looks at @comments, RecordNotFound if not there
# better if we know @comments is complete, and checking for nonexistent
#  records would be wasteful
comment.in_memory(:only).children

# the real thing - goes all the way to DB even though target is already in RAM
# ... but there's no way for find() to realize that :(
comment.children

I'm not even sure yet if this is possible, let alone a good idea, but I'm curious, and it'd be helpful.

Basically I want to redirect find() so that it looks first/only at the collection that's already been loaded, using something like a hypothetical @collection.find{|item| item.matches_finder_sql(...)}.

The point is to prevent unnecessarily complex caching and expensive database lookups for stuff that's already been loaded en masse.

If possible, it'd be nice if this played nice with extant mechanisms for staleness, association lazy loading, etc.

The nested-comments thing is just an example; of course this applies to lots of other situations too.

So... how could I do this?

+1  A: 

You should not write something that's already in Rails itself! You can easily leverage Rails' caching methods to put your query results in Memcached (or what ever caching framework you configured):

class ArticleController < ApplicationController 
  def index
    @articles = Rails.cache(:articles_with_comments, :expires_in => 30.minutes) do
      Article.find(:all, :include => :comments)
    end
  end
end

BTW. the :expires_in is optional. You can leave the cache 'forever' or expire it manually.

Second example, as by my comment:

class ArticleController < ApplicationController 
  def index
    @page = Page.find(params[:page_id]

    @articles = Rails.cache([:articles_with_comments, @page.id], :expires_in => 30.minutes) do
      Article.find(:all, :include => :comments, :conditions => { :page_id => @page.id})
    end
  end
end

This will cache the articles and comments for a given @page object.

Ariejan
As an added bonus, you can use an array for the cache-key. This key may include 'dynamic' variables. I added a second example.
Ariejan
This doesn't do what I want, because it doesn't allow me to continue to use standard find and find-using methods (like .children from awesome_nested_set). The problem is not a caching issue (like what you address) but more a proxying issue.
Sai Emrys