🙆‍♀️

[Misc #21035] Module#autoload? と Module#const_defined? の挙動に関するチケット

に公開

[Misc #21035] Clarify or redefine Module#autoload? and Module#const_defined?

  • Module#autoload?Module#const_defined? の挙動に関するチケット
  • Module#autoload?Module#const_defined? は一般的には次のような挙動になる
# foo.rb
module M
  class Foo
  end
end
# m.rb
module M
  # Foo の手数を参照すると foo が require される
  autoload :Foo, './foo'

  # Foo は定義はされていないが定義されているように振る舞う
  p constants            # => [:Foo]
  p const_defined?(:Foo) # => true

  # まだ Foo がロードされていない場合はそのパス名を返す
  p autoload?(:Foo)      # => 'foo'

  # 定数を参照すると autoload が発火し foo.rb を読み込む
  Foo

  # ロード後は autoload? の戻り値が変わる
  p constants            # => [:Foo]
  p const_defined?(:Foo) # => true
  p autoload?(:Foo)      # => nil
end
  • これなんですが以下のように autoload で指定したファイルが既にロード済みの場合に const_defined?(:Foo)false になったり autoload?(:Foo)nil を返すケースがある、というのがチケットの内容になる
# main.rb
require "./foo"
# foo.rb
require "./m"
module M
  autoload :Foo, './foo'

  p constants            # => [:Foo]
  p const_defined?(:Foo) # => false
  p autoload?(:Foo)      # => nil
end
$ ruby main.rb
[:Foo]
false
nil
  • 上記のような構成になっているときに main.rb を読み込むと const_defined?autoload? が意図しない値を返すという事象
  • これは foo.rb が既にロード済みなので Fooautoload 中である、とみなしているみたいですね
  • どういうことかというと次のような autoload がある場合
autoload :Foo, './foo'
  • いずれかのタイミングで Foo を参照すると ./foorequire される
  • また autoload 以外で require "./foo" がされる(されている)と『 autoload が破棄される』という挙動になる
require "./foo"

module M
  autoload :Foo, './foo'

  # autoload は破棄されている
  p const_defined?(:Foo) # => false
  p autoload?(:Foo)      # => nil
end
  • これにより最初に上げたような挙動になるみたいですね
  • その後も少し議論しているんですが最終的には起票者がどういう挙動になっているのかは理解したようでチケット自体は閉じられています
GitHubで編集を提案

Discussion