🐕

[Bug #21193] Object.const_source_location が参照できなくなったというバグ報告

に公開

[Bug #21193] Inherited callback returns nil for Object.const_source_location

  • 開発版の Ruby 3.5-dev で .inherited 時に Object.const_source_location で参照できなくなったというバグ報告
class A
  def self.inherited(other)
    p other # => B
    super
    p Object.const_source_location(other.name)
    # Ruby 3.4 => ["test.rb", 8]
    # Ruby 3.5 => nil
  end
end

class B < A
end
  • .inherited は『自身が継承されたとき』に呼ばれるフックメソッド
    • 上記の場合だと class B < AA を継承したタイミングで呼ばれている
    • .inherited の引数は継承先のクラスが渡される
  • これなんですが [Misc #21143] Speficy order of execution const_added vs inherited によって .const_added.inherited の実行順を変えたのが影響しているみたいですね
  • Ruby 3.4 では .const_added -> .inherited の順で呼ばれていたものを上記のチケットで .inherited -> .const_added の順にされました
$caller = []

module M
  class C
    def self.inherited(subclass)
      $caller << ".inherited: #{subclass}"
    end
  end

  def self.const_added(cname)
    $caller << ".const_added: #{cname}"
  end

  class D < C; end
end

pp $caller
# Ruby 3.4 => [".const_added: D", ".inherited: M::D"]
# Ruby 3.5 => [".inherited: M::D", ".const_added: D"]
  • これにより今回の問題が発生するようになったみたいですね
  • 実際に Shopify's では以下のように .inherited.const_source_location に依存しているコードがあるらしく挙動が壊れてしまったみたい
def self.inherited(config)
  return super unless config.superclass == Config

  # 継承した config の定義元によって何かしらロードするような処理を仕込んでいる
  if (class_name = config.name) &&
    (config_location = Object.const_source_location(class_name)) &&
    (parent_dirname = File.dirname(config_location[0]))
    config_dirname = File.join(parent_dirname, "config")
      config.autoload(:Something, File.join(config_dirname, "something.rb"))
  end

  super
end

参照

GitHubで編集を提案

Discussion