Rails 3 Time.now without the year? - ruby-on-rails-3

I have a Rails 3 application that is a basic intranet for our company. I have an announcement controller which checks for any announcements that have been created with a scheduled date that matches the current date.
class Announcement < ActiveRecord::Base
scope :active, lambda {
where("starts_at <= ? AND ends_at >= ?", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("updated_at > ? OR starts_at > ?", hide_time.utc, hide_time.utc) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
end
However, most announcement will be deleted within a week or so of the scheduled end date. All other announcements are simple Happy Birthday messages to staff members. Due to the way we are using the announcement system, it seems sensible to only check against the day/month and not the year, as birthday messages etc. will be annual.
What would the simplest way to 'ignore' the year in my controller code?
UPDATE
I have updated the controller code to the below code, however, announcements no longer hide after the end datetime.
class Announcement < ActiveRecord::Base
scope :active, lambda {
where("day(starts_at) <= ?
AND month(starts_at) <= ?
AND day(ends_at) >= ?
AND month(ends_at) >= ?",
Time.now.utc.day,
Time.now.utc.month,
Time.now.utc.day,
Time.now.utc.month)
}
scope :since, lambda { |hide_time|
where("day(starts_at) > ?
AND month(starts_at) > ?",
hide_time.utc.day, hide_time.utc.month) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
end
An example record:
22, 'Test Announcement', 'This is a test announcement, please ignore it. Seriously - stop reading.', '2012-06-25 13:40:00', '2012-06-25 13:55:00', '2012-06-25 13:47:23', '2012-06-25 13:52:15');
Even though I have set :exclude_year on the input select boxes for the datetime fields it still puts the current year in.

If you are using MySQL database you could use its day() and month() methods. I don't know if these methods can be found for other databases as well. So doing it this way may make your code become dependent on the database server.
scope :active, lambda {
where("day(starts_at) <= ?
AND month(starts_at) <= ?
AND day(ends_at) >= ?
AND month(ends_at) >= ?",
Time.now.utc.day,
Time.now.utc.month,
Time.now.utc.day,
Time.now.utc.month)
}

Couple things. You should be using AND not OR if I understand you correctly, you want the time to fall between those to items. 2nd thing is you need to specify that it is at the beginning of the day and the end of the day. Right now it's just being set to the now which doesn't really say the expanse of 24 hours very clearly :
class Announcement < ActiveRecord::Base
scope :active, lambda {
where('starts_at < ? AND ends_at > ?', Time.zone.now.end_of_day, Time.zone.now.beginning_of_day)
}
scope :since, lambda { |hide_time|
where('updated_at > ? AND starts_at > ?', Time.zone.now.end_of_day, Time.zone.now.beginning_of_day)
}

Related

Scope to get users older then 18 years old by birthday field (Rails 3.2)

I need to write scope that will help me get users where age will be > 18 years, but I don't have age field in database.
My solutions doesn't works =(
1. scope :adult, -> { where('Time.now.utc.to_date.year - birthday.to_date.year >= 18') }
2. scope :adults, -> { where('birthday >= ?', age) }
def age
Date.today - 18.years
end
try this
scope :adults, -> { where('birthday <= ?', 18.years.from_now) }

Compare Time.now in Rails but ignore year?

I have the following code in my Rails 3 application:
scope :active, lambda {
where("starts_at <= ? AND ends_at >= ?", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("updated_at > ? OR starts_at > ?", hide_time.utc, hide_time.utc) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
However, I want to return the results regardless of whether the year matches the current year or not. So long as the day and month match it's fine. Is this possible?
The starts_at and ends_at columns are datetime formatted. So they will automatically include a year even if I dont set one in the form:
<%= f.datetime_select :starts_at, :order => [:day, :month], :discard_year => true %>
Any pointers would be appreciated.
As Old Pro points out in the comments this does pose a problem with leap years. You likely want to split each dayofyear into MONTH(x) AND DAYOFMONTH(x) instead.
You can use the dayofyear function
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_dayofyear
scope :active, lambda {
where("dayofyear(starts_at) <= dayofyear(?) AND
dayofyear(ends_at) >= dayofyear(?)", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("dayofyear(updated_at) > dayofyear(?) OR
dayofyear(starts_at) > dayofyear(?)", hide_time.utc, hide_time.utc) if hide_time
}
in mssql it's
datepart(dayofyear,date)
in postgres it's
extract(DOY from date)
in sqlite3 it's
strftime("%j",date)

Scope evaluate time in Rails 3

I'm writing a scope that should look for calls with a status of open and any call with a datetime greater than current time. I'm a bit fuzzy on the syntax, can someone help point me in the right direction.
Example that fails:
scope :scheduled_calls, where(:call_status => "open", :transfer_date > Time.now)
You need to use a lambda to evaluate the scope when it's called, as opposed to when the class is loaded.
scope :scheduled_calls, lambda { where(["call_status = ? and transfer_date > ?", "open", Time.now]) }
Try this:
scope :scheduled_calls, where("call_status = 'open' and transfer_date > ?", Time.now)

Rails 3 Custom Validation

I'm attempting to created a custom validation that verifies a schedule's start_date and end_date do not overlap with another schedule
class Schedule < ActiveRecord::Base
#has many scheduleTimes (fk in scheduleTime)
has_many :scheduletimes, :inverse_of => :schedule
validate :dateOverlaps?
scope :active, lambda {
where('? between start_date and end_date', Date.today)
}
def dateOverlaps?
results = ActiveRecord::Base.connection.execute("Select (start_date::DATE, end_date::DATE) OVERLAPS ('#{self.start_date}'::DATE, '#{self.end_date}'::DATE) from schedules;")
errors.add_to_base("Date ranges cannot overlap with another schedule") if results.first["overlaps"] == 't'
end
however, this causes
NoMethodError: undefined method `add_to_base'
I have tried creating a custom validator and using the private validate method to no avail. Could someone shine some light on this for me?
Try replacing this:
errors.add_to_base("Date ranges cannot overlap with another schedule")
with this:
errors.add(:base, "Date ranges cannot overlap with another schedule")
Instead of:
errors.add_to_base
try using:
errors.add

Can I use common ActiveRecord scopes(scope) with module in Rails?

In rails3, I make same scopes in model. for example
class Common < ActiveRecord::Base
scope :recent , order('created_at DESC')
scope :before_at , lambda{|at| where("created_at < ?" , at) }
scope :after_at , lambda{|at| where("created_at > ?" , at) }
end
I want to split common scopes to module in lib. So I try like this one.
module ScopeExtension
module Timestamps
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
scope :recent , lambda{order('created_at DESC')}
scope :before_at , lambda{|at| where("created_at < ?" , at) }
scope :after_at , lambda{|at| where("created_at > ?" , at) }
end
end
and I write this one
class Common < ActiveRecord::Base
include ScopeExtension::Timestamps
end
But Rails show this error.
undefined method `scope' for ScopeExtension::Timestamps::ClassMethods:Module
(I didn't forget auto loading library)
How can I easily reuse common scope feature in active record?
I guess this problem to relate loading sequence. But I don't have any idea to solve.
Please hint me.
I solved this calling the scope on self.included(class):
module Timestamps
def self.included(k)
k.scope :created_yesterday, k.where("created_at" => Date.yesterday.beginning_of_day..Date.yesterday.end_of_day)
k.scope :updated_yesterday, k.where("created_at" => Date.today.beginning_of_day..Date.today.end_of_day)
k.scope :created_today, k.where("created_at" => Date.today.beginning_of_day..Date.today.end_of_day)
k.scope :updated_today, k.where("created_at" => Date.today.beginning_of_day..Date.today.end_of_day)
end
end
In Rails 3 there's no difference between a declared scope and a class method that returns an ActiveRecord::Relation, so it can be more elegant to use a mixin module:
class MyClass < ActiveRecord::Base
extend ScopeExtension::Timestamps
end
module ScopeExtension
module Timestamps
def recent
order('created_at DESC')
end
def before_at(at)
where('created_at < ?' , at)
end
def after_at(at)
where('created_at > ?' , at)
end
end
end
MyClass.after_at(2.days.ago).before_at(1.hour.ago).recent