How to check if last_updated more than 15.minutes.ago? - ruby-on-rails

This should be simple, but I am getting edge cases that seem to be failing, I am doing something wrong and it kinda confuses me. I have a method like this:
def self.needs_updating?(last_updated_time, time_since_update)
return false if last_updated_time.nil?
time_since_update < last_updated_time
end
It's called using this syntax:
Tools::Utils.needs_updating?(#entry.updated_at, 15.minutes.ago)
What I am trying to do is discover whether #entry has been updated at least 15.minutes.ago. If it has been updated 20.minutes.ago this should return true, if it has been updated 1.minute.ago, it should return false.
I think i may need to insert DateTime.now.utc in there maybe. But how can i do it so that I can keep using the Rails idiom (14.minutes.ago, 1.hour.ago etc..) ?
Thanks!

It should be
def self.needs_updating?(last_updated_time, time_since_update)
return false if last_updated_time.nil?
last_updated_time < time_since_update
end
If you want time in UTC use last_updated_time.utc < time_since_update.utc
If you want time in your local time use last_updated_time.localtime < time_since_update.localtime
example
last_updated_time = #entry.updated_at
=> 2022-05-11 22:30:11 +0200
time_since_update = 15.minutes.ago
=> 2022-05-12 22:35:15 +0200
last_updated_time < time_since_update
=> true

Related

Count Records within 30 days of today

I Have a model an Opportunity model that has an attribute called date_of_opportunity. I am trying to write a method that counts how many opportunities are within 30 days of today. However when I try to call my method in the console, I get the error 'undefined local variable or method'
Here is my model:
class Opportunity < ActiveRecord::Base
def calculate_num_days
num_days = 0
#opportunity = Opportunity.all
#opportunity.each do |opportunity|
if (opportunity.date_of_opportunity - Date.today < 30)
num_days = num_days + 1
return num_days
end
end
end
end
Can someone help me figure out whats wrong? Thanks!!
If you will get counts how many opportunities are within 30 days of today, you can try this :
class Opportunity < ActiveRecord::Base
def self.calculate_num_days(from = (Date.today-1.month).beginning_of_day,to = Date.today.end_of_day)
where(date_of_opportunity: from..to).count
end
end
And on your console you can type like this
Opportunity.calculate_num_days
Output looks like :
irb(main):001:0> Opportunity.calculate_num_days
←[0m←[1m←[35m (51.0ms)←[0m SELECT COUNT(*) FROM "opportunities" WHERE ("opportunities"."date_of_opportunity" BETWEEN '2014-05-04 00:00:00.000000' AND '2014-06-04 23:59:59.999999')
=> 2
You seem to want a class method, but are defining an instance method. Do this:
def self.calculate_num_days
...
end
Maybe, #opportunity = Opportunity.all should be #opportunities = Opportunity.all
Unless I am missing what you are trying to do I would let ActiveRecord do the heavy lifting. Opportunity.where("date_of_opportunity - :today < 30", today: Date.today).size
Disclaimer, this is completely untested

ActiveRecord where method datetime passed

Thanks for your continuing support in my latest venture in a Rails app. It's been a while since I've made something in Rails so here is my latest question. I appreciate the help you've all given in the past.
I have a Model called Event.rb which contains a date:date and time:time fields for the Event.
I also have a method inside of the model which is..
def begins
DateTime.new(date.year, date.month, date.day, time.hour, time.min, time.sec)
end
As I can't see if something has truly passed because I only have Date and Time separate so I need them together.
My question is...
I want to be able to add in the DateTime :begins into the following other method in my Model...
def self.past
where("date <= ?", TIME_NOW)
end
Just like I have a method which is...
def upcoming?
self.date >= Time.now
end
Which I could easily change self.date to begins and would past I would imagine?
Thanks!
Perhaps something like this will work for querying the database for past events using your existing date and time columns:
scope :past, lambda {
where("date <= ? and time <= ?",
Time.now.strftime("%Y-%d-%m"),
Time.now.strftime("%H:%M:%S")
)
}
past_events = Event.past
For checking the current instance, you could continue to use your begins method:
def past?
begins < DateTime.now
end
#event = Event.first
#event.past?

updating a model attribute that will only change once

I have a subject model with attributes including a start_date and end_date - as well as a completed boolean attribute.
In subject.rb, I have a method to find how many weeks are remaining for a given subject:
def weeks_left
time = (self.end_date.to_time - Date.today.to_time).round/1.week
if time < 0
"completed"
elsif time < 1
"less than 1 week"
elsif time == 1
"1 week"
else
"#{time} weeks"
end
end
I want to tick the completed attribute if self.weeks_left == "completed" and the best way to do that seems like a call back, but I'm a bit unsure about using after_find - in general it seems like it would be too many queries, and indeed too big of a pain (especially after reading this) - but in this case, once a subject is complete, it's not going to change, so it seems useless to check it's status more than once - what's the best way to handle this?
Why dont you make a scope for this?
scope :completed, ->{where("end_date <= ?", Time.now)}
and a method
def completed?
self.weeks_left == "completed"
end
Looks like you need ActiveRecord::Callbacks. You can see more information here or on rails guide
before_save :update_completed
def update_completed
if (end_date_changed?)
time = (self.end_date.to_time - Date.today.to_time).round/1.week
self.complete = time < 0
end
end
This way you update the complete flag whenever end_date changes and it would always be in sync.
However because this is a calculated value you could also not store it as an attribute and simply define a method to get it
def complete
time = (self.end_date.to_time - Date.today.to_time).round/1.week
return time < 0
end

How to format values before saving to database in rails 3

I have a User model with Profit field. Profit field is a DECIMAL (11,0) type. I have a masked input on the form which allows user to input something like $1,000. I want to format that value and remove everything except numbers from it so i will have 1000 saved. Here is what i have so far:
class User < ActiveRecord::Base
before_save :format_values
private
def format_values
self.profit.to_s.delete!('^0-9') unless self.profit.nil?
end
end
But it keeps saving 0 in database. Looks like it is converting it to decimal before my formatting function.
Try this:
def profit=(new_profit)
self[:profit] = new_profit.gsub(/[^0-9]/, '')
end
First of all, this:
def format_values
self.profit.to_s.delete!('^0-9') unless self.profit.nil?
end
is pretty much the same as this:
def format_values
return if(self.profit.nil?)
p = self.profit
s = p.to_s
s.delete!('^0-9')
end
So there's no reason to expect your format_values method to have any effect whatsoever on self.profit.
You could of course change format_values to assign the processed string to self.profit but that won't help because your cleansing logic is in the wrong place and it will be executed after '$1,000' has been turned into a zero.
When you assign a value to a property, ActiveRecord will apply some type conversions along the way. What happens when you try to convert '$1,000' to a number? You get zero of course. You can watch this happening if you play around in the console:
> a = M.find(id)
> puts a.some_number
11
> a.some_number = 'pancakes'
=> "pancakes"
> puts a.some_number
0
> a.some_number = '$1,000'
=> "1,000"
> puts a.some_number
0
> a.some_number = '1000'
=> "1000"
> puts a.some_number
1000
So, your data cleanup has to take place before the data goes into the model instance because as soon as AR gets its hands on the value, your '$1,000' will become 0 and all is lost. I'd put the logic in the controller, the controller's job is to mediate between the outside world and the models and data formatting and mangling certainly counts as mediation. So you could have something like this in your controller:
def some_controller
fix_numbers_in(:profit)
# assign from params as usual...
end
private
def fix_numbers_in(*which)
which.select { |p| params.has_key?(p) }.each do |p|
params[p] = params[p].gsub(/\D/, '') # Or whatever works for you
end
end
Then everything would be clean before ActiveRecord gets its grubby little hands on your data and makes a mess of things.
You could do similar things by overriding the profit= method in your model but that's really not the model's job.
class User < ActiveRecord::Base
before_save :format_values
private
def format_values
self.profit = profit.to_s.gsub(/\D/,'') if profit
end
end
def format_values
self.profit.to_d!
end
I recommend you to write custom setter for this particular instance variable #profit:
class User
attr_accessor :profit
def profit= value
#profit = value.gsub(/\D/,'')
end
end
u = User.new
u.profit = "$1,000"
p u.profit # => "1000"
I would suggest using the rails helper of number with precision. Below is some code.
Generic Example:
number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
Rails code Example:
def profit=(new_profit)
number_with_precision(self[:profit], :precision => 1, :significant => true)
end

Rails doesn't allow me to set 'date' field to more than 30 days in advance - Help - Rails 3.0

On my User model, I have an attribute trial_end_date.
The column in the table looks like this:
# trial_end_date :date
However, if I try to change the date to far in the future, at the Rails console, this is what happens:
a = User.find(2)
a.trial_end_date = "2019-12-30"
=> "2019-12-30"
>> a.save
=> true
>> a.trial_end_date
=> Sat, 19 Nov 2011
WTF? Why does it do that? I have no idea why it does this?
Even if I try update_attributes(:trial_end_date => "2019-12-30") the same thing happens.
Here are all the methods in my User model that relate to trial_end_date:
after_validation :set_trial_end
def has_trial_expired?
if (self.trial_end_date <= Date.today)
return true
else
return false
end
end
def set_trial_end
plan = self.plan
end_of_trial = Date.today + self.plan.trial_duration.days
self.trial_end_date = end_of_trial.to_date
end
def trial_will_almost_end?
if (self.trial_end_date - Date.today <= 3)
return true
else
return false
end
end
def when_does_trial_end?
self.trial_end_date
end
marcamillion,
You commented that you thought that you thought that validation would happen "just on the initial user creation." As comments have pointed out, that's not true if you use after_validation, but it IS true if you use
before_validation_on_create
(See, for example, http://ar.rubyonrails.org/classes/ActiveRecord/Callbacks.html )
Using that would restrict the creation of dates by your users, but wouldn't prevent you (or them! Be careful!) from changing them later in other ways.
After validation the trial_end_date gets set based on the plan duration, no?
One refinement to Bob's answer: *_on_create and its ilk are deprecated in 3.0 and removed in 3.1. In the interest of maintainability, you probably want to adopt the new form:
before_validation :some_method, :on => :create
It's a quick tweak that'll save you headaches in the future.

Resources