🎃

[Misc #21143] const_added と inherited の実行順に関するチケット

に公開

[Misc #21143] Speficy order of execution const_added vs inherited

  • .const_added.inherited の実行順に関するチケット
  • 以下のコードを実行すると .const_added -> .inherited 順で実行されることがわかります
module M
  class C
    def self.inherited(subclass)
      pp ".inherited: #{subclass}"
    end
  end

  def self.const_added(cname)
    pp ".const_added: #{cname}"
  end

  class D < C; end
end
__END__
".const_added: D"
".inherited: M::D"
  • .const_added定数が定義されたときに呼ばれる メソッド
    • 上記の場合だと D が定義されたときに呼ばれる
  • .inherited継承されたときに呼ばれる メソッド
    • 上記の場合だと D < C されたときに呼ばれる
  • なので .inherited が呼び出されたタイミングでは『既に DC を継承している状態』として扱われるようになる
  • このチケットではこの実行順を明文化しよう、というチケット
  • Zeitwerk がこの挙動に依存しているので明文化したい、みたいなモチベーションみたいですね
  • これなんですがコードの書き方によって期待する順番が違ったりするみたいで色々と難しそうですね
  • 例えば以下のように Class.new をした場合は .const_added -> .inherited の順で呼び出されたりします
module M
  class C
    def self.inherited(subclass)
      pp ".inherited: #{subclass}"
    end
  end

  def self.const_added(cname)
    pp ".const_added: #{cname}"
  end

  D = Class.new(C)
end
__END__
".inherited: #<Class:0x000077cfd57f83f8>"
".const_added: D"
  • こういうのを考慮すると inherited -> const_added の順で呼び出すようにする話もでたりしていますね
  • これなんですが最終的には Ruby 3.5 から 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"]
  • Zeitwerk 関連でどれぐらい影響があるんですかねえ
GitHubで編集を提案

Discussion