Correctly using Scope in rails 4 model - ruby-on-rails

I'm using scope to fetch a random banner, I'm using the gem Randumb
scope :sidebar_top, -> { where(ad_type_id: 2).order_by_rand.first }
When no advert is found, all rows are returned. According to the Randumb docs, it should return nil in the instance of nothing being found.
Should I be using scope to return a single instance in the first place? It seems like the best way, but I rarely see scope examples returning less than a small subset.
Any ideas how I can return nil if nothing is found?
Thanks

Something like this? A class method instead of scope.
def self.sidebar_top
where(ad_type_id: 2).blank? ? nil : where(ad_type_id: 2).order_by_rand.first
end

You shouldn't use scope to return a single instance. Observe [1]
and as for the answer:
def self.sidebar_top
Array(where(ad_type_id: 2).order_by_rand).first # single query
end
[1] scope expects a scope return: If the proc returns nil or false, a scope is returned instead.
scope :this_or_all, ->(feeling) { where(state: feeling) if feeling.present? }
Hence, you can do this User.this_or_all(nil).this_or_all(happy).

Related

Why find_by scope returns an array when the result is nil

I have a scope in my model:
scope :default_template, ->(template_type) { find_by(is_default: true, template_type: template_type) }
I'm using find_by but the problem is: when there is no record, the result of the scope is a blank array instead of nil.
EmailTemplate.default_template('blahblah') # => []
Can someone explain why?
That's stated in the docs:
... If it returns nil or false, an all scope is returned instead.
In order for your scope to be composable, it should return an ActiveRecord::Relationship, and if no record satisfies the given criteria an empty ActiveRecord::Relationship is returned. That ensures that further scope invocations in the model don't raise a NoMethodError when invoking them on a nil result.
If you really need it to return nil, then make your scope a class method. That will preserve the original return value.

Rails: Is it possible to check if a named scope is valid for a given active record?

I'm Using Ruby 1.8.7-p374 and Rails 2.3.18. (Yeah, I know, we're working on it.)
I'm considering dynamically passing a named scope to be used as a condition for an ActiveRecord. Is it possible to check to see if a passed string is a valid named scope for the record?
For Example:
If I have a named scope called :red defined for Car
named_scope :red, :condition => ["color = ?", "red"]
Then is there some function where I could do
Car.some_function("red") # returns true
Car.some_function("blue") # returns false
Thanks in advanced.
You can use .respond_to?(:method) (documentation here)
In your case :
Car.respond_to?(:red) # => true
Car.respond_to?(:blue) # => false
But you said:
I'm considering dynamically passing a named scope to be used as a condition for an ActiveRecord
I hope you will not use something like this:
# url
/cars?filter=red
# controller
def index
#cars = Car.send(params[:filter]) if params[:filter].present? && Car.respond_to?(params[:filter])
#cars ||= Car.find(:all)
Guess what woud happen if I use this URL?
/cars?filter=destroy_all
The Car model responds to the method .destroy_all, so Ruby calls it on the Car model. BOOM, all cars are destroyed!
Klass.scopes will return a hash of all scopes for that class. You can see if it's in there - the names are stored as symbols. eg
if Car.scopes[:red]
...
This will return the scope itself (truthy) or nil (falsy), which is fine for passing/failing an if test. If you literally want either true or false back then you can do !! on it to convert it to a boolean.
a_bool = !!Car.scopes[:red]

Why do I get this error when I try to use scope?

I get this error.
undefined method 'recent' for #
My codes are
User controller
#users = User.find_by_username(params[:id]).all_following.recent
User model
scope :recent, lambda { |n = 10| order("last_active_at DESC").limit(n) }
if using lambda , should it be taking arguement ?? if you know exactly what the limit ,don't need to use lambda
try looking this guide on
13.2
http://guides.rubyonrails.org/active_record_querying.html#working-with-scopes
From the gem documentation all_following is an array. This array may even be non-homogeneous.
The recent scope is really a method that can be called on an ActiveRecord.
I'm afraid the two don't match up and that's why the error says you can't call recent on an instance of an Array.
I hope that helps.
I don't think this is possible using scope, because the all_following method will return an array, so it will not even look in the User model to try to find the scope. Another reason you should not use scope is that scope is for fetching items from the database in a particular way that you want to reuse, not sorting things. To get the functionality you want, I would add a method to the Array class like this:
class Array
def recent(limit = 10)
self.sort_by { |users| users[:last_active_at] }[0..(limit-1)]
end
end
Then you can call #users = User.find_by_username(params[:id]).all_following.recent(3) in your controller, and pass in any value you want for the limit value. If you leave off the limit value, such as with #users = User.find_by_username(params[:id]).all_following.recent, then it will use 10 as the default.

Scoped and scope in rails

Can somebody explain what this method does and what I can pass to it?
scoped(options = nil)
Returns an anonymous scope.
And also what the scope method does? I don't understand after reading the documentation.
In ActiveRecord, all query building methods (like where, order, joins, limit and so forth) return a so called scope. Only when you call a kicker method like all or first the built-up query is executed and the results from the database are returned.
The scoped class method also returns a scope. The scope returned is by default empty meaning the result set would not be restricted in any way meaning all records would be returned if the query was executed.
You can use it to provide an "empty" alternative like in the query_by_date example by MurifoX.
Or you can use it to combine multiple conditions into one method call, like for example:
Model.scoped(:conditions => 'id < 100', :limit => 10, :order => 'title ASC')
# which would be equivalent to
Model.where('id < 100').limit(10).order('title ASC')
The scope class method allows you to define a class method that also returns a scope, like for example:
class Model
scope :colored, lambda {|col|
where(:color => col)
}
end
which can be used like this:
Model.colored
The nice thing with scopes is that you can combine them (almost) as you wish, so the following is absolutely possible:
Model.red.where('id < 100').order('title ASC').scoped(:limit => 10)
I also strongly suggest reading through http://guides.rubyonrails.org/active_record_querying.html
I have used it in the past.When you make chained calls to the ActiveRecord query interface like this:
Model.where(:conditions).where(:more_conditions).where(:final_conditions)
Each one of them is already scoped, making the chain work without any problems. But let's say you have something like this:
Model.query_by_date(date).query_by_user(user).query_by_status(status)
scope :query_by_date, lambda { |date|
case date
when "today"
where(:date => Date.today)
when "tomorrow"
where(:date => Date.tomorrow)
else
# Any value like '' or 0 or Date.whatever
end
}
This would cause an error if the date param is not today or tomorrow. It would pick the last value and try to chain this query with the next one query_by_user, resulting in a undefined method default_scoped? for ''. But if you put a scoped method in the else condition, it would work without any flaws, because you are saying to activerecord that you pass through this method/named scope and didn't make any calls to where/find/other activerecord methods, but returned a scoped object, so you can continue chaining queries and stuff.
It would be this way in the end.
else
scoped
end
Hope you understand this simple example.

Rails scope with HABTM relationship count

I have an Event class with a HABTM relationship with a User class. I'm trying to create a Event scope that includes only Events that have 2 Users associated with it.
I currently have a Event#status method that returns the following:
def status
self.users.length == 2 ? "matched" : "not matched"
end
So now basically I'm trying to find how to write a scope that includes all "matched" events. I tried scope :matched, self.users.length == 2, which didn't work at all, but is there a similar way that I'm missing?
EDIT: This class method does this correctly, but it'd still be nice if I could encapsulate it in a scope.
def self.pending
Event.all.map { |e| e if e.status == "matched" }
end
You've got a few problems here. Right now, your status method is returning literal strings, which is a bit surprising -- it would be more common to have this return a Boolean value. Also, the name status is not descriptive -- perhaps exactly_two_users? would be better. In addition, if you use users.count instead of users.length, then the DB will do the count more efficiently.
Your scope could simply be where(:users.count => 2), I believe.

Resources