LoginSignup
9
5

More than 5 years have passed since last update.

RuboCop 0.47.0 のCHANGELOGを読む

Last updated at Posted at 2017-01-16

追記 2017/01/20

1/18 に、0.47.0で発生したバグ修正を含む 0.47.1 がリリースされました。

0.47.0 を使わず、こちらのバージョンを使用したほうが良いでしょう。

追記ここまで


本日RuboCopのバージョン(0.47.0)がリリースされました。

CHANGELOGから新機能を見ていこうと思います。

新規Cop追加

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

この章では、0.47.0で新たに追加されたCopをひとつずつ紹介します。

Lint/MultipleCompare

10 < x < 20

上のような式はPythonや数学では正しい式ですが、Rubyではそれらとは異なる動きをします。

上記の式は(10 < x) < 20 の順に解釈され、10 < xの結果であるtruefalse20という数値の大小が比較されることになってしまいます。
これは「xが10と20の間にある」ことを検査したいであろうプログラマの意図とは異なります。

このCopではこの問題を検出し、自動で10 < x && x < 20の様なコードに修正することが出来ます。

熟練したRubyプログラマであればこのような間違いを犯すことは少ないと思われますが、普段Rubyを書かない人やプログラミング初心者にとっては陥りやすい罠でしょう。

実際にTwitterでアンケートを取ったところ、半数近くの人からこのような間違いをしたことがあるという解答を得ることが出来ました。

Lint/SafeNavigationChain

このCopは、Ruby 2.3 から導入されたSafe Navigation Operator(通称 ぼっち演算子)の誤った使い方を指摘します。

foo&.bar

例えば、nilを返す可能性があるfooメソッドにbarメソッドをチェインをしたい場合、Safe Navigation Operatorを使用するとシンプルに記述することが可能です。

では、このコードに更にbazメソッドをチェインする場合を考えます。

foo&.bar.baz

この場合に上記の様に書いてしまうと、fooメソッドがnilを返した際にnilに対してbazを呼び出してしまうため、NoMethodErrorが発生してしまいます。
このコードは以下のように書き換えるのが正しいでしょう。

foo&.bar&.baz

このCopは、この様なSafe Navigation Operatorをメソッドチェインで使用している際のバグを検出、自動修正します。

なお、このCopはRuby 2.3以降を使用している場合にのみ有効になります。
もしRuby 2.3を使用している場合は、.rubocop.ymlに以下を追記する必要があります。

AllCops:
  TargetRubyVersion: 2.3

Security/MarshalLoad

Marshal.load 及び Marshal.restore メソッドには、設計上任意コードを実行できる危険性が存在します。
Module: Marshal (Ruby 2.3.3)

そのため、Webアプリケーションなどにおいてユーザーが入力可能な値をMarshal.loadすることは避け、代わりにJSONなどを採用すべきでしょう。

具体的には、以下のようなコードに対してこのCopは警告を出します。

Marshal.load(dump)

なお、Marshal.loadを安全に使用できるアプリケーションや実装方法も存在します。
例として、通信を行わないコマンドラインツールではMarshal.loadを安全に使用することが出来ると考えられます。

そのような場合は、.rubocop.ymlに以下のように記述することでこのCopを無効にすることが可能です。

Security/MarshalLoad:
  Enabled: false

Security/YAMLLoad

このCopはSecurity/MarshalLoadのYAML版です。
以下のようなコードに対して警告を出します。

YAML.load(dump)

Performance/RegexpMatch

このCopは、Ruby 2.4 で追加された String#match?メソッドなどに対応しています。
String#match? メソッドは、String#matchString#=~ などと同様正規表現とのパターンマッチを行いますが、MatchDataオブジェクトを生成しないためそれらより高速です。

String#match?メソッドの詳細については以下の記事がよくまとまっているので是非ご覧ください。
サンプルコードでわかる!Ruby 2.4の新機能と変更点 - Qiita

このCopは、match?メソッドで充分である(MatchDataを使用していない)match, =~メソッドの使用を検出、自動修正します。

# `MatchData` を使用していないため、`match?`に置き換え可能
def foo
  if x.match /re/
    puts 'matched'
  end
end

# 上記と同様 `MatchData` を使用していないため、`match?`に置き換え可能
def foo
  if x =~ /re/
    puts "matched #{$~}"
  end
end

# `MatchData` を使用しているため、`match?`に置き換えは不可能(Copは警告を出さない)
def foo
  if x.match /re/
    puts "matched #{$~}"
  end
end

このCopはRuby 2.4以降を使用している場合にのみ動作します。そのため、Ruby 2.4を使用している場合は.rubocop.ymlに以下のように追記してください。

AllCops:
  TargetRubyVersion: 2.4

尚、このCopの機能や実装の詳細などに関しては別途記事が存在するので、よろしければそちらもご覧ください。

Rails/FilePath

ファイルパスの連結はバグを生みやすい問題です。
セパレータを重複して書いてしまったり、あるいはセパレータを抜かして書いてしまうと、意図しないパスを表してしまうことがあります。

このCopは、Railsアプリケーションにおけるパスの連結の際に、Rails.root.joinメソッドを使用する様に推奨します。

# bad
Rails.root.join('app/models/goober')
File.join(Rails.root, 'app/models/goober')
"#{Rails.root}/app/models/goober"

# good
Rails.root.join('app', 'models', 'goober')

Rails/SkipsModelValidations

ActiveRecord の update_attribute などのメソッドは、モデルに対するUpdateを行いますが、その際にバリデーションをスキップします。
このCopでは、そのようなメソッドの使用を検出します。

バリデーションについて、詳しくはこの記事をご覧ください。 Active Record Validations — Ruby on Rails Guides

# bad
Article.first.decrement!(:view_count)
DiscussionBoard.decrement_counter(:post_count, 5)
Article.first.increment!(:view_count)
DiscussionBoard.increment_counter(:post_count, 5)
person.toggle :active
product.touch
Billing.update_all("category = 'authorized', author = 'David'")
user.update_attribute(website: 'example.com')
user.update_columns(last_request_at: Time.current)
Post.update_counters 5, comment_count: -1, action_count: 1

# good
user.update_attributes(website: 'example.com')

Rails/ReversibleMigration

Rails のマイグレーションには、ロールバック(rake db:rollback)が可能なものと、不可能なものがあります。

# bad
def change
  # qualification カラムの型がわからないため、rollback出来ない。
  remove_column(:suppliers, :qualification)
end

# good
def change
  # remove_columnの際も明示的に型を指定することで、rollbackが可能になる。
  remove_column(:suppliers, :qualification, :string)
end

このCopは、上記の例の様にrollbackすることが出来ないマイグレーションを検出します。

このCopを作成された方が記事を書かれているので、詳しくはそちらをご覧ください。 rubocopでreversibleなmigrationかどうかチェックしたかったので作った - くりにっき

Style/MethodCallWithArgsParentheses

引数を受け取るメソッド呼び出しに、()が使われていない場合を検出、自動修正します。

# メソッド呼び出しに()がないため、警告を出す
do_something x

# () があるためok
do_something(x)

なお、このCopはrequireincludeなどの構文的メソッドの呼び出しにも()をつけるよう警告を出します。
この様なメソッドに対しては警告を出さないようにするには、.rubocop.ymlに以下を追記します。

Style/MethodCallWithArgsParentheses:
  IgnoredMethods:
    - require
    - include

このCopはデフォルトでは無効になっており、使用するには以下のように明示的に有効にする必要があります。

Style/MethodCallWithArgsParentheses:
  Enabled: true

まとめ

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

Release RuboCop 0.47 · bbatsov/rubocop

ちなみに、前回(v0.46.0)リリースの時の私のツイートです。

本当のCopをお楽しみいただけたでしょうか?

過去のリリース

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5