Rista Tech Blog

株式会社リスタの技術?ブログ

rubocopのRails/BulkChangeTableがCIでちゃんと動かない問題

1日マスクをしているとなぜか頭痛と肩こりがひどい@mikedaです。

最近rubocopのRails/BulkChangeTableを有効にしたのですが、

  • 開発環境とCI環境(CircleCI)で挙動が違う
  • 有効にしているはずなのにCopが動かない

という問題が発生したので、原因と対応についてまとめておきます。

Rails/BulkChangeTableについて

Railsのmigrationで1つのテーブルについて複数の操作をする時に、このように個別に書くのではなく、

class AddHogeToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :hoge, :string
    add_column :users, :fuga, :integer
  end
end

change_tableにbulk: trueオプションをつけてまとめましょう、というCopです。

class AddHogeToUsers < ActiveRecord::Migration[6.0]
  def change
    change_table :users, bulk: true do |t|
      t.string :hoge
      t.integer :fuga
    end
  end
end

こうすることで個別に実行されていたALTER文が

ALTER TABLE `users` ADD `hoge` varchar(255)
ALTER TABLE `users` ADD `fuga` int

まとめて実行されるようになって処理が高速化されます。

ALTER TABLE `users` ADD `hoge` varchar(255), ADD `fuga` int

守れてないとこういうエラーが表示されます。

% bundle exec rubocop db/migrate/20200529235237_add_hoge_to_users.rb
Inspecting 1 file
C

Offenses:

db/migrate/20200529235237_add_hoge_to_users.rb:3:5: C: Rails/BulkChangeTable: You can use change_table :users, bulk: true to combine alter queries.
    add_column :users, :hoge, :string
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1 file inspected, 1 offense detected

起こった問題

BulkChangeTableは、database.ymlのdevelopmentのadapter設定がmysql2かpostgresqlの時だけ有効になります。
そのため、

  • CI用のdatabase.ymlにdevelopment設定がない
  • database.ymlがERBファイルになっている

といった場合に、Copが適切に有効化されない問題が起こりました。

詳細な挙動はドキュメント、ソースコードを参照ください。

www.rubydoc.info

github.com

CI用のdatabase.ymlにdevelopment設定がない

開発環境とCI環境で別のdatabase.ymlを使っていて、CI環境ではtestの定義しか書かれていない場合、database.ymlにdevelopmentの定義がないためCopが有効化されません。

例)

test:
  adapter: mysql2
  database: test
  username: root
  password: root
  host: 127.0.0.1

database.ymlがERBファイルになっている

環境変数によって挙動を切り替える等の理由でdatabase.ymlをERB化している場合、BulkChangeTableはdatabase.ymlを正常に読み込めないためCopが有効化されません。

例)

development:
  adapter: mysql2
  database: development
  username: root
<% if ENV['LOCAL_DB'] %>
  password:
  socket: /tmp/mysql.sock
<% else %>
  password: root
  host: 127.0.0.1
<% end %>

どうすれば良いのか

上記の問題が起こらないようにdatabase.ymlを調整してもいいですが、.rubocop.ymlでdatabaseを明記してあげるのが簡単かつ確実そうです。

Rails/BulkChangeTable:
  Database: mysql