SideCI TechBlog

SideCIを作っているアクトキャットのエンジニアによる技術ブログです。


RuboCop 0.43.0 の CHANGELOG を読む

こんにちは、RuboCop大好き!Pockeです。

先日、RuboCopのバージョン0.43.0がリリースされました。

Release RuboCop 0.43 · bbatsov/rubocop

このリリースには、筆者を始めとするSideCIのメンバーによるPull Requestも11個含まれています。 今日はそのCHANGELOGから、気になる新機能を見ていきましょう。

新規Cop追加

Copとは、RuboCopにおいてひとつのルールを指す言葉です。例えば、「インデントが正しいかチェックする」「非推奨メソッドを使っていないかチェックする」などが1つのCopの単位になります。 この章では、0.43.0で新たに追加されたCopをひとつずつ紹介します。

Style/DocumentationMethod

このCopはPublicメソッドに対してドキュメントコメントを書くことを促します。

解説

class MyClass
  # Method1 does something.
  def method1
    # ...
  end

  def method2
    # ...
  end


  private

  def method3
    # ...
  end
end

例えば上記のようなClass定義をこのCopで検査した場合、def method2の部分に下記のような指摘が行われます。

test.rb:7:3: C: Missing method documentation comment.
  def method2 ...
  ^^^^^^^^^^^

def method1ではドキュメントコメントが書かれているため、またdef method3ではプライベートメソッドを定義しているため、指摘は発生しません。

まとめ

長く保守していくコードでは、ドキュメントコメントの有無がメンテナンスを行う上で重要となります。
コーディング規約としてコメントを書くことを強制したい場合、このCopを有効活用することが出来るでしょう。

なお、コメントを書く必要が薄いメソッドも多いためか、このCopはデフォルトでは無効になっています。 そのため、このCopを使用したい場合は.rubocop.ymlなどで明示的に有効にする必要があります。

Style/SafeNavigation

このCopはSafe navigation operatorが使用できる場合、それを使用することを促します。

Safe navigation operatorは、Ruby 2.3で新たに追加された演算子です。 foo&.barの様に使用します。これはfoo.nil? ? nil : foo.barのように動作します。

参考: サンプルコードでわかる!Ruby 2.3の主な新機能 - Qiita

解説

if foo
  foo.bar
end

foo.bar if foo

foo && foo.bar

上記のような文は、全てfoo&.barで代替することが可能です。 そのため、上記コードに対して検査を行うと、以下のような指摘が行われます。

test.rb:1:1: C: Use safe navigation (&.) instead of checking if an object exists before calling the method.
if foo ...
^^^^^^
test.rb:5:1: C: Use safe navigation (&.) instead of checking if an object exists before calling the method.
foo.bar if foo
^^^^^^^^^^^^^^
test.rb:7:1: C: Use safe navigation (&.) instead of checking if an object exists before calling the method.
foo && foo.bar
^^^^^^^^^^^^^^

まとめ

Safe navigation operator を使用することでコードをシンプルに保つことが可能です。
導入してみてはいかがでしょうか。

なおSafe navigation operatorはRuby2.3から追加された演算子であるため、このCopを動作させるには.rubocop.ymlで明示的にRubyのバージョンを指定する必要があります。
以下のコードを.rubocop.ymlに追記して下さい。

AllCops:
  TargetRubyVersion: 2.3

Rails/SafeNavigation

このCopもStyle/SafeNavigationと同様、Safe navigation operatorが使用できる場合にそれを使用することを促します。 Style/SafeNavigationと異なる点は、ActiveSupportで追加されるtry!メソッドを対象にすることです。

解説

foo.try!(:bar)

例えば、上記のコードはfoo&.barと同等です。そのため、上記のコードを検査すると以下の指摘が行われます。

test.rb:1:1: C: Use safe navigation (&.) instead of try!.
foo.try!(:bar)
^^^^^^^^^^^^^^

まとめ

ActiveSupportが提供する拡張に依存しないコードを書くことが出来るため、積極的にSafe navigation operatorを使うように矯正できるこのCopは有用でしょう。

なお、このCopを有効にするには以下のコードを.rubocop.ymlに追記する必要があります。

AllCops:
  TargetRubyVersion: 2.3

Rails:
  Enabled: true

Rails/NotNullColumn

Railsアプリケーションのマイグレーションファイル内で、NOT NULL制約がついておりデフォルト値がついていないカラムの追加を検出します。

解説

class AddNotNullColumn < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :name, :string, null: false
  end
end

上記のようなマイグレーションを実行すると、(使用しているRDBMSにもよりますが)多くの場合エラーが発生してマイグレーションが失敗してしまいます。
またMySQLを使用している場合にはエラーは出ないものの、既存のレコードのnameカラムに対してはMySQLが勝手にデフォルト値を設定します。

そのため、このCopは上記のコードに対して下記の指摘を行います。

db/migrate/test.rb:3:40: C: Do not add a NOT NULL column without a default value
    add_column :users, :name, :string, null: false
                                       ^^^^^^^^^^^

この指摘を回避するには、null: false, default: ''のようにデフォルト値を指定する必要があります。

まとめ

開発時と運用時で異なるRDBMSを使用していたり複数のRDBMSのサポートを行っている場合、このCopは問題の早期発見に役立つでしょう。

なお、このCopを有効にするには以下のコードを.rubocop.ymlに追記する必要があります。

Rails:
  Enabled: true

Style/VariableNumber

このCopは、変数名に数字を含める際のスタイルを統一します。

解説

変数名に数字を含める場合、以下のような3通りのケースが考えられます。

# 変数名にそのまま数字を続ける
var1 = 1

# 変数名と数字の間にアンダースコアを入れる
var_1 = 1

# 数字をそもそも使わない
var_one = 1

このCopを使用することで、この3つのスタイルのうちどれかに統一することが可能です。 それぞれ.rubocop.ymlに以下のコードを追記します。

# var1 に統一する場合
Style/VariableNumber:
  EnforcedStyle: normalcase

# var_1 に統一する場合
Style/VariableNumber:
  EnforcedStyle: snake_case

# var_one の様に数字を含ませないよう統一する場合
Style/VariableNumber:
  EnforcedStyle: non_integer

まとめ

プロジェクトで変数の命名規則が決まっている場合、このCopを有効活用することが出来るでしょう。

特に命名規則が決まっていない場合、以下のコードを.rubocop.ymlに追記することでこのCopを無効にすることが可能です。

Style/VariableNumber:
  Enabled: false

Performance/SortWithBlock

このCopは、パフォーマンス上の観点から不要なsortメソッドの呼び出しをsort_byメソッドに置き換えます。

解説

Rubyには、ソートを行う為のメソッドがsortsort_byの2つ定義されています。

配列の中身をStringに変換した上で辞書式順序でソートしたい場合、sortメソッドでは以下のように書きます。

array.sort{|a, b| a.to_s <=> b.to_s}

しかし、この書き方では後述するsort_byメソッドより低速になってしまいます。 sortメソッドは比較を行う度にブロックを呼び出すため、比較の回数だけto_sが呼ばれてしまいます。

sort_byを使用すると、この点を解消することが可能です。

array.sort_by(&:to_s)

sort_byでは全ての要素に1度ずつしかto_sを呼び出さないため、このケースではsortを使用するよりも高速にソートを行うことが可能となります。

このCopを使用して最初に例にあげたコードを検査すると、以下のような指摘を行います。

test.rb:1:7: C: Use sort_by(&:to_s) instead of sort { |a, b| a.to_s <=> b.to_s }.
array.sort{|a, b| a.to_s <=> b.to_s}
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

まとめ

このCopを使用することで、パフォーマンス上の問題となりうる点を自動的に検出することが可能です。

また、このCopはAuto Correct機能に対応しているため、-aオプションを付与してRuboCopを実行することで対象のコードを自動で修正することが可能です。

Lint/UnifiedInteger

このCopは、Fixnum, Bignumクラスを参照している箇所を検出します。

解説

Ruby 2.4 では、Fixnum, BignumクラスはIntegerクラスに統合される予定です。

参考: サンプルコードでわかる!Ruby 2.4の新機能と変更点 - Qiita

Ruby 2.4からはFixnum, Bignumクラスは両者ともIntegerクラスへの参照となります。 そのため、Fixnumがコード中に書かれている場合は見た目と挙動が異なり、わかりづらいコードとなってしまいます。

このCopはそのような状態を防止するため、Fixnum,Bignumクラスの使用を指摘します。 例えば、以下のコードに対して以下の指摘を行います。

a.is_a? Fixnum
a.is_a? Bignum
a.is_a? Integer
test.rb:1:9: W: Use Integer instead of Fixnum.
a.is_a? Fixnum
        ^^^^^^
test.rb:2:9: W: Use Integer instead of Bignum.
a.is_a? Bignum
        ^^^^^^

まとめ

このCopを使用することで、上記のような紛らわしいコードを検出することが可能です。

また、このCopはRuby 2.4未満を使用している場合でも有効となっています。 Ruby2.4への移行を考えた際にこのCopが有効に働くこと、そもそもFixnumBignumを使用するケースは少なくIntegerの間違いである可能性が高いこと、が理由です。

その他

この章では、新規Cop追加以外の新機能を見ていきます。

HTMLフォーマッタの出力に目次がつくようになりました

rubocop --format html > index.htmlの様にRuboCopを実行すると、RuboCopの結果をHTMLとして出力することが可能です。 今回のリリースでは、この出力に目次がつくようになりました。

f:id:sideci:20160920132901p:plain

筆者はあまりHTMLフォーマッタを使用しないのですが、このフォーマッタをよく使用するユーザーにとっては便利な機能かもしれません。

まとめ

この記事は以上になりますが、RuboCop 0.43.0ではこの他にも多くの機能追加、バグ修正が行われています。 より詳しい変更を知りたい方は、リリースノートをご覧ください。

Release RuboCop 0.43 · bbatsov/rubocop

また、SideCIでは今すぐRuboCop 0.43.0を利用することが可能です。是非お試し下さい!