Trying to negate a scope - ruby-on-rails-3

i am writing some scopes in rails 3 and got stuck on one. i have this scope.
scope :current, lambda {
joins("join rents on rents.property_id = properties.id").
where("rents.start_date <= ? and rents.end_date >= ?", Date.today, Date.today)
}
I want to be able to write a scope that gets everything BUT current.
so something like= Everything - current.
in console i can do something like Property.all - Property.current and it works. but i cant seem to get the syntax for the scope.

It works in the console (or a controller for that matter) because you are working on arrays there.
I'm also interested in add/sub scopes from scopes.

Related

Rails Active Records using maximum and minimum simultaneously

Below both the query work fines
Event.joins(:visit).where(:page_id => 3).group(:visit_id).minimum('events.time')
Event.joins(:visit).where(:page_id => 3).group(:visit_id).maximum('events.time')
I want to do find the diff maximum('events.time') - minimum('events.time') and group by visit id
For this I am writing below query
Event.joins(:visit).where(:page_id => 3).group(:visit_id).(maximum('events.time') - minimum('events.time'))
I am getting error, (undefined method maximum for main:Object)
can anyone help me out for this active records query
That's because you're trying to call maximum and minimum as though they were helper or instance methods. You have to call them on an instance of an ActiveRecord object. In your case I'm guessing that you have class Event < ActiveRecord::Base at the top of your model file.
Even if you change your code to use the ActiveRecord methods, you still have malformed Ruby code at (:visit_id).(maximum. So if the calculation performs and you get a date your code would look like this:
Event.joins(:visit).where(:page_id => 3).group(:visit_id).(SomeTimeStamp), which will throw a different error.
You'll have to rework your query either way, but the important thing to note is that you can't call minimum or maximum as helper methods.

Rails 3: Excluding Results by Default

On my site, moderators can flag spammy comments. When these comments are flagged, they get quarantined so they no longer appear in regular views, though they can still be seen in the administrative control panel. At the moment, I exclude them from regular views like so:
#comments = Comment.where(:flagged => false)
I do this in every controller that has comments in it, of which there are many. I get the feeling that there's a cleaner way to handle this in Rails. Perhaps somewhere in the comments model I can specify that when querying for comments, only retrieve those that aren't flagged. If so, how is that done? And even if that's not possible, is there some other way to dry this code?
u can use a default scope
default_scope where(:flagged => false)
see http://apidock.com/rails/ActiveRecord/Base/default_scope/class
the default scope can be ignored using unscoped. See http://apidock.com/rails/ActiveRecord/Base/unscoped/class
i would prefer using a scope rather a default scope since i dont have to override it when all the records are needed. Depends upon the frequency of fetching all/unflagged records.
Make a scope (named 'clean' for this example):
class Comment < ActiveRecord
scope :clean, where(:flagged => false)
end
Then use:
#comments = Comment.clean
For future-proofing, you may may want to add a class method called default_view which just calls clean and use that instead. As your 'default' needs change, just modify the default_view method.

How to pass in a block rather than a variable to a method in ruby that wants a variable

Using Rails3, the awesome FactoryGirl gem has a method create_list that takes a strategy, a number of times and then a hash of values to pass into the strategy. (copied from https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md)
twenty_year_olds = FactoryGirl.create_list(:user, 25, date_of_birth: 20.years.ago)
Suppose that instead of passing "20.years.ago", I want to pass a lambda (or Proc) that gets executed once for each time that that the variable gets read. Is this possible in Ruby or FactoryGirl, or would FactoryGirl have to check each param to see if it's a lambda.
dob_dynamic = -> { random_dob some_param }
twenty_year_olds = FactoryGirl.create_list(:user, 25, date_of_birth: dob_dynamic )
When I did something like this, I got:
raise ActiveRecord::AssociationTypeMismatch, message, Object expected, got Proc
I worked around this issue by doing something like this:
# pass i, b/c times always passes index to proc
# dob_dynamic.() invokes dob_dynamic
create_user = -> i { create :user, date_of_birth: dob_dynamic.() }
25.times &create_user
Any better way to do this?
From what I understand of you want, you should be able to create a new factory (probably using factory inheritance or a trait) and put the randomising code in that factory. If you use a block in a factory it will be reevaluated each time, so you can easily use create_list.
I posted the feature request for the create_list to support taking a proc. For the time being the hook after(:create) is great for code like this, as original posted:
# pass i, b/c times always passes index to proc
# dob_dynamic.() invokes dob_dynamic
create_user = -> i { create :user, date_of_birth: dob_dynamic.() }
25.times &create_user

Trying to create a rails model scope query for records since date x, use a lambda?

I think I will want to use combine a model level finder with a lambda.
named_scope :recent_snaps, lambda {|since_when| {:conditions=>{:created_at >= since_when}}}
but I am not sure and also if I have the syntax correct, espcially for the parameter piece and can't run the app right now to check at console.
I do not want to use a find_by_sql or a controller find, I want my finder at the model level for rspec testing.
If you're on Rails 3 (as you presumably are, given the question's tags) you should be using scope rather than named_scope and where rather than conditions. Additionally, you can't use >= in a hash.
Your finished scope should look something like this:
scope :recent_snaps, lambda { |since_when| where("created_at >= ?", since_when) }
To complement Alex's answer: for the looks of the query you're trying, I think you'll like squeel:
scope :recent_snaps, lambda { |since_when| where{created_at >= since_when} }
Just other ways to achieve the same (at least in rails 3.1)
in plain rails
scope :recent_snaps, ->(since_when) { where("created_at >= ?", since_when) }
rails+squeel
scope :recent_snaps, ->(since_when) { where{created_at >= since_when} }

rails scope filtering

Hey guys, I have the following scope:
scope :expires_within, lambda
{|time| where("created_at BETWEEN ? AND ?", 30.days.ago,
time.from_now - 30.days)}
It's not all that important, it works.
This simply gives me all of the objects in my database which were created within a certain time frame. What I want to do is filter this scope such that it removes some of the objects.
The above scope is on a model named Post. I have another model named Lock which "belongs to" a Post, and each Post "has many" Locks. So this means that there is a foreign key on each lock with the id of its corresponding Post.
What I want to accomplish is the following: I want to filter out the posts from the above scope which do not have any locks. So from an abstract/high-level view: I want to get the posts returned from the above scope and remove any which have any associated locks (even if just one).
Is this possible? Would I have to use some form of compound query, using something like except? I'd appreciate any help.
I currently have something that works, but I have a nagging feeling that it isn't very efficient, perhaps it can be done on the database by modifying the above scope and be more efficient:
Post.expires_within(1.day) - Lock.all.collect { |lock| lock.post }
So this basically gets the collection of posts, then it fetches each of the locks' posts and dumps them all into an array which is then subtracted from the original set of posts.
Someone who has already experienced this problem was kind enough to help me out on IRC (Radar), and pointed me to this answer. Now my new scope is the following:
scope :not_locked, lambda { joins("LEFT JOIN locks on
(posts.id = locks.post_id)").where("locks.post_id IS NULL") }
scope :expires_within, lambda {|time| where("posts.created_at BETWEEN ? AND ?",
30.days.ago, time.from_now - 30.days).not_locked }
And it works very well. Hope that helps anyone else out there with the same problem.
With plain ActiveRelation, string-based LEFT JOINs are unavoidable; however, you can greatly simplify the BETWEEN calculations using the Ruby Range class:
scope :expires_within, lambda { |time|
where(:created_at => 30.days.ago..(time.from_now - 30.days)) }
You should be do it with a subquery, something like...
scope :without_locks, :conditions => "not exists(select * from locks where posts.id = locks.post_id)"