何をするのか
タイトルのとおりです。
Ruby3.1 では、Structの子クラス.new(…)
の引数が全てキーワード引数である場合、警告が出るようになるそうです。(ここ)
それは、Ruby3.2 からは、keyword_init: true
していなくても、位置引数に加えてキーワード引数が使えるようになるため、あらかじめ警告しておこうという趣旨のようです。[Feature #16806]
とても有り難いことです。
その絡みで、過去にタイトルと同じような提案([Feature #11925])があったものの、却下されていたことを知りました。
でも、この提案の内容も良さげな感じがします。実装も簡単そうなので、自力で生やしてみようと思いました。
コード
class Struct
class << self
alias _new new
def new(*args, keyword_init: false, &block)
sub = _new(*args, keyword_init: keyword_init, &block)
if keyword_init
sub.instance_eval { alias create new }
else
sub.instance_eval(<<~EOC, __FILE__, __LINE__ + 1)
def create(#{args.map { |arg| "#{arg}: nil" }.join(", ")})
new(#{args.join(", ")})
end
EOC
end
sub
end
end
end
簡単な説明
Original の Struct.new
の中身を、エイリアス _new
に退避させて、new
を自作し、自作の Struct.new(…)
で子クラスを作る際に、instance_eval
を使って特異メソッド create
を作っています。
例えば、
Person = Struct.new(:name, :age)
したとき、併せて
class << Person
def create(name: nil, age: nil)
new(name, age)
end
end
したのと同じ効果があります。
なお、keyword_init: true
のときは new
自身がキーワード引数をとるので、create
は new
のエイリアスにしてあります。
余談
Ruby3.1 からは、
sub = _new(*args, keyword_init: keyword_init, &block)
の部分は、
sub = _new(*args, keyword_init:, &block)
と書けるようです。
大変有り難い。