with_scope issue

I have the following code:

class User < ActiveRecord::Base   has_many :requests do   def find_active(options = {})       with_scope :find => { :conditions => ["requests.active = ?", true] } do         find(:all, options)       end   end   end end

Executing user.requests.find_active results in the following SQL: SELECT * FROM requests WHERE (( requests.user_id = 10 ) AND ( requests.active = 1 )) AND (requests.user_id = 10) ORDER BY requests.name

Note the extra (requests.user_id = 10).

Is this as expected? I can use with_exclusive_scope and that makes it go away, but the doc makes me wonder if I may not get what I expect since has_many is probably doing some kind of with_scope under the covers.

I'm using Rails 1.2.5 and Ruby 1.8.6.

I have the following code:

class User < ActiveRecord::Base   has_many :requests do   def find_active(options = {})       with_scope :find => { :conditions => ["requests.active = ?", true] } do         find(:all, options)       end   end   end end

Executing user.requests.find_active results in the following SQL: SELECT * FROM requests WHERE (( requests.user_id = 10 ) AND ( requests.active = 1 )) AND (requests.user_id = 10) ORDER BY requests.name

Would the find_active method not be better defined on the Request
class ?

Fred

I don't think so because I'm looking for active requests of the user. Sure, I could have a Request.find_active(user) method, but I decided to make use of my has_many association. While it does bring a little Request knowledge into User, I think user.requests.find_active is cleaner and makes more sense than Request.find_active(user). To me, that's one of the beauties of has_many, has_one, etc.

Besides, that is not the original question. :slight_smile:

I don't think so because I'm looking for active requests of the user. Sure, I could have a Request.find_active(user) method, but I decided to make use of my has_many association. While it does bring a little Request knowledge into User, I think user.requests.find_active is cleaner and makes more sense than Request.find_active(user). To me, that's one of the beauties of has_many, has_one, etc.

you can make use of the has_many, since this sort of stuff works:

class Request < ActiveRecord::Base    def self.find_active      find :all, :condition => ["active = ?", true]    end end

And then user.requests.find_active finds active requests for that user.

Fred

Umm...looks like a bug to my eyes..does it happen in latest edge rails as well ?

Haven't tried edge rails yet, but I found another one that scares me. I have a class that uses an after_create callback to build up several associations. I'm finding that a simple Request.find call is inheriting scope depending on how I create the object. It's hard to explain without see all the code, but here goes.

User has_many Requests. Requests will build up associations amongst themselves by after_create calling search(). The search method in Request is basically like:

def search   Request.find(...) end

If I create a Request like this:

user.requests.create!()

Then calls in the Request.find() calls in Request get a (user_id = n) added to their where clause. That should not happen.

If I call Request.search() directly or do user.requests << Request.new(), then the (user_id = n) is not added to the where clause.

Sure feels like a nasty, subtle bug to me.

Is this the best place to let it be known?

I'm going back to 1.2.4.

If I create a Request like this:

user.requests.create!()

Then calls in the Request.find() calls in Request get a (user_id = n) added to their where clause. That should not happen.

This scoping is intentional although the consequences can be subtle.
It's what makes user.requests.find_active work when you've only
defined Request.find_active

If I call Request.search() directly or do user.requests << Request.new(), then the (user_id = n) is not added to the where clause.

Sure feels like a nasty, subtle bug to me.

Is this the best place to let it be known?

dev.rubyonrails.org, the rails-core mailing lists or the rails- contrib irc channgels are more suitable for discussing the
development of rails itself.

Fred

> Then calls in the Request.find() calls in Request get a (user_id = n) > added to their where clause. That should not happen.

This scoping is intentional although the consequences can be subtle. It's what makes user.requests.find_active work when you've only defined Request.find_active

This is intentional? That surprises me because of all the subtle side- effects. I mean how would I know what to do in Request.after_create because the behavior is different based on how I was created (user.requests.create() vs. user.requests << Request.new())? I'm of the opinion that code (like a state machine) should not be dependent on where you're called from. It should only be dependent on the arguments passed. I guess I need to go through all my code and make sure I don't have any other issues like this.

There still is the issue of doubling up of (user_id = 10) when using with_scope.

I played with this some more, and I get the power of this for finders, but create can cause some troubles. Knowing this, I moved all my association extensions from User to Request, and that got rid of the double (user_id = 10) and (user_id = 10). Although, I still think the previous is a minor issue with with_scope.

I fixed my Request.find issue by adding with_exclusive_scope anywhere where I really did want to search the entire requests table and not inherit any scope.

Yes, it does work, but in your example user.requests.find_active will not cache. That was the main reason for me putting it in User. I ended up with the best of both worlds using the scope_out plugin. Now I have:

class Request   belongs_to :user   scope_out :active, :conditions => ["requests.active = ?", true] end class User   has_many :requests, :extend => Request::AssociationMethods end

Now, find_active, with_active, and calculate_active are available from Request and user.requests, I get the caching I want, and it's a whole lot less code.