こんにちは、mzpです。最近はBuckleScriptで、OCamlをJavaScriptに変換して遊んでいます。

先日、Misoca開発チームで frozen_string_literal を有効にするようにしたので、そのときの話を紹介したいと思います。

🔥有効にする前に起きたこと

Ruby 3.0からfrozen_string_literalが標準で有効になるという話もあって、一部のコードに # frozen_string_literal: true が登場するようになりました。

次第に、 # frozen_string_literal: true を書いてないと、レビューで指摘が入るようになりました。

🚓 Rubocopの設定変更

機械的にチェックできる項目をレビューで指摘するのは好きではないので、RubocopのStyle/FrozenStringLiteralCommenteを有効にし、自動でチェック・修正できるようにしました。

その際、既存のコードにはすべて ruboocp:disable をいれて、このルールに検知されないようにしました。

%w( app lib spec config ) .each do | name | Dir [ "#{ name } /**/*.rb " ].each do | path | content = File .read(path, encoding : ' utf-8 ' ) if content !~ /\A # frozen_string_literal: true / File .write(path, " # rubocop:disable Style/FrozenStringLiteralComment

" + content) end end end

🔪Ripperによる自動書き換え

大半のファイルに rubocop:disable が書いてあるのは微妙かなと思ったので、安全に書き換えれるファイルでは frozen_string_literal を有効にするようにしました。

具体的にはRipperで全ファイルを走査して、文字列リテラルを使っていないファイルでは frozen_string_literal を有効にするようにしました。

require ' ripper ' class FindStringLiteral < Ripper :: Filter def on_tstring_beg (_, data) data = true end def on_tstring_content (_, data) data = true end def heredoc_beg (_, data) data = true end end def string_literal? (path) content = File .read(path) FindStringLiteral .new(content).parse( false ) end %w( app lib spec config ) .each do | name | Dir [ "#{ name } /**/*.rb " ].each do | path | unless string_literal?(path) content = File .read(path, encoding : ' utf-8 ' ) if content =~ %r{\A # rubocop:disable Style/FrozenStringLiteralComment } File .write(path, content.gsub( /\A.*/ , " # frozen_string_literal: true " )) end end end end

🚀今後の予定

残ったファイルは自動では修正できないので、別の修正するたびにちょっとづつ書き直しています。ボーイスカウトルールです。

🔉宣伝

Misocaではコードを綺麗にしていくエンジニアを募集しています。