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.
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 ?
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.
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.
end
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.
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.
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.
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.
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.