With ruby 2.7 beginless ranges were introduced. Now you can have:
(..5)
(5..10)
(10..)
With integers, .include?
works as expected:
(..5).include?(6) # false
(..5).include?(5) # true
(..5).include?(2) # true
(..5).include?(-100) # true
The same does not work for date ranges however:
(..Date.tomorrow).include?(Date.today) # RangeError (cannot get the first element of beginless range)
Funnily, it works the other way round:
(Date.yesterday..).include?(Date.today) # true
And finally:
(Date.yesterday..).include?(Date.today - 2.days) # Seems to loop forever.
This is such a weird behaviour. All 3 cases bring a different result and only 1 of them actually works as intended.
I mean, I guess it would be understandable if we had a range that has some kind of "continious" logic to it, that it might be hard to check for inclusion. But relatively easy classes like Date should at least work. Date is almost like an Integer anyways. And even Float can do this, too, so I don't see why Date or DateTime shouldn't.
The usecase I have is that the database might give nil for a 2 dates that I'm querying. These are start and end dates that I want to use in a range, but I can't be sure that one of them might not be nil, which would be fine for my logic, but that would result in a beginningless range, which can't handle .include?
.
I can easily make my usecase work with some manual ugly checks, but that's not the elegant ruby way. Am I missing something here? Or should this be a feature that's just not there yet?
With
Range#include?
, you are actually iterating the range, comparing each element in the range whether it is equal to the tested element. Only with number ranges, this is optimized internally to behave as you apparently expect it to. To quote the docs:Thus, instead of
Range#include?
you likely want to useRange#cover?
here which only checks the boundaries of the range (and which works the same asRange#include?
only with numeric boundaries):With your examples,
Range#cover?
does the right thing: