Created
October 23, 2024 11:15
-
-
Save sinsoku/ced2addf130dbba063790949a773cd03 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace :rbs do | |
task zeitwerk: :environment do | |
namespace_keys = [] | |
# 他のRakeタスクと続けて実行すると既にクラスを読み込んでいるため、 `__namespace_dirs` が | |
# 空になってしまうので、リロードしておく。 | |
Rails.autoloaders.main.reload | |
# constantize すると `__namespace_dirs` が更新されるので、空になるまで繰り返す。 | |
until Rails.autoloaders.main.__namespace_dirs.empty? | |
namespace_keys += Rails.autoloaders.main.__namespace_dirs.keys | |
namespace_keys.each(&:constantize) | |
end | |
# Zeitwerkに生成された定数の一覧を取得する。 | |
namespaces = namespace_keys.map(&:constantize).select do |klass| | |
filename, _line = Object.const_source_location(klass.to_s) | |
filename.end_with?("lib/zeitwerk/loader/callbacks.rb") | |
end | |
# `Foo::Bar::Buz` を `{ Foo: { Bar: :Buz } }` の構造に変換する。 | |
# 処理を単純にするため、Object を先端に入れている。 | |
structured = { Object => {} } | |
namespaces.each do |namespace| | |
[namespace, *namespace.module_parents].reverse.inject(structured) do |result, klass| | |
result[klass] ||= {} | |
end | |
end | |
# Hashを再起的にRBSに変換する。 | |
to_rbs = ->(modules, indent) { | |
next if modules.empty? | |
modules.map {|klass, body| | |
name = klass.name.split("::").last | |
definitions = [] | |
definitions << if klass.instance_of?(Module) | |
"module #{name}" | |
elsif klass.instance_of?(Class) && klass.superclass | |
"class #{name} < #{klass.superclass}" | |
else | |
"class #{name}" | |
end | |
definitions += [to_rbs[body, indent + 1], "end"] | |
definitions.compact.join("\n").indent(indent) | |
}.join("\n") | |
} | |
# RBSをファイルに書き込む。 | |
content = <<~RBS | |
# Generated from lib/tasks/rbs.rake | |
#{to_rbs[structured[Object], 0]} | |
RBS | |
path = Rails.root.join("sig/generated/zeitwerk.rbs") | |
path.parent.mkdir unless path.parent.exist? | |
path.write(content) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment