问题描述
在我的 Rails 应用程序中,字符串变得足够聪明,可以将自己与日期进行比较:
$ rails console
Loading development environment (Rails 5.2.4.2)
2.6.6 :001 > '2020-01-01' < Time.Now
=> true
而在常规 irb 中,这会失败:
2.6.6 :001 > '2020-01-01' < Time.Now
Traceback (most recent call last):
8: from /usr/share/rvm/gems/ruby-2.6.6/bin/ruby_executable_hooks:22:in `<main>'
7: from /usr/share/rvm/gems/ruby-2.6.6/bin/ruby_executable_hooks:22:in `eval'
6: from /usr/share/rvm/rubies/ruby-2.6.6/bin/irb:23:in `<main>'
5: from /usr/share/rvm/rubies/ruby-2.6.6/bin/irb:23:in `load'
4: from /usr/share/rvm/rubies/ruby-2.6.6/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
3: from (irb):6
2: from (irb):6:in `rescue in irb_binding'
1: from (irb):6:in `<'
ArgumentError (comparison of String with Time Failed)
我对如何实现这一点有些担忧,但我不知道去哪里寻找。我尝试了以下方法:
- 看着
config/initializers
- 在整个存储库中搜索
(class|module) str
(正则表达式,不区分大小写) - 在整个存储库中搜索
comparable
(假设它与 the Comparable module 有关。
我怀疑它是由某个宝石带来的,但我不知道如何缩小范围。欢迎任何建议。
解决方法
这是一个有趣的问题,这就是我认为正在发生的事情......
我相信在字符串上调用 <
实际上是调用 <=>
方法。 String#<=>
方法的 c 代码包含此代码段
static VALUE
rb_str_cmp_m(VALUE str1,VALUE str2)
{
int result;
VALUE s = rb_check_string_type(str2);
if (NIL_P(s)) {
return rb_invcmp(str1,str2);
}
result = rb_str_cmp(str1,s);
return INT2FIX(result);
}
现在我不是 c 程序员,但我认为当参数不是字符串时,它会反转比较。所以 some_string < Time.now
变成 Time.now > some_string
。
然后调用 Time#compare_with_coercion
方法(在 active_support/core_ext/time/calculations.rb 中),结果,在字符串参数上调用方法 to_datetime
。
如果您在 activesupport/core_ext/string/conversions 中的 debugger
方法中加入 to_datetime
行,您可以跟踪调用堆栈并查看导致我解释的跟踪。