default_scope can't take procs

The primary problem I have with the current implementation is
that it only supports a hash as the argument. I'd like to be able
to pass in a proc as well similar to how named and anonymous scopes
currently work.

My current work-around involves pushing a lambda into
default_scoping and implementing a simple argument selector to
emulate [] on a hash but there are other problems later if named
scopes are used in conjunction (I have to convert to a hash at that
point). I've browsed through the code and not found a quick fix.
I'll search more later but if someone else has an idea for fixing
this I'd be grateful.

Attached is a patch that adds support for setting a block for
default scope as such:

default_scope do
{ :conditions => ["created_at < ?", Time.now] }
end

Other than this it behaves almost the same as the previous
implementation. In the few places I had to change it, I tried to
mimic the behavior of named_scopes. The most notable of these is
that the order param now overwrites previous ones. For instance

will produce the following order clause: "age DESC" where
previously it would have produced "name DESC, age DESC".

Also, defining multiple default_scopes will stack effects in the
same way that multiple nested_scopes would. This way you could
define a sitewide default_scope and add on to it in subclasses.

Another small caveat is that, while the condition will be used
in the create scope if it is a hash, it will be ignored if it is a
string. So { :name => "Peter" } would be used whereas "name =
'Peter'" would not be. Personally, I don't think default scope
should affect the create scope, but since that was already there I
was hesitant to remove that functionality entirely.

Finally, to completely override the effects of default_scope,
use #with_exclusive_scope.

Here's an even better version. Default scope isn't merged in
until #scope is actually called which means that you can override
the default scope from within a #with_scope block. I also added a
#without_default_scope wrapper that temporarily disables all
default_scopes for that class.

Right. I actually don't use that patch. Instead I create an
object that poses as a Hash which seems to work well (a hash with a
proc passed is not enough since you can't merge or iterate properly
-- I basically have a proc subclass that generates proxied new
hashes on demand).

@Steffen There are issues that still won't work on that method.
Specifically creation scopes (it uses a has_key? rather than a nil
check last time I looked) as well as proper scope merging as they
use other keys and/or require iteration.

So if it is working for you, it is probably limited to very
specific cases. I would not recommend trying to use that method in
more general code.

Notice the line with + sign. I added that line and now
default_scope can take proc. However since the default_scope
finally builds a relation object, the lazy loading behavior of
default_scope is lost. It means if you define

The issue is that default scopes should be stored as procs and
evaluated on the last moment. Evaluating the proc when appending
the scope will not work as expected.
Its not too complicated to get the default_scope to accept procs
with hash conditions, but supporting arel conditions is a bit more
complex.
I managed to get some concept code working, supporting all possible
default_scope conditions with the exception of arel conditions
needing a unscoped prefix.

As the scopes are merged and evaluated at the last possible
moment, i.e the current_scoped_methods method, and arel conditions
are delegated to the scoped method, it will end up in an endless
loop (as scoped will access the current_scoped_methods method,
which in turn then makes a call to scoped etc).

I'm a little confused. I've just cloned rails/master and built a
gem only to find out this feature still isn't working. Tim's patch
with new current_scoped_methods and test cases is merged, but
Tanel's modification of default_scope method is not. The lambda is
immediately passed to construct_finder_arel and produces a familiar
error.

Also, Tim, ActiveRecord::Relation responds to :call so maybe
current_scoped_methods should use is_a?(Proc) like in Neeraj's
version.

I've just hopped into this conversation, so a huge thanks to you
guys for looking into this issue.

When Coach began as Manhattan Leather Bags in 1941, they were
inspired by baseball glove design and they created discount
handbags with similar qualities.supra shoes didn't come
along until years later. The original handbag collection had twelve
designs, and they were made from supple, tan, top-quality leathers,
and showed the same excellent craftsmanship and stitching that we
still expect today.

Coach hired a woman named Bonnie Cashin in 1962, and she brought
new looks to Coach bags. She used many new fabrics and organic
materials like jersey and wool. She created the brass hardware that
is symbolic of timberland boots.
When Lew Frankfort came aboard in 1979, he brought Coach to the
next level, and they became a name known the world over. Affordable
luxury was not a concept that many people knew, but now they would.
Soon after Frankfort joined Coach, they came out with a catalog,
and opened their flagship store in New York City.

In the 1980's, Coach expanded and made innovations that brought
its brand relevance and strength. They designed watches starting in
1988, and also expanded their company to Tokyo. Japan still counts
among the leading purchasers of coach purses and Coach
heels. The manufacture of Coach shoes began in the mid 90's, and
Reed Krakoff, the lead designer, loaned his talents to help put
Coach heels and louis vuitton bags
outlet on the map. Their shoes aren't as popular as
their handbags, but they are well worth the money invested in their
purchase.

Women love heels, but at times you may want to wear shoes that
don't have this height. true religion jeans
outlet are as dedicated to comfort as they are to
style and pizzazz. Coach platforms come in many styles, from gold
loafers to slingbacks with open toes. You may be able to find Coach
shoes at discount prices online, but be sure that you are buying
genuine Coach products.

If you're looking for moncler
jackets that will make you feel and look glamorous,
Coach shoes and chanel
purses can do that well. Coach sneakers and heels will
make you feel sporty, too. When selecting pumps or sandals to wear,
you'll be happy to count on the long-standing quality and service
of the Coach company. They have many styles to choose from,
including chic and glamorous Coach boots and Coach heels. Whether
you are looking for sports shoes like Coach sneakers or sexy shoes
like Coach boots, the Coach name will never let you down.

Tim, sorry for late reply. I've tried your example and it indeed
worked. But when I switched my project to 3.0.3 gem and applied
your patch I was still getting errors. After some digging I've
found out that the problem is with models that use multiple calls
to default_scope. This is allowed by rails and the exceptions raise
when default_scope tries to merge scopes provided with multiple
calls.

Actually, I've just found out that my previous patch and all
other patches in this ticket were just ignoring the previous
default_scope definitions instead of merging them with the
current.

I've updated my gist to take previous default_scope declarations
into account. This is consistent with how default_scope works for
non-lambda arguments. This patch allows you to have declarations
like this one:

If you feel this is a bug you can open a new ticket. But
supporting it would be a bit tricky. Uniqueness validator expects
column names which it compares with simple = sql operator. Named
and default scopes are much more powerful. Beside allowing all the
order(), limit(), offset() etc. finders they allow more complex
where() conditions.

You might need to add the :scope option explicitly to your
validations or if that's not possible you can do something similar
to what I did in a project at my company, but I have to warn you -
it's a really ugly hack:https://gist.github.com/791214

This fits my project, but you might want to modify this to read
scope column names from model's class variables.

Internally order and limit (as well as
where and others) are delegated to scoped that
calls current_scope_methods, therefore, the last two calls
will calculate lambdas immediately, save the result, and keep
appending it to the final query (thanks to
construct_finder_arel). If one of the first two
default_scope lambdas passes a hash to where like
where(:locale => I18n.locale.to_s), the last two scopes
will completely overwrite the locale attribute in the
final query.

Please also try with regular scopes + where, order, etc. Again
they will just overwrite default scopes. My workaround is a module
called DynamicDefaultScoping, I include it in classes
where I need dynamic default scopes after all
calls to regular default_scope and scope and then
user already dynamic default_scope.

In that module I've also implemented the possibility to name
default scopes like:

That call to order will evaluate default scope and save it's
current state in :red named scope. The same thing causes the
Post.unscoped.published == Post.published absurd. Also calling
multiple named scopes like Post.published.recent.by_author("Mike")
pollutes the final query with multiple default_scope nodes.

This way we can execute them inside unscoped.scoping {} and
apply clean default/named scopes when necessary. Cons: API change;
application code is a bit ugly; scope could no longer accept
extensions like it does now - through blocks.

It's a general idea, but one specific implementation I thought
about is: With each created ActiveRecord::Relation keep it's second
copy that would be free of definitions from default scope. Let's
say that the copy is called r.without_default. Relations returned
from default scope would have the without_default cleared to
unscoped. Then every scope merge or modification would affect both
copies equally. Named scopes would be merged into current relation
using their without_default copy.

Cons: rails code gets a bit ugly; we're wasting resources

.3. Keep the default scope separate from the current scope stack
and only merge it into the current relation when we're about to hit
database.

Cons: also some ugly rails code; not as simple as the above;
might brake 3rd party gems that use methods like where_values,
order_values, because those wouldn't contain data from default
scope.

I've started implementing 3 and then thought 2 would be much
quicker. You can find it on my pull request on
github. I'm also attaching the patch.

I think that Adam Wróbel is correct in his general idea:
AR::Relation should be refactored because there are some problems
with current code. I think we should find some new way to store and
apply default_scopes.

Known issues: this ticket, ticket #6011 (which
as I understand difficult to fix in edge without ugly fixes) also
we have problems with except (ticket #6290, hey
can anybody review it???). Anything else?

I think that the best solution for now is to prevent merging
default_scopes with procs. It looks very standoffish for all users
and especially for new users to Rails3 and Rails at all when rails
raises some internal exception like

I think that it is not acceptable. So right now we can raise an exception (see my patch at https://rails.lighthouseapp.com/projects/8994/tickets/6297, also I will attach it here) and start working on refactoring this code. I think we should find as many issues about AR::Realtion as possible for now to design new great solution.

I've started a discussion on core with suggestion to change
scope syntax. I think the syntax change is certainly a better way
of resolving this issue than a hacky work around like the one I've
posted here.