Ruby nil converted to_date - ruby

I am writing a little updater for a app that will update the last time someone logs in to the app and then saves it. its in rails 3.2 and ruby 1.9.3p327
def update_last_seen
if current_account.present?
if (Date.current - 1.day) > current_account.last_login_at
current_account.last_login_at = Date.current
current_account.save
end
end
end
I stuck that into the application controller and call it with a before filter. The only thing is that sometimes i have dates that are nil. so comparing date to nil gives errors. you cant call to_date on a nil.
nil.to_f => 0.0
nil.to_i => 0
nil.to_s => ""
nil.to_date => NoMethodError: undefined method `to_date' for nil:NilClass
"2013/07/26".to_date => Fri, 26 Jul 2013
how can i have it set it to be accepted as a blank date as it were.
i could always do
if current_account.last_login_at.blank? || (Date.current - 1.day) > current_account.last_login_at
that way it will set it if its not there but is there a semantic way of doing it?
UPDATE:
You might think this is has no point. the reason i ask is because there are some engines that have a nil for a date. for example excel will return dates two ways 1. as text as in "06/12/2013" or 2. an integer as the number of days from 01/01/1900. that date is excels nil date i was hoping that there was a default date for nils for Ruby. if there isn't you can just comment nope there isn't sorry man. giving a downvote without explanation as to why means that you really don't care about helping/teaching anything you're just there pushing buttons. if i did something wrong with this question you can tell me ill try fix it, if it doesnt make sence?

You could add in another method to clean up the code a little bit.
def new_login_since?(last_login)
last_login.blank? || (Date.current - 1.day) > last_login
end
def update_last_seen
if current_account.present? && new_login_since?(current_account.last_login_at)
current_account.update_attributes { last_login_at: Date.current }
end
end

To answer the actual question ... you can monkey-patch the NilClass like this
class NilClass
def to_date
Date.today
end
end
nil.to_date # => #<Date: 2013-09-26 ((2456562j,0s,0n),+0s,2299161j)>
Of course, the accepted answer shows the better approach.

Related

In ruby, is there a method you can call on an object to return that object? [duplicate]

I am doing some reflection, and ran into an unexpected road block.
Is there an object method in ruby (or rails) that returns itself
ruby-1.9.2> o = Object.new
=> #<Object:0x00000104750710>
ruby-1.9.2> o.class
=> Object
ruby-1.9.2> o.send :self
NoMethodError: undefined method `self' for #<Object:0x00000104750710>
What I want
ruby-1.9.2> o.send :self
=> #<Object:0x00000104750710>
Is this built in? Or do I need to extend Object (It always gets me nervous opening up Object):
class Object
def itself
self
end
end
And then so:
ruby-1.9.2> o.send :itself
=> #<Object:0x00000104750710>
Ok, some background on what I am trying to achieve. I have a generic table helper in my rails app, and you call if like so:
render_list #person, [{field: :name, link_to: :itself},
{field: {address: :name}, link_to: :address}]
I was struggling on the right way to call :itself -- but i'm thinking that my patch is the way to go.
Yes! If you have Ruby 2.2.0 or later, you can use the Kernel#itself method.
You can see the extensive discussion of this feature here: https://bugs.ruby-lang.org/issues/6373. The patch was submitted by Rafael França in message #53.
You can see it in the official Ruby source by looking in object.c.
There is a discussion about adding such method: http://bugs.ruby-lang.org/issues/6373
If you are using Ruby version >= 1.9 you can use tap method with empty block:
Object.tap{} => Object
Object.new.tap{} => #<Object:0x5f41334>
self is the object itself, no need to extra fetch it. After your patch, try the following:
>> a=[2,3,4] #=> [2, 3, 4]
>> a == a.itself #=> true
>> a.object_id #=> 71056290
>> a.itself.object_id #=> 71056290
...it is exactly the same
self is a keyword referring to the default receiver. It is not a method. See this page for an example.
Your itself method works fine. You can also say:
o.instance_eval('self')
For a class, use class_eval instead:
Object.class_eval('self')
There is a #yourself method in Smalltalk. It has sense because of the syntax of the language where you can send several messages to the same object and want to get the object itself at the end of the phrase.
aList add: (anObjet doThis; andThat; yourself).
Also in Smalltalk the default return value for a method is self, but in Ruby it's the last instruction's return value (or nil if there is nothing in the method).
Anyway maybe we should all start using explicit returns :)
If for some weird logic reason you have to call a method on some object but what you want is really the object itself, then I don't see why you couldn't extend the Object class to do just that.
There's really no reason why it would break your program unless the method exists somewhere else (did or will exist) and did (or will) do something else. Maybe a slight loss in performance?
Try .presence
>> a=[2,3,4]
=> [2, 3, 4]
>> a == a.presence
=> true

Ruby Benign vale for nil DateTime

When comparing DateTimes, all objects being compared must be the same type. However, I have a data set that has nil dates. I want to treat these dates as older (or perhaps newer) than any other date. Is there a way to construct a benign value that will compare as older (or alternatively, newer) than any other date?
example:
data = [
{ name: :foo, timestamp: make_benign(some_valid_timestamp) },
{ name: :bar, timestamp: make_benign(nil)}
]
data.sort_by{|datum| datum[:timestamp]} #=> [<bar>, <foo>]
data.max_by {|datum| datum[:timestamp]} #=> <foo>
data.min_by {|datum| datum[:timestamp]} #=> <bar>
EDIT: I happen to be stuck on ruby 1.9 for this problem, so solutions for older versions of ruby would be nice. (But newer solutions also are nice for future reference)
From the docs, the requirement is not that "all objects are the same type". It says:
The other should be a date object or a numeric value as an astronomical Julian day number.
So for a benign value that is guaranteed to be before/after any date, you could use -Float::INFINITY and Float::INFINITY accordingly.
DateTime.now > Float::INFINITY #=> false
DateTime.now > -Float::INFINITY #=> true
EDIT:
So we need a solution that works in Ruby 1.9 and Rails 3.2.9, huh...
Well the reason the above won't work is because of this monkeypatch in ActiveSupport:
class DateTime
def <=>(other)
super other.to_datetime
end
end
This is particularly problematic. Unfortunately, you may need to just use a "very big/small number" instead...
However, if you're able to upgrade a little bit to Rails 3.2.13 (or apply this updated monkeypatch manually), where the method signature was changed to:
class DateTime
def <=>(other)
super other.kind_of?(Infinity) ? other : other.to_datetime
end
end
...Then you can use Date::Infinity (TIL that's a thing) instead of Float::Infinity, and this "fixed" version of the method now handles it correctly:
DateTime.now > Date::Infinity.new #=> false
DateTime.now > -Date::Infinity.new #=> true

Task for telegram bot

I wrote a telegram bot on ruby with the help of rack. My questions is what can help me do this task. I need bot to send one message when starts_at field of my activerecord object will be equal to now. Basically I need to perform this task all the time so it monitor my database and send this message. I thought about something like delayed_job, don't know how it can help me to achieve my goal.
I need to close the event at a specific time:
class Event < ActiveRecord::Base
def close
if starts_at == Time.now
send_message("Some farewell message")
end
end
end
I need to check all the time if this event is ready to close and send a message after this.
You don't tell us what datatype your starts_at field is, but odds are really good that you'll almost never find an instant where starts_at is equal to Time.now so
if starts_at == Time.now
will usually fail.
Time.now has a granularity well beyond a second. This is from the Time class documentation:
All times may have fraction. Be aware of this fact when comparing times with
each other -- times that are apparently equal when displayed may be different
when compared.
and from the Time#to_f documentation:
t = Time.now
"%10.5f" % t.to_f #=> "1270968744.77658"
t.to_i #=> 1270968744
If you're using a DateTime or Time field it's going to be very hard to match that microsecond. Instead, you need to change your code from using == to <= which will trigger whenever start_time is less than or equal to Time.now, assuming it's a compatible field of course.
Change:
if starts_at == Time.now
to:
if starts_at <= Time.now
and it should work better.
Alternately, you can make your starts_at field be an integer or fixnum and then change the values in it to integers, then compare using Time#to_i which results in just the seconds, not the sub-seconds:
[1] (pry) main: 0> foo = Time.now
2017-01-25 15:21:08 -0700
[2] (pry) main: 0> foo.to_f
1485382868.014835
[3] (pry) main: 0> foo.to_i
1485382868
But you'll still want to use <= instead of ==.
Here's something to mull over:
foo = Time.now
bar = Time.now
foo # => 2017-01-25 15:25:42 -0700
bar # => 2017-01-25 15:25:42 -0700
foo.to_f # => 1485383142.157932
bar.to_f # => 1485383142.1579342
foo.to_i # => 1485383142
bar.to_i # => 1485383142
Even at full-speed, Ruby still returns a different time for foo vs. bar, and in that gap your code would miss firing. to_i gives you a one-second granularity but that's still not enough, hence the need for <=.

Ruby: comparing dates of two Time objects

What is the best way to compare the dates of two Time objects in Ruby?
I have two objects such as:
time_1 = Time.new(2012,12,10,10,10)
time_2 = Time.new(2012,12,11,10,10)
In this example, the date comparison should return false.
Otherwise, same date, but different times, should return true:
time_1 = Time.new(2012,12,10,10,10)
time_2 = Time.new(2012,12,10,11,10)
I have tried to use .to_date that works for DateTime objects, but it is not supported by Time.
Just require the 'date' part of stdlib, then compare the dates:
require "date"
time1.to_date == time2.to_date
Job done.
I have verified that this works for me:
time_1.strftime("%F") == time_2.strftime("%F")
The %F format returns the date portion only.
Maybe just testing this way:
time_1.year == time_2.year && time_1.yday == time_2.yday
It'll be less resource consuming than string comparison.
monkey patch the class Time with this method and it i'll be nice to read
class Time
def date_compare(time)
year == time.year && yday == time_2.yday
end
end
time_1.date_compare time_2
to_date works just fine in ruby 2.0 and ruby 1.9.3 and ruby 1.9.2 http://ruby-doc.org/stdlib-1.9.2/libdoc/date/rdoc/Time.html
>> time_1.to_date
=> #<Date: 2012-12-10 ((2456272j,0s,0n),+0s,2299161j)>
but it's not in the stdlib of ruby 1.8.7 http://ruby-doc.org/stdlib-1.8.7/libdoc/time/rdoc/Time.html - but then, your way of creating a time object doesn't work in that version either:
> time_1 = Time.new(2012,12,10,10,10)
ArgumentError: wrong number of arguments (5 for 0)

Ruby: how to get at meridian in Time objects?

If I've got a time object:
t = Time.now
and I want to know if that time is AM or PM, right now the only way I can figure to do this is:
t.strftime("%p") == "PM"
Now, that %p is getting interpolated from something, right? Is there another way to get to it?
I ask because I'm doing some time formatting where I want to display a time range like:
"9:00 AM - 5:00 PM"
"4:00 - 5:30 PM"
"10:15 - 11:45 AM"
Right now I have to do this checking the string value of strftime, but I'd prefer to write something like:
if start_time.am? && end_time.pm? || start_time.pm? && end_time.am?
...instead of the much more verbose strftime string comparisons I'm doing now.
Based on http://www.ruby-doc.org/core-1.9.3/Time.html, I do not believe there is any other way. You could monkey-patch Time to save you some tedious strftime, however:
class Time
def meridian
self.strftime('%p')
end
def am?
self.meridian == 'AM'
end
def pm?
self.meridian == 'PM'
end
end
There isn't anything as nice as time.am? but you can use time.hour < 12 instead.
class Time
def am?
self.hour.in? (0..12)
end
def pm?
self.hour.in? (13..24)
end
end

Resources