Rails: default scoping being cached by query cache? - ruby-on-rails

I got a default scoping like this which is dynamic:
default_scope :conditions => ["departure_date >= ?", DateTime.current.beginning_of_day]
When I use this code the first day is ok. Lets say first day is 28-03-2011
But the next day seems like it's still using "departure_date >= 28-03-2011"
Is my default scoping being cached?

The problem is that that code is only being executed once, when your app is loaded, and thus the actual date isn't changing. You need to change it to load lazily:
default_scope lambda { { :conditions => ["departure_date >= ?", DateTime.current.beginning_of_day] } }
This way, Datetime.current.beginning_of_day will be evaluated each time you make a query.

Related

Find all old records returning same amount for greater than and less than (> <)

I'm trying to find (and then delete) all old records in my DB on heroku.
For some reason these two are equal (notice < and >)
Post.find(:all, "updated_at > ?", 30.days.ago).count
Post.find(:all, "updated_at < ?", 30.days.ago).count
Makes me hesitent about using the delete.
What call should I make to ensure I do get only the older records?
The other answers are correct, but for updated ActiveRecord syntax:
Post.where("updated_at < ?", 30.days.ago)
Less than is what you want:
Post.find(:all, :conditions => ["updated_at < ?", 30.days.ago])
If you're unsure, print some of the records to the console using p or awesome_print (ap).
You need to specify your where clause with :conditions, like so:
Post.all(:conditions => ["updated_at < ?", 30.days.ago])
If you want to delete old records, you want to delete records that have an earlier date, ie, less than the given date (30.days.ago)
Instead of deleting the records, why don't you set a state flag to pending_delete and then once you're satisfied you can delete all the records in that state.

How to merge multiple 'having' clause relation in rails 3

Facing problem for merging multiple having clause ... As in my model i had two scopes written
1.)
scope :vacant_list, lambda {|daterange|
where("vacancies.vacancy_date" => daterange , "vacancies.availability" => ["Y" ,"Q"]).joins(:vacancies).group("vacancies.property_id").having("count(vacancies.vacancy_date) >= #{daterange.count}") unless daterange.blank?
}
2.)
scope :amenity_type, lambda {|term|
where("amenities.name" => term).joins(:amenities).group("amenities.property_id").having("count(amenities.name) >= #{term.size}") unless term.blank?
}
I need to do something like this
#model = Model.vacant_list(daterange).amenity_type(term)
But i always get wrong number of arguments (2 for 1)
/home/vivek/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.10/lib/arel/select_manager.rb:100:in `having'
/home/vivek/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.6/lib/active_record/relation/query_methods.rb:180:in `build_arel'
/home/vivek/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.6/lib/active_record/relation/query_methods.rb:149:in `arel'.
If i remove any one of scopes having clause then the above action works perfectly .
Is there any way to like-
#model = Model.vacant_list(daterange) and then remove the active record relation and then apply #model.amenity_type(term).
I tried lots of things but didnt find any solution for this.....
I think you're doing this wrong - it took me quite a while to dig out what the actual intent of the 'having' clauses above was. From what I can tell, it looks like the idea is that you pass in an array of dates or amenities and want to find properties that match all of them.
The underlying issue is that (AFAIK) code like this will NOT do the right thing:
# NOTE: WILL NOT WORK
scope :vacant_on, lambda { |date| where('vacancies.vacancy_date' => date, "vacancies.availability" => ["Y" ,"Q"]).joins(:vacancies) }
scope :vacant_list, lambda {|daterange|
daterange.inject(self) { |rel, date| rel.vacant_on(date) }
}
Unless this has changed in Arel (haven't poked it much) then this fails because you end up with exactly one join to the vacancies table, but multiple where clauses that specify incompatible values. The solution is to do multiple joins and alias them individually. Here's how I used to do it in Rails 2:
named_scope :vacant_on, lambda { |date|
n = self.connection.quote_table_name("vacancies_vacant_on_#{date}")
{ :joins => "INNER JOIN vacancies AS #{n} ON #{n}.property_id = properties.id",
:conditions => ["#{n}.vacancy_date = ? AND #{n}.availability IN (?)", date, ["Y","Q"]] }
}
Explicitly specifying an 'AS' here lets multiple versions of this scope coexist in one query, and you get the results you'd expect.
Here's a rough translation of this to modern Arel syntax:
scope :vacant_on, lambda { |date|
our_vacancies = Vacancy.arel_table.alias
joins(our_vacancies).on(our_vacancies[:property_id].eq(Property.arel_table[:id])).
where(our_vacancies[:vacancy_date].eq(date),
our_vacancies[:availability].in(["Y" ,"Q"]))
}
Haven't tried it, but this post:
http://www.ruby-forum.com/topic/828516
and the documentation seem to imply it would do the right thing...

How can I compose/chain queries in Rails 2?

I have one query that gets the total count of rows with one condition, and a second query that gets the total count of rows with the same condition plus another condition. Ideally, I wouldn't repeat myself in the code and could instead just chain/compose the extra condition onto the first query.
I'm thinking of something like this.
query1 = Table.find(:all, :conditions => "condition1")
query2 = query1.find(:all, :conditions => "condition2")
It'd also be nice to find out what this looks like for the Table.count use case, since that's what I'm actually trying to do at the moment.
I'm guessing that the ActiveRecord::Base has some method that will return the query object as opposed to executing it, but I haven't found that in the docs.
Although Rails 3 makes this significantly easier, you can always do it in Rails 2 with a little hack that emulates it:
# config/initializers/rails2_where_scope.rb
class ActiveRecord::Base
named_scope :where, lambda { |conditions| {
:conditions => conditions
}}
end
This way you can chain together multiple conditions in a manner that's forward-compatible with Rails 3:
query2 = Table.where(condition1).where(condition2).all
Rails 3 uses AREL to do most of the SQL computations so that's why it's much more flexible than Rails 2.

Ruby on Rails: setting future "publishing" dates for blog postings

I'm trying to set blog postings to publish at certain dates in the future. I have in my Posting model:
named_scope :published, :conditions => ["publish_at <= ?", Time.now]
I'm using this in my controller to call the published postings:
#postings = Posting.published
The development server works fine, but I believe the production server needs me to refresh the cache (using "pkill -9 dispatch.fcgi") or I won't see the new postings when it's supposed to publish.
Is there any way to set future times for the postings' publishing dates correctly on the production server? Do I have to refresh the cache every time?
You are correct, because the named scope is evaluated when the class loads.
You should re-write it to be dynamic or (maybe better) use the database's now() function.
Either of these should work:
named_scope :published, lambda { {:conditions => ["publish_at <= ?", Time.now]} }
Note how this uses a lambda to always return the current time in the conditions hash.
named_scope :published, :conditions => "publish_at <= now()"
This is database dependent (the above should work for MySQL) but probably a tiny bit faster.
Check to see if you have any of the following statements in your production environment:
ActionController::Base.cache_store = :memory_store
OR
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
OR
ActionController::Base.cache_store = :mem_cache_store
OR any other setting for ActionController::Base.cache_store

Variable field name in named_scope?

In a Rails model I am trying to acheive a named_scope that filters on a start_date and end_date. This is easy. But I am going to have to do it on lots of different fields on many occasions.
Is this asking for trouble? If so why (SQL injection?) and is there another way to acheive this.
named_scope :between, lambda {|start_date, end_date, field|
{ :conditions => ["#{field} >= ? AND #{field} <= ?", start_date, end_date] }
}
EDIT: Solution Used
Using Eggdrop's line of thinking I went with:
##valid_fields = %w(fields in here)
named_scope :between, lambda{ |start_date, end_date, field_name|
field = (##valid_fields.include?(field_name)) ? (field_name) : raise (ActiveRecord::StatementInvalid)
{ :conditions => ["#{field} >= ? AND #{field} <= ?", start_date, end_date]}
}
Now I can reuse my named_scope for fields I wish to filter on date range without rewriting essentially the same scope over and over and whitelist the field names to avoid any mucking about with my column names and tricky SQL injection should the code ever get exposed to user input in the future.
Maybe you could write a method in your model to validate 'field':
If table x, then 'field' must be a particular existing date field in that table.
In other words you don't allow external input into 'field' directly - the external input has to map to known attributes and defined conditions specified in your validate method.
In general, though, this overall direction would not seem to be recommended.

Resources