Railsのジェネレータにおけるfrozen string literalマジックコメント

Railsbin/rails g migration create_articles といったようにマイグレーションファイルをジェネレータで作る機会が多いと思う。

% bin/rails g migration create_articles title:string
      invoke  active_record
      create    db/migrate/20171003150532_create_articles.rb
% cat db/migrate/20171003150532_create_articles.rb
class CreateArticles < ActiveRecord::Migration[5.1]
  def change
    create_table :articles do |t|
      t.string :title
    end
  end
end

先日 Rails 本体は frozen string literal の対応がされたが、ジェネレーターで作られる Rails アプリケーションコードには frozen string literal マジックコメントは入らないため、各自入れることになる (マイグレーションに限らずジェネレータ全般の話) 。

根拠としては以下の PR でジェネレータのテンプレートにそのマジックコメントを入れる対応を送ったが、コアチームによって Close されている。詳しくはリンク先。

github.com

なので現実線として、upstream のジェネレータに頼ることなく frozen string literal マジックコメントを Rails アプリケーションコードに入れるには、エディタに設定するなり、RuboCop の Style/FrozenStringLiteralComment cop で autocorrect するなりの手段を講じることになる。

あとはコアチームを説得できる材料を持って Issue を立てるなども考えられるので、そちらの線で本気で対応しようと考える場合は考慮してみてもよいかもしれない (個人的に難易度高い気がしていますが) 。

RubyKaigi 2017 のときに、Rails コントリビューター勢での雑談でこのあたり少し話題に挙ったのを思い出したので、話題に挙った内容で記していない点や異なる点はあるが記しておいた。

Bundler / RuboCop の今後の Ruby のサポートバージョン

自分が見回っているリポジトリの中で、世の中でよく使われているであろう Bundler と RuboCop の今後の Ruby のサポートバージョンについて書いておく。

Bundler

bundler/bundler の master ブランチは Bundler 2 になっていて、Ruby 2.3 以上のみがサポートとなった。

https://github.com/bundler/bundler/blob/0747989a9bf47fc0404248d20628ab64ce09d3be/.travis.yml#L28-L30

PR としては1ヶ月経たないくらい前にマージされたこちら。

github.com

Bundler 1 系は Ruby 1.8 系からサポートしていて、Bundler の実装としても Ruby 1.8 で動く必要があったのと、Travis CI のマトリックスが多く CI にとても時間が掛かっていたという開発視点でだいぶ良い判断だと思う。個人的に見ている中で rails/rails (というかそのほとんどは railties) より長い CI のリポジトリは bundler/bundler だけだったので、その点でも良くなりそう。

RuboCop

RuboCop も Ruby 2.0 のサポートが切られた。

github.com

このまま Ruby 2.1 のサポートも切る流れになると思うので、EOL の Ruby に気を払わなくて良くなるのはこちらも良いこと。

その他

Rails に関しては表立った情報がないけれど、Rails 5 で Ruby のサポートバージョンを変えたように、今後の Rails 6 でどうして行くのか関心のあるところ。

Ruby AST Visualizer 0.4.0 をリリースした

RuboCop で cop を作るときに、Parser gem の S 式を知るために使っている自作 gem となる。

github.com

もともとプレゼンテーション用に AST の画像を作るために作ったものだったが、すっかり cop 作成時の S 式のテキスト表現を得るためのものになったため、画像出力が不要な場面が多くなった。

ということで以下のような -n (--no-image) オプションを追加した。

% ruby_ast_visualizer -n -e 'puts("hello")'

(send nil :puts
  (str "hello"))

これで不要な a.png ファイルが作成されなくなった。デフォルト --no-image も考えたが、もともとの gem の方針から外れるのでそれは控えた。

RuboCopのdef_node_matcherにおけるグローバル変数の表現

今日作っていた cop で調べたログ。

github.com

グローバル変数 $stderr を Parser gem による S 式で表現すると以下のようになる。S 式の比較のため、引数が複数あるパターンと引数のないパターンを挙げておく。ちなみに ruby_ast_visualizer は自作の gem で Parser gem の出力表示を得ることができるラッパーコマンドとして使っている。

% ruby_ast_visualizer -e "$stderr.puts('foo', 'bar')"

(send
  (gvar :$stderr) :puts
  (str "foo")
  (str "bar"))
% ruby_ast_visualizer -e "$stderr.puts"

(send
  (gvar :$stderr) :puts)

このグローバル変数 $stderr を RuboCop の def_node_matcher に渡す S 式で表現する場合、$ という記号はマッチした値をキャプチャする意味で使われているためそのまま使えない。代わりに # でメソッド呼び出しにする。

def_node_matcher :stderr_puts?, <<-PATTERN
  (send
    (gvar #stderr_gvar?) :puts
    ...)
PATTERN

対応するメソッドの方でマッチするか判定させる。

def stderr_gvar?(sym)
  sym == :$stderr
end

他の cop 実装がサンプルコードになっているので、このあたり gvargrep して他に類似のケースでどうしているかを調べてみていた。

追記 pocke さんから Twitter でコメントを頂く。

Bundler 2 での bundle コマンド非互換の影響

以前、ぎんざRuby会議01 での Bundler 2 のプレゼンで取り挙げたように bundle コマンドの挙動が Bundler 2 で変わる予定。

現在の Bundler 1.15 系と異なり、Bundler 2 での bundle コマンドは以下のようにコマンドの一覧表示になる。

% bundle
Bundler version 2.0.0.dev (2017-09-29 commit 5e9c40aa1)

Bundler commands:

  Primary commands:
    bundle install [OPTIONS]    # Install the current environment to the system
    bundle update [OPTIONS]     # Update the current environment
    bundle cache [OPTIONS]      # Locks and then caches all of the gems into vendor/cache
    bundle exec [OPTIONS]       # Run the command in context of the bundle
    bundle config NAME [VALUE]  # Retrieve or set a configuration value
    bundle help [COMMAND]       # Describe available commands or one specific command

(snip)

この変更がどういうことかというと、bundle gem で作った gem の README.md テンプレートに記されたインストール手順をそのまま README に使っている gem が問題になる (慣れた人であれば、インストールドキュメントを見ながら bundle コマンドを打つ人は少ないと思うが) 。端的にいうと bundle コマンドではインストールできなくなるので、bundle install に書き直す必要がある。bundle install であれば1系、2系両方に対応するので、それが正解だと思っている。

Bundler 2 は未だリリースされていないため、もう少し様子見をした方が良いかなと思っているけれど、この変更が実現したら OSS Gate ワークショップあたりで README どおりの手順で行なったけれどインストールできないといった PR が多数出るのかなとも予測していたりする。

bundle gem を利用する gem 職人向けには Bundler にパッチを出しておいたので、このパッチ以降は問題がそのはなくなるけれど、現在世にある gem でも相当数のインストール手順が bundle になっているものがあるので、振る舞いとしては良くなったと思う一方で、既存ドキュメントへの非互換として知っておいて良いと思う。

github.com

Meguro.rb#7

RuboCop 0.50.1 がリリースされそうなこの頃、onk に onkcop の RuboCop 0.50.0 対応の進捗どうですか?の確認も兼ねて行ってきた。会場はラクスルさん。空中庭園のようなとても良い会場でした。

megurorb.connpass.com

乾杯からはじまって5つのトークを聞いた後に、自己紹介タイム、歓談という流れ。それぞれのトークについての話をしたり、Okuribito の発表の話から Ease of Development 繋がりで、Ruby 3 に導入されると言われている型システムの話をしたりと、多岐に渡った話ができて楽しかったです。

あとビールとピザご馳走さまでした。