I've been using AR::Base.with_scope lately, and it's great. I'm finding
it especially useful with the new around_filters that let you specify a
block and yield to the filter chain.
But, when you pass with_scope a :create hash, there is some strange
behavior: Base.create respects the scoping, but Base.save does not, even
if you are saving a new record.
This feels inconsistent. A before_create filter in a model will fire in
both cases; shouldn't scoping behave the same?
I'd be happy to provide tests and a patch, but wanted to check before I
put those together. Is anybody working on this? Do people feel that this
is the right behavior for with_scope to have?
Thanks,
phil
--
Phillip Kast
(909)630-9562
ph...@unimedia.org
Any chance this will make it into core? It has tests and cleans up a
surprise lurking in with_scope.
Could you give us an example of what you're doing with with_scope that
needs this. The example in the trac ticket is definitely not
something that needs with_scope.
login = Login.new({:name=>"anonymous"}.merge(params[:login]))
login.save
--
Cheers
Koz
I'm using with_scope in an around_filter, similar to the way the
scoped_access plugin does.
The idea is to cleanly ensure that a number of models are always
associated with a user's account.
But, we have some places where stuff needs to be done to a new model
object before it's ready to save.
Something along the lines of:
class ApplicationController < AC::Base
def scope_story_by_account
Story.with_scope(:find => {:conditions => {:account_id =>
current_account.id}, :create => {:account_id => current_account.id}) {
yield }
end
around_filter :scope_by_account
end
class StoryController < ApplicationController
def create
@story = Story.new(params[:story])
# do some stuff to @story here...
@story.save #@story.account_id won't be set, without the patch
we're discussing.
end
end
...But with a bunch of models, controllers, and actions thrown into the mix.
Philosophically, it also seems to me that with_scope and ActiveRecord
callbacks should have the same behavior: if new_record is true, the
create callbacks/scoping should happen, regardless of whether you called
create or save.
phil
Michael Koziarski wrote:
>> http://dev.rubyonrails.org/ticket/3734
>>
> Could you give us an example of what you're doing with with_scope that
> needs this. The example in the trac ticket is definitely not
> something that needs with_scope.
>
> login = Login.new({:name=>"anonymous"}.merge(params[:login]))
> login.save
--
Phillip Kast
(909)630-9562
ph...@unimedia.org
The 'right' way to do this would be
class StoryController < ApplicationController
def create
@story = current_account.stories.build params[:story]
# ...
@story.save # account_id set as required.
end
end
with_scope was originally just an implementation method for the
association proxies, and any time you find yourself using it, you're
probably missing out on a much much simpler option.
As it stands, I think I'd prefer the philosophical inconsistencies, if
it points people at the right way to do it.
--
Cheers
Koz
This is the same example I see for with_scope over and over again,
which troubles me greatly. I've only ever seen one example of
with_scope that seemed reasonable. Mike Clark had something in his
model class to merge multiple conditions into one. Even though that
seemed sorta legit, I really don't like features that point people in
the wrong direction 95% of the time. Having with_scope publicly
available seems to be exactly that.
I'm seriously considering that we should deprecate with_scope as a
public method and make it private by 2.0.
Deprecation is a step too far, there are legitimate uses of
with_scope, particularly around encapsulated finds for permissions
models. I just think that there needs to be some serious warnings in
the documentation:
"You almost certainly can find a nicer way to do this"
The documentation should be updated to reflected it's intended use as well.
That being said, encapsulated finds with permissions models don't
need create or new or any of that stuff. As it stands, this is a
wontfix for me.
--
Cheers
Koz
That been said with_scope has its limitations.
Diego
On Oct 15, 7:29 pm, "Michael Koziarski" <mich...@koziarski.com> wrote:
> > This is the same example I see for with_scope over and over again,
> > which troubles me greatly. I've only ever seen one example of
> > with_scope that seemed reasonable. Mike Clark had something in his
> > model class to merge multiple conditions into one. Even though that
> > seemed sorta legit, I really don't like features that point people in
> > the wrong direction 95% of the time. Having with_scope publicly
> > available seems to be exactly that.
>
> > I'm seriously considering that we should deprecate with_scope as a
> > public method and make it private by 2.0.Deprecation is a step too far, there are legitimate uses of
So what I want is to be able to say something in a controller like:
scoped_by_current_account :story
with the result that all find calls on a Story model from that
controller will only return stories that belong to the current account,
and any new stories will always be associated with the current account.
I've done this. It's easy to whip up using with_scope and around
filters, and it seems like a clean and useful abstraction to me. Among
other things, it reduces the risk that a hole in test coverage and/or a
missing find condition could result in, say, returning stories for the
wrong account. You can nest scoping and do this for several models at a
time too, no problem.
This works great for finds. The trouble is that you can't do the same
thing for creates, because the association won't be set if you call save
instead of create, which is too fragile. But, the abstraction makes the
most sense if it works in both directions.
It seems like the legit uses of with_scope that folks have mentioned all
involve passing :find, and the bad uses I've given all use :create.
Documentation would help, but the bigger issue is consistency. I think
either with_scope/:create should treat new records the same way the rest
of ActiveRecord does, or it should go away. Perhaps the way to make it
go away would be to leave with_scope public, but extract the part that
handles the :create hash to a private method.
Thanks, and I hope I'm not ranting too much here -- I'm just interested
in seeing this little corner of Rails be less surprising.
phil
2006/10/15, Phillip Kast <ph...@unimedia.org>:
> So what I want is to be able to say something in a controller like:
> scoped_by_current_account :story
> with the result that all find calls on a Story model from that
> controller will only return stories that belong to the current account,
> and any new stories will always be associated with the current account.
Isn't this clearer / more understandable ?
current_user.stories.find(params[:id])
It is obvious from this that you're fetching a user story, not a
random story. If I don't see your filters (which I almost never
bother to parse unless that's what I'm working on currently), then I
won't catch your filter usage.
Same goes for creating too:
current_user.stories.create!(params[:story])
# works the same with build
Bye !
--
François Beausoleil
http://blog.teksol.info/
http://piston.rubyforge.org/
Improved documentation seems like a better solution than to limit the
useage. This is Ruby we're talking about, there are lots of features
available to misuse already, that doesn't take away the legitimate
uses.
Best regards,
Tomas Jogin
The decision has been rendered as follows: with_scope will go protected
from Rails 2.0. That means you'll be able to use with_scope in your own
private finders (the only legitimate use I've seen), but not in, say,
filters.
Now we just have to find a way to get those deprecation messages in
there when its being called in the public space.
You must be new here :). Rails is opinionated software. We make things
that we consider good style easy to do and bad style hard. Or rather,
we make good style beautiful and bad style ugly. So even though
with_scope is going protected, you can still use it in filters if you
really, really want to. Just use send(:with_scope) -- that'll side-step
the access control. Yes, that'll be ugly and it's intended to be.
Using around_filters in the manor you describe makes it really hard to
figure out what's going on from looking at the action in isolation. It
looks like you're doing a straight, in-secure find to the naked eye.
And there's no bread crumbs for others to follow that'll leave them to
your around_filter. It's a recipe for hard-to-read code. And it shields
the reader from understand your model hierarchy.
In summary, Rails will shake its head at you for using with_scope in
filters, but will ultimately leave it at your discretion to choose the
right path. We believe in encouragement and discouragement, not
allowing or forbidding.
In summary, Rails will shake its head at you for using with_scope in
filters, but will ultimately leave it at your discretion to choose the
right path. We believe in encouragement and discouragement, not
allowing or forbidding.