I have a model with some scopes defined on it:
class Alarm < ActiveRecord::Base
scope :open_alarms, where("state != ?", AlarmStates::STATELIST[-1])
scope :new_alarms, where('state is null')
scope :site_alarms, lambda{ |site| where(:mac_address => site.mac_address)}
#etc
end
These scopes are all Relation objects, so I should be able to chain them in a with_scope call. But I can't get it all to work. I have been trying something along the lines of
Alarm.with_scope(:find => Alarm.open_alarms){all}
which, AFAIK, should apply the Relation Alarm .open_alarms onto the find, and then effectively find all alarms matching the open_alarms scope. But I get an 'unidentifed variable or method "all" for Main:Object' error when I try this. I have tried a whole series of variants, but nothing seems to get there.
Where I am trying to get to is being able to chain a series of scopes, then apply pagination so I can output pages of alarms.
Any help would be very much appreciated.
Thanks,
Steve
#alarms = Alarm.open_alarms.page(params[:page])
#alarms = Alarm.open_alarms.site_alarms("http://sample.me").page(params[:page])
(Using gem will_paginate or kaminari).
You seem to be using pre 3.2 code to do your query.
Related
I have a scope method defined in my Child's model profile.rb as follows
scope :fees_to, -> (fees_to) { where("fees_to <= ?", "#{fees_to}") }
And in the parent which is tutor.rb there is
has_one :profile, dependent: :destroy
Now in rails console i can do Profile.all.fees_to(10) for example and its valid. But how can i do call the fees_to method through the parent Tutor?
Basically now i am able to filter through my profiles in the index view of it. But what i would like to do is to filter through the tutor index view based on values from the child.
All help and advice would be greatly appreciated! Thank you
This makes no sense with a has_one. You cannot "have one" and still apply a scope to it. Scopes work on many records, to apply additional conditions and narrow down the result set, not specific records.
You're effectively trying to add a where(fees_to < ?) condition to a specific record, which obviously makes no sense.
If you want to use a scope via an association, it needs to be a has_many.
In profile.rb:
You can create a function like:
def fees_to n
Profile.fees_to(n) # your scope is called here
end
Then in rails console :
tutor = Tutor.first
tutor.profile.fees_to(10)
it works like a filter. If profile's fees_to is > n, you will get [] (array empty)
UPDATED
bad => #tutor = Tutor.all
good => #tutor = Tutor.includes(:profile).all # with eager loading, avoid n+1 query.
may be you want make loop like that:
#tutor.each do |tutor|
tutor.profile.fees_to(10)
end
I defined the following in my tutor.rb
def self.fees_search(n)
#profile = Profile.fees_to(n)
if #profile.empty?
return Tutor.none
else
#profile.each do |y|
y.tutor
end
end
end
Not sure if its clunky or if there are redundancies, but it works perfectly fine in rails console. I can do Tutor.fees_search(10) and it renders all the respective Tutors accordingly.
Cheers
Is there a way to use a filter criterion in where, which is not a DB column. If I have a Movie model with the following method:
def blockbuster?
imdb_rating > 8
end
is there a way to do something like Movie.where(:blockbuster? => true). I know that in this particular example it's possible to just use the imdb_rating attribute (Movie.where('imdb_rating > ?', 8)), but there are cases, when a method does a more complex logic. Currently, if I want to do this, I must call Movie.all.select(&:blockbuster?), but I want to do this at the DB level. Thank you.
P.S. I am sure that similar questions are asked many times, but I can't seem to think of the right keywords to find them here or on Google. Please, post a link if this is answered elsewhere.
Have you tried making it into a scope? There is some information on scopes in the Rails documentation.
Basically, with your method, you'd do something like:
class Movie < ActiveRecord::Base
scope :blockbuster, -> { where('imdb_rating > ?', 8) }
end
Movie.blockbuster # returns all relevant objects as an ActiveRecord relation
Class methods are also available as scopes:
class Movie < ActiveRecord::Base
def self.blockbuster?
where('imdb_rating ?', 8)
end
end
Movie.blockbuster? # also returns all relevant objects as an ActiveRecord relation
In Rails3 I have:
Class Teacher
# active :boolean
has_and_belongs_to_many :subjects
Class Subject
# active :boolean
has_and_belongs_to_many :teachers
I am trying to construct a Teacher scope that returns all Teachers that are active or are associated with a Subject that is active.
These scopes work individually, but how to combine them as a single scope with an OR?
scope :active_teachers, where(active: true)
scope :more_active_teachers, joins(:subjects).where(:subjects => {active: true})
I've tried this without success:
scope :active_teachers, where(active: true).or(joins(:subjects)
.where(:subjects => {active: true}))
UPDATE:
I thought I had a solution, but this no longer lazy loads, hits the database twice and — most importantly — returns an array rather than an AR object!
scope :active_teachers, where(active: true) |
joins(:subjects).where(:subjects => {active: true})
You have Squeel to your rescue. More details here.
Using that, you could define something like:
class Teacher
...
scope :active_teachers, joins{subjects}.where {(active == true) | (subjects.active == true)}
...
end
I think the short answer is you can't.
Oring in the code is going to break lazy loading ... really no way around it as you need the database to make the evaluation. ActiveRecord can't make the evaluations on the scopes without executing each subclause individually.
Something like this the following should work:
joins(:subjects).where("subjects.active = true OR teachers.active = true")
Not quite as elegant, but can be wrapped into a method for reuse.
You can solve this by dropping to AREL. See this SO Question for how to do that.
AREL OR Condition
Or from the AREL Source Code README.md. I think (but haven't verified) that this would translate to the following for your particular example.
teachers.where(teachers[:active].eq(true).or(subjects[:active].eq(true)))
Good Luck!
There's a rails pull request for this (https://github.com/rails/rails/pull/9052), but in the meantime, some one has created a monkey patch that you can include in your initializers that will allow you to do this and still give you an ActiveRecord::Relation:
https://gist.github.com/j-mcnally/250eaaceef234dd8971b
With that, you'll be able to OR your scopes like this
Teacher.active_teachers.or.more_active_teachers
or write a new scope
scope :combined_scopes, active_teachers.or.more_active_teachers
I have a situation where the behavior of an existing app is changing and it's causing me a major headache.
My app has Photos. Photos have a status: "batch", "queue", or "complete". All the existing Photos in the app are "complete".
99% of the time I need to only show complete photos, and in all of the existing codebase I need every call to Photos to be limited to only complete photos.
However, in the screens related to uploading and classifying photos I need to be able to fairly easily override that default scope to show batched or queued photos.
Like many others, I need to find a way to easily override the default scope in certain situations. I looked at these questions (1, 2) and they don't seem to answer what I'm looking for.
The code I wish worked is this:
class Photo < ActiveRecord::Base
...
default_scope where(:status=>'complete')
scope :batch, unscoped.where(:status=>'batch')
scope :queue, unscoped.where(:status=>'queue')
...
end
However, that doesn't work. I tried wrapping the scope methods in lambdas, and that didn't work either.
I realize default_scope comes with baggage, but if I can't use it with overrides then I'm looking at adding scope :complete ... and having to comb through every call to photos in my existing app and add .complete in order to filter unprocessed photos.
How would you solve this problem?
def self.batch
Photo.unscoped.where(:status=>"batch")
end
This thread is more authoritative: Overriding a Rails default_scope
I give it a shot. Lets say you want to remove a where clause from a default scope (and not just override it with another value) and keep associations you can try this:
class Photo < ActiveRecord::Base
default_scope where(:status => 'complete').where(:deleted_at => '').order('id desc')
def self.without_default_status
# Get the ActiveRecord::Relation with the default_scope applied.
photos = scoped.with_default_scope
# Find the where clause that matches the where clause we want to remove
# from the default scope and delete it.
photos.where_values.delete_if { |query| query.to_sql == "\"photos\".\"status\" = 'complete'" }
photos
end
end
I am using acts_as_taggable to create a tag cloud for my application. I have a model 'Question' which is using the acts_as_taggable plug-in. I basically want to filter the tags for the question model.
I also have a 'Subject' model. So the relation between subjects and questions is that a subject has many questions, and a question belongs to a subject.
So when i call #subject.questions.tag_counts, it works fine. But say I call
#subject.free_questions.tag_counts, where free_questions is a method I've defined, it gives a me an "undefined_method tag_counts for #<Array>. I basically want to find all the tags for a subset of the questions.
Can anybody suggest a workaround?
It may help to implement free_questions as a named_scope so you can call association methods on it.
Something like:
class Question < ActiveRecord::Base
named_scope :free, :conditions => {:free => true} # conditions that make a question 'free'
end
Then you can:
#subject.questions.free
and I suspect this may work as well. (don't have a lot of experience with acts_as_taggable)
#subject.questions.free.tag_counts
When you use a named_scope (instead of a model method you've defined) you get back a proxy object that looks and acts like an Array, but allows you to chain ActiveRecord association methods onto it. Any methods that work on #subject.questions you should be able to call on #subject.questions.free.