This project is archived and is in readonly mode.
:through option for "named_scope"
Reported by MatthewRudy | April 27th, 2008 @ 11:59 PM
The new "named_scope" functionality is great.
But it feels like it should have the option of specifying a :through scope name,
through which the scope will be defined.
(my git patch has a more full description)
Comments and changes to this ticket
-
MatthewRudy April 28th, 2008 @ 12:00 AM
==============
from the patch
==============
Subject: [PATCH] adding :through option to "named_scope"
named_scope is a welcome addition to rails core,
and it's a treat to see examples like;
Topic.approved.replied
working right out of the box.
My patch aims to make it easier to define a combined scope;
named_scope :approved_and_replied, :conditions =>
['replies_count > 0'], :through => :approved
My patch adds this functionality, as well as allowing arrays of :through
scopes (the array case needs a test to be added, but I need some advice
on how to extend the Topic class in test/models to allow this)
A clear next step is to allow the following;
:through => {:written_before => "2007-01-01", :approved => nil}
which would call the "written_before" scope, passing it the value as
argument
A more amibitious aim is to allow arguments to accumulate from :through
scopes,
eg.
named_scope :written_before_and_replied, :conditions => *blah*,
:through => :written_before
would yield a scope object which passed the first argument to
:written_before
Topic.written_before_and_replied(1.year.ago)
same as Topic.written_before(1.year.ago).replied
-
MatthewRudy April 28th, 2008 @ 10:24 PM
- Assigned user set to Pratik
-
DHH April 30th, 2008 @ 08:45 PM
- State changed from new to incomplete
Looking good, but missing docs and tests. Please change status to open when fixed.
-
duncanbeevers May 2nd, 2008 @ 11:02 PM
I've been thinking about similar functionality, and thus far I've found it most flexible to simply specify composite scopes as proper methods.
def self.written_before_and_replied time written_before(time).replied end
I feel the added complexity of the implementation of a :through option that decomposes arguments for passing to various scopes outweighs the benefits of the slightly shorter syntax, especially in the case of named scopes with optional or splat arguments.
-
MatthewRudy May 3rd, 2008 @ 12:46 AM
i think my current test example doesn't quite explain it well.
the point is,
why create a scope, just so you can combine it with another one.
we want 1st condition,
we want 1st and 2nd condition combined,
but we may not want 2nd condition on its own...
so creating a scope, just so we can compose it,
doesnt make sense.
example:
class Monkey < ActiveRecord::Base # we assume only living monkeys have use named_scope :living, :conditions => ["dead = ?", false] named_scope :useful, :conditions => ["nimble = ? AND randy = ?", true, true], :through => :living end
we have no use for finding any dead monkeys who are nimble and randy,
so we cut the unneccessary scope.
but yeah,
i need to add some examples to the documentation,
and get the testing up to scratch.
-
MatthewRudy May 4th, 2008 @ 11:32 AM
- no changes were found...
-
MatthewRudy May 4th, 2008 @ 11:32 AM
proper testing, bit of documentation.
will do one more bit of testing later.
-
MatthewRudy May 9th, 2008 @ 12:41 PM
hmm...
still need to do this,
Tests that are missing are;
"doesnt work with Proc"
"doesnt work through named_scopes with Proc"
I wonder if these shortcomings should be fixed, before I reopen it
- that's why I have written the tests yet.
Making it work with Procs would involve a much more complex implementation, I think.
Should really put in the time over the weekend to try and make it work.
-
Mathijs Kwik May 21st, 2008 @ 10:26 AM
I would like to add some thoughts on this topic.
Combining/composing/through'ing named_scopes would be nice to have indeed, but I would like to mention that this feature would be very good on associations.
class Product < ActiveRecord::Base belongs_to :category named_scope :cheap, :conditions => { :price => 0..5 } named_scope :recent, lambda { |*args| {:conditions => ["released_at > ?", (args.first || 2.weeks.ago)]} } named_scope :visible, :include => :category, :conditions => { 'categories.hidden' => false } end
In this example 'visible' is just a simple attribute, but it's from an associated model (category). What if category had more attributes, like hidden, private, public, read_only and stuff like that. Say Category already has some named_scopes, for example 'free_for_all' which means a category is public but not read-only.
Now it would make sense (maybe not in this example but in general) to be able to use these named_scopes in the Product model, not on instances only, but in named_scopes themselves as well. So Product.in_free_for_all can just use the 'free_for_all' scope in Category. Otherwise you would have to define this 'logic' twice, which isn't very DRY. I hope my explanation is clear enough...
Of course I know that I could also use Category.free_for_all.map(&:products), but then all products get fetched right away which means sorting them (and other nice stuff finders have) will have to be done in ruby instead of in the DB.
A solution I use at the moment is this, but I'm not fully satisfied with it yet, so any comments are very welcome...
class Product << ActiveRecord::Base belongs_to :category named_scope :in_free_for_all, lambda {{ :conditions => {:category_id => Category.free_for_all.find(:all, :select => 'id').map(&:id)} }} end
Which will do 2 queries, which is default for :include in rails 2.1 anyway I believe.
While this solution works, it would be a lot nicer if rails could somehow write a subquery for it. Along the lines of:
(example, doesn't work)
named_scope :in_free_for_all, :conditions => {:category => Category.free_for_all}
should translate to:
select * from products where category_id in (select id from categories where public = 't' and read_only = 'f')
I thought I'd mention this 'wishlist-item' in this ticket, since it's very related.
Thanks,
Mathijs
-
Ryan Bigg April 10th, 2010 @ 09:40 PM
- Tag set to activerecord, edge, named_scope, through
I was under the impression that chaining scopes did this.
-
Ryan Bigg April 10th, 2010 @ 11:59 PM
- State changed from incomplete to needs-more-info
Yes, I think that if you were to chain the named_scopes or even use anonymous scopes it would work. I'm open to further discussion on this ticket.
-
Ryan Bates April 11th, 2010 @ 05:17 AM
This is possible with the new Active Record query syntax added in Rails 3.
scope :visible, where(:hidden => false) scope :available, visible.where(:discontinued => false)
This is also much cleaner than the
:through
solution provided in the ticket. -
MatthewRudy April 11th, 2010 @ 12:22 PM
that's all fair enough
this was 2 years agothe problem with the original ticket
(back when it was necessary) was that there was no clear way to deal with lambda-based scopes
so... i ended up never having a complete solutionfeel free to close this off
or perhaps add documentation to the "scope" method
pointing out that you can use an existing scope as part of the constructor. -
Rohit Arondekar June 15th, 2010 @ 12:05 PM
- State changed from needs-more-info to invalid
-
Oinak July 20th, 2010 @ 02:06 PM
- Importance changed from to
You can use proxy_options to use one named_scope from another:
class Thing #... named_scope :billable_by, lambda{|user| {:conditions => {:billable_id => user.id } } } named_scope :billable_by_tom, lambda{ self.billable_by(User.find_by_name('Tom').id).proxy_options } #... end
This way it can be chained with other named_scopes (as an advatange to use custom class methods).
I use this in my code and it works perfectly.
-
Neeraj Singh July 20th, 2010 @ 02:36 PM
class User < ActiveRecord::Base named_scope :billable_by, lambda{|user| {:conditions => {:billable_id => user.id } } } named_scope :billable_by_tom, lambda{ self.billable_by(User.find_by_name('Tom').id).proxy_options } end
Above code does not work in rails edge. There is no method called 'proxy_optioins' in edge.
-
Ryan Bates July 20th, 2010 @ 05:05 PM
Neeraj, In Rails 3 you can just call the scope directly.
class User < ActiveRecord::Base named_scope :billable_by, lambda{|user| {:conditions => {:billable_id => user.id } } } named_scope :billable_by_tom, lambda{ billable_by(User.find_by_name('Tom')) } end
-
Oinak July 20th, 2010 @ 09:23 PM
Neeraj:
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html
documents the proxy_options feature of named_scope
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 223 named_scope that composes other named_scopes That's a pretty cool idea. It's similar to ticket #57.
- 223 named_scope that composes other named_scopes Closing until we have a patch. Also looks like a duplica...