Eager loading for the readership tracking problem

Martin Sojka wrote:

It seems like I cannot use this:

@posts = Post.find :all,
  :include => [ :readerships, :author ],
  :joins => "LEFT OUTER JOIN readerships ON (readerships.post_id =
posts.id AND readerships.reader_id=#{current_user.id})",
  :order => 'posts.created_at DESC',
  :limit => 50

Perhaps something like this would work:

class Post < ActiveRecord::Base
   cattr_accessor :current_user_id
   has_one :current_user_readership, :class_name => 'Readership',
              :conditions => 'readerships.reader_id = #{current_user_id}'
end

Post.current_user_id = current_user.id
@posts = Post.find :all, :include => [:current_user_readership, :author],
                          :order => 'posts.created_at DESC', :limit => 50

Otherwise you'll have to either use the available AR mods that allow
the writing of custom eager sql, or select the author and readership
info into the Post models.

Martin Sojka wrote:

Perhaps something like this would work:

class Post < ActiveRecord::Base
   cattr_accessor :current_user_id
   has_one :current_user_readership, :class_name => 'Readership',
              :conditions => "readerships.reader_id = #{current_user_id}"
end

Mark, thanks for great idea. I've started to play with this in the console. It works great when I hardcode the current_user_id into the :conditions (like :conditions => "readerships.reader_id = > 9").

But so far I had no luck to use the current_user_id in the condition. It always results in blank space there, eventually resulting in bad SQL - SELECT * FROM readerships WHERE ... AND (readerships.reader_id = ).

In the console, Post.current_user_id = 9 assigns the class variable well. I then can see it when typing:

Post.current_user_id
=> 9
p = Post.find :first
p.current_user_id
=> 9

But calling p.current_user_readership results in the invalid SQL described above.

Somehow, the current_user_id is not passed into the :conditions statement.

Otherwise this solution would be great since I can assign current_user_id to Post before calling the find to eager load just the current user readership. I just need to find the way to pass that variable into the has_one :conditions.

Anybody solved this before? Thanks!

Sorry, it looks like the sql interpolation is not done on the
model class, perhaps it's on the association class itself. If
that's the case you can't use the accessor-setting approach
since has_one does not support association extensions.

However I've posted a before about setting the conditions for
an association dynamically:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/7f7f715d917b9faa

That should work:

class Post < ActiveRecord::Base
   has_one :current_user_readership, :class_name => 'Readership'

   def self.current_user=(user)
      reflect_on_association(:current_user_readership).options[:conditions] =
          "readerships.reader_id = #{user.id}"
   end
end

Post.current_user = current_user
@posts = Post.find :all, :include => [:current_user_readership, :author],
                           :order => 'posts.created_at DESC', :limit => 50