2022/06/30 今回の気になった bugs.ruby のチケット
今週は Module#autoload
で意図しない定数が定義された時に対応するチケットがありました。
[Feature #18815] instance_{eval,exec} vs Proc#>>
- 次のように
Proc#>>
した結果はinstance_eval / instance_exec
に適さない
measure = proc { p "self=#{self}"; size } multiply = proc { '*' * _1 } # measure のブロック内のレシーバは 'test' になる 'test'.instance_eval(&measure) # "self=test" # => 4 # しかし Proc#>> した場合はその限りではない 'test'.instance_eval(&measure >> multiply) # "self=main" # NameError (undefined local variable or method `size' for main:Object)
- これを動くようにサポートしませんか?という提案
- コメントでは次のようなユースケースが提示されている
FLATTEN = -> { [*_1] } IDENTITY = -> { _1 } DATE = -> { Date.parse(_1) } # パラメータのリストをどのように処理するのかの定義を持つ定数を定義したい TRANSFORMATIONS = { param1: FLATTEN, param2: FLATTEN, param3: IDENTITY, # この時に他の処理を呼び出しつつ、コンテキストに依存するような処理を定義したい # param4: FLATTEN >> -> { allowed?(:something) ? _1 : DEFAULT } # 現状だと以下のように定義する必要がある param4: -> { allowed?(:something) ? FLATTEN.(_1) : DEFAULT } }
[Bug #18813] Let Module#autoload be strict about the autoloaded constant
Module#autoload
は定数が最初に参照された時に自動でrequire
する機能になる
# /tmp/x.rb module M class X end end
# sample.rb module M # M::X を参照した時に自動的に require '/tmp/x' される autoload :X, '/tmp/x' end # このタイミングで require '/tmp/x' される M::X
- また、まだ読み込まれていない場合に
Module.constants
やModule.const_defined?
を使用すると次のような結果が返ってくる
module M autoload :X, '/tmp/x' end # まだ M::X は定義されていないが定義されているかのように振る舞う p M.constants(false) # => [:X] p M.const_defined?(:X, false) # => true
- この時に
/tmp/x
でM::X
が定義されていない場合に意図しない動作になる
# /tmp/x.rb # M の配下ではなくてトップレベルに X が定義される class X end
# sample.rb module M autoload :X, '/tmp/x' end p M.constants(false) # => [:X] p M.const_defined?(:X, false) # => true module M # このタイミングで require '/tmp/x' される # しかし、実際には M::X は定義されない X end # それにより結果が変わる p M.constants(false) # => [] p M.const_defined?(:X, false) # => false
- このように
autoload
するときの構造と実際にrequire
された結果が違う場合はエラーにしたいというのがこのチケットの趣旨になる - 今回の対応では Ruby 3.2 では警告を出す対応になっている
-W
を付けた時のみ警告がでる- 将来的にエラーにするかどうかは互換性の問題があるのでまた決まってないかも?
- https://bugs.ruby-lang.org/issues/18813#note-5
# /tmp/x.rb # M の配下ではなくてトップレベルに X が定義される class X end
# sample.rb # -W を付けた時のみ警告が出る pp $VERBOSE # => true module M autoload :X, '/tmp/x' end module M X # => /tmp/voLY8oW/52:10: warning: Expected /tmp/x to define M::X but it didn't end