with_scope should not be protected.

I recently upgraded my edge rails in order to take advantage of the
view_paths patch Rick applied the other day, and discovered with_scope
is now protected. I'm well aware of the abuses of with_scope, but
isn't this approach sort of heavy-handed? Since when has Rails been
about protecting programmers from themselves?

If core needed with_scope to implement assocations doesn't it stand to
reason that a Rails developer might need similar functionality? Let
me give you my use case:

class User < ActiveRecord::Base
  def accessible_property_find(*args)
    Property.with_scope(:find => accessible_property_options) do
      Property.find(*args)
    end
  end
end

A couple of things to note here:

* I'm not using this in a controller in a mysterious way, it's in a
model as it should be, but the protected method is still inaccessible
since I'm calling it from a different model.
* An association doesn't do the trick here, the actual conditions vary
depending on user type and aren't a simple foreign key.
* I know I could work around this by refactoring the functionality
into Property and passing the user as a parameter, but that makes my
code both more complicated and more opaque. The User should know what
properties it can access. A Property shouldn't have any concept of
the user.

So I'm requesting that this change be rolled back. Thoughts?

Since, um … forever?

Well why isn't it written in Java then?

You can always access it via send():

   Foo.send(:with_scope, ...) do
     # ...
   end

All that making it protected does is make sure people realize they
are taking their lives into their own hands when they play with it in
scopes where they shouldn't be playing with it.

- Jamis

+1 to rolling back this change until there's a way to make with_scope
available to association extensions.

The useful (and AFAIK popular) technique for creating typed join
models detailed at
http://blog.hasmanythrough.com/2006/8/19/magic-join-model-creation is
broken by making with_scope protected.

There were many fights about with_scope back when it was public. Rails is opinionated, and it is the opinion of the core team that it should become protected to ensure clean MVC. When you think about it, it makes sense. You can still leverage with_scope in your public model methods that accept a block.

There were also people that wanted to be able to access sessions and controller params in their models. Naturally, the core team wouldn’t allow that. Imagine what would happen if they did…

Oh … now I see buggy GMail keeps me out of sync, and that you’re discussing association extensions and model-to-model stuff. My bad, sorry - I will show myself out.

Come on now, that's a totally bogus analogy. The separation between M
and VC is huge. You gain so incredibly much from having your models
independent that there's absolutely no reason to support that.

The whole with_scope thing is much more nuanced. Please review my use
case and tell me how it should be done. If you saw my whole
architecture, you would see that giving Property any knowledge of User
to work around this would just be ugly. Sending the method to
Property is definitely the best solution unless there is some other
avenue I haven't thought of.

I just get this feeling that there's been this huge crackdown due to
misuse without looking at the actual use cases where it might be
justified.

Oh ... now I see buggy GMail keeps me out of sync, and that you're
discussing association extensions and model-to-model stuff. My bad, sorry -
I will show myself out.

Don't feel bad.. GMail got me too just now. :frowning:

class User < ActiveRecord::Base
  def accessible_property_find(*args)
    Property.with_scope(:find => accessible_property_options) do
      Property.find(*args)
    end
  end
end

NFI what accessible_property_options is, but I'm guessing it has to do
with the current user? This is closer to how I'd approach the
problem:

class Property < AR::Base
  def find_with_user(user, *args)
    with_user(user) { find *args }
  end

  protected
  def with_user(user, &block)
    with_scope :find => user.accessible_property_options, &block
  end
end

class User < AR::Base
  def accessible_property_find(*args)
    Property.with_user(*args)
  end
end

The Property store should be the one responsible for scoping its own data retrieval. If the User needs to specify conditions then it shouldn't be done w/o the Property datastore's knowledge (with_scope), it should tell the Property data store the conditions it wants applied, and the Property store can use whatever it needs to make that happen (in this case an AR with_scope). See Rick's example for a better separation of responsibilities.

-- tim

I see what you're saying, but I think it's a little presumptuous to
dictate this architecture just to satisfy an abstract concept of how
things should be structured. David often says how he hates contrived
examples, and removing with_scope access to other models feels like
it's based on certain assumptions that don't always hold. Here's
why:

* The first thing is that ideally this would be modelled by User
has_many :accessible_properties, but that is not possible because
accessible_properties does not have static conditions (they depend on
the instance, therefore they can't hardcoded with a class macro... if
there is a way around this please do tell). An association proxies to
another model without requiring extra hooks to be defined on the
target model. I want to do the exact same thing because:
* The notion of accessible properties is mostly meaningful to the
user. I think if you saw my entire app, you would see my point of
view. I have a pretty clean division of concerns where the User model
is in charge of policing its own access to things, while the other
models just remain focused on their own data. I realize this is sort
of a 6-of-one-half-a-dozen-of-the-other aesthetic issue, but:
* If I decide the only meaningful interface is through User, why
should I have to have twice the methods and add a layer of indirection
where with_scope is specifically designed for this situation? Rails
does place high value on aesthetics after all.

In short, I'm not trying to make a nuisance of myself on an old issue.
When I heard with_scope would be protected it didn't bother me
because I just assumed it would work in all models, but I didn't
realize it would mean you could only scope within the same model.
Given the limitations of associations, I think that's an undue
restriction.

First thing:

class << ActiveRecord::Base
  public :with_scope
end

Second thing:

* The first thing is that ideally this would be modelled by User
has_many :accessible_properties, but that is not possible because
accessible_properties does not have static conditions (they depend on
the instance, therefore they can't hardcoded with a class macro... if
there is a way around this please do tell).

You can do this. If you pass in a conditions string between single
quotes, it will be interpolated by your instance. That is:

has_many :accessible_properties,
  :class_name => 'Property',
  :conditions => 'face_id = #{id}'

#{id} will be the id of your object, not your class. Dynamic.

Brilliant. Okay, my grievances are really starting to melt away now.