Problem with Thinking Sphinx and scopes - ruby-on-rails

For several hours now I am unsuccessfully trying to get sphinx scopes work.
I want to scope tags of ActsAsTaggableOn. In my model (that is taggable) I tried the following scopes:
# This normal scope works
scope :tagged, lambda {
joins(:taggings => :tag).
where("tags.name = 'consequatur'")
}
# fails! (can't convert ActiveRecord::Relation into Hash)
sphinx_scope :tagged do
joins(:taggings => :tag).
where("tags.name = 'consequatur'")
end
Another try with the old conditions:
# works with normal scope (returns one record)
scope :tagged, :joins => :taggings, :conditions => {"taggings.tag_id" => 74}
# fails! (returns nothing)
sphinx_scope(:tagged) do
{:joins => :taggings, :conditions => {"taggings.tag_id" => 74}}
end
How can I make those scopes work? Is there another way to archive that task? I want to only search those models that are tagged with a specific tag.

Related

Updating named_scope :all for new ActiveRecord format

I have been upgrading named scopes to scopes following this guide http://m.onkey.org/active-record-query-interface. The one thing I have run into in the code I am working with that I have not seen in any examples is how do deal with
named_scope :all, <ect>
The closest example I have seen is in the m.onkey.org guide which is
named_scope :red, :conditions => { :colour => 'red' }
to
scope :red, :conditions => { :colour => 'red' }
So would the same idea apply, making my new code
scope :all, <ect>
or am I missing something?
The scope .all is reserved since ActiveRecord allows you to retrieve all the entries with the same method:
User.all #=> returns ALL the users existing in the DB
You should rename your scope to a different name.
Also, you don't have to precise the conditions keyword. Here is part of codes I'm using in my projects:
scope :ordered, order(:name)
scope :with_color, lambda{ |color| where( color: color.try(:to_s) ) }
# this lambda scope is usable by:
# Model.with_color(:red)

How to combine different scopes in Rails 3?

I have the following named scopes in my rails app:
scope :published, :conditions => {:status => 'published'}
scope :coming_soon, :conditions => {:status => 'coming_soon'}
scope :in_development, :conditions => {:status => 'in_development'}
scope :cancelled, :conditions => {:status => 'cancelld'}
I'm having trouble writing one that combines "published" and "combing soon." Here's what I've tried.
scope :public, :conditions => {"status == published || status == coming_soon"}
Any ideas?
Rails 2: named_scope :public, :status => ['published', 'coming_soon']
Rails 3: scope :public, where(:status => ['published', 'coming_soon'])
Rails will see the array and use the IN operator in the sql.
An a note: The other approach (chain existing scopes) of Article.published.coming_soon would NOT work because an Article can't be both of those things at the time time or be a subset of each other.
Another note: Careful when you want something dependent on a variable parameter. For example say you wanted "future appointment" for a scheduling system, you might write
[This is invalid]
Rails 2: named_scope :upcoming_appts, :conditions => (['appt_dt > ?', Time.now])
Rails 3: scope :upcoming_appts, where(['appt_dt > ?', Time.now])
However there's a problem: The Time.now will get evaluated the first time the class is evaluated not when the scope itself is evaluated.
To overcome this you use a lambda (silly name but basically means anonymous function - or to put it even simpler 'a function that doesn't actually have a name') as follows:
[This is valid]
Rails 2: named_scope :upcoming_appts, lambda {:conditions => (['appt_dt > ?', Time.now])}
Rails 3: scope :upcoming_appts, lambda {where(['appt_dt >= ?', Time.now])}
This scope will now get evaluated at execution time each time - it is used - so Time.now will be the actual current date-time.

Can rails scopes filter on the number of associated classes for a given field

I am using Rails 3 with Mongoid.
I have a Folder class that can then be shared with other User classes as such
class Folder
has_one :owner
has_many :users
I am trying to create two scopes one that can be used to return all private folders, and one to return all shared folders. Is there any way to count the number of associations within a scope?
scope :personal, where(:users.count => 0) #Erroring on count...
scope :shared, where(:users.count.gt => 0) #Erroring on count...
I've considered building methods instead but would prefer scopes as I wish to chain them with other scopes.
Since you're accessing referenced documents - your users method is a virtual attribute, which you can't access during your query. You can however use user_ids (the array of User ids in your Folder document) to perform the kinds of queries you want:
Either of these should work for your personal scope:
scope :personal, where(:user_ids.size => 0)
# or
scope :personal, where(:user_ids => [])
And for your shared scope:
scope :shared, where(:user_ids.ne => [])
scope :personal, where({ "$or" => [{:user_ids => nil}, {:user_ids => {"$size"=>0}}] })
scope :shared, where({ "$nor" => [{:user_ids => nil}, {:user_ids => {"$size"=>0}}] })
This should handle both the cases when :user_ids is not set or is null and when it is set to an empty array.

Rails 2.3.x - how does this ActiveRecord scope work?

There's a named_scope in a project I'm working on that looks like the following:
# default product scope only lists available and non-deleted products
::Product.named_scope :active, lambda { |*args|
Product.not_deleted.available(args.first).scope(:find)
}
The initial named_scope makes sense. The confusing part here is how .scope(:find) works. This is clearly calling another named scope (not_deleted), and applying .scope(:find) afterwards. What/how does .scope(:find) work here?
A quick answer
Product.not_deleted.available(args.first)
is a named-scope itself, formed by combining both named scopes.
scope(:find) gets the conditions for a named-scope (or combination of scopes), which you can in turn use to create a new named-scope.
So by example:
named_scope :active, :conditions => 'active = true'
named_scope :not_deleted, :conditions => 'deleted = false'
then you write
named_scope :active_and_not_deleted, :conditions => 'active = true and deleted = false'
or, you could write
named_scope :active_and_not_deleted, lambda { self.active.not_deleted.scope(:find) }
which is identical. I hope that makes it clear.
Note that this has become simpler (cleaner) in rails 3, you would just write
scope :active_and_not_deleted, active.not_deleted
Scope is a method on ActiveRecord::Base that returns the current scope for the method passed in (what would actually be used to build the query if you were to run it at this moment).
# Retrieve the scope for the given method and optional key.
def scope(method, key = nil) #:nodoc:
if current_scoped_methods && (scope = current_scoped_methods[method])
key ? scope[key] : scope
end
end
So in your example, the lambda returns the scope for a Product.find call after merging all the other named scopes.
I have a named_scope:
named_scope :active, {:conditions => {:active => true}}
In my console output, Object.active.scope(:find) returns:
{:conditions => {:active => true}}

named_scope is creating new objects if I use it with a :join

Disclaimer: My original exposure to Ruby on Rails was literally years ago, and a great many things are new to me now that I'm picking it back up. One of these things is named scopes. They seem great, but I'm not getting the result I expect. Here's a for-instance:
class User
has_many logs
named_scope :logged_in, :joins => ['logs'], :conditions => ['logs.logout_at IS NULL']
end
Class Log
belongs_to user
end
It is my understanding that doing a
User.logged_in
should be exactly the same as doing a
User.find(:all, :joins => ['logs], :conditions => ['logs.logout_at IS NULL'])
But instead, I'm getting back different objects. To demonstrate:
real = User.find_by_name('admin')
#<User id:12345, name: 'admin' ... >
fake = User.logged_in.find_by_name('admin')
#<User id: 54321, name: 'admin' ... >
So my question is: Where on earth is this new object coming from, and how do I get named_scope to give me the original one?
named_scope :logged_in,
:conditions => ["logs.logout_at IS NULL"],
:include => :logs
It looks like you have multiple users named 'admin'
Try this:
User.find_all_by_name('admin')

Resources