Range#include?
changed in ruby 1.9 . In ruby 1.8, testing whether a value was included in a range meant testing that it was greater than the start of the range and smaller than the end. Ruby 1.9 will iterate through all values in the range, checking whether any of them are equal to the value being tested.
The justification is that this is needed for some special cases. The example given in the link above is a range of objects wrapping integers, where #succ is defined to add 2 to the wrapped integer and comparison is defined by comparing the wrapped integer. If we write f(x) for the object wrapping x then you would have f(1) <= f(2) and f(2) <= f(3), but f(2) is not in the sequence generated by calling #succ repeatedly on f(1), since that yields only objects wrapping odd numbers.
While more correct in this sense, this is obviously a lot slower. It’s also not immune from a different class of issues. As Xavier Noria pointed out on the rails-core list, it’s easy enough to construct ranges with an infinite amount of elements in them for example. Calling include on them would hang forever on 1.9.
In the real world, my main concern is the speed. In our app we had a validates_inclusion_of on dates with a 100 year date range. On 1.8 this was instant, but on 1.9 calling include? on that range was taking around 130ms (on a 2.66Ghz Core i7).
Luckily the old include?
logic is available as cover?
. As of 3.0.5 Rails will use cover?
rather than include?
for validates_inclusion_of
, until then you could drop something like this in an initializer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|