如何在 Rails 中找到对象补丁的来源?

问题描述

在我的 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 行,您可以跟踪调用堆栈并查看导致我解释的跟踪。