RuboCop 0.47.0 のCHANGELOGを読む

追記 2017/01/20
===========

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


https://github.com/bbatsov/rubocop/releases/tag/v0.47.1

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


追記ここまで


------------


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




https://github.com/bbatsov/rubocop/releases/tag/v0.47.0


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



新規Cop追加
============

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

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


Lint/MultipleCompare
------

- PR https://github.com/bbatsov/rubocop/pull/3795
- Document http://rubocop.readthedocs.io/en/latest/cops_lint/#lintmultiplecompare


```ruby
10 < x < 20
```

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

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

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

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

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

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">アンケート<br>10 &lt; x &lt; 20<br>のような書き方をサポートしていないプログラミング言語(例: Ruby)で、上記のようなコードを書いてしまったことが</p>&mdash; Pocke (@p_ck_) <a href="https://twitter.com/p_ck_/status/810316522656845824">December 18, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


Lint/SafeNavigationChain
--------

- PR https://github.com/bbatsov/rubocop/pull/3804
- Document http://rubocop.readthedocs.io/en/latest/cops_lint/#lintsafenavigationchain

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

```ruby
foo&.bar
```

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

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

```ruby
foo&.bar.baz
```

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

```ruby
foo&.bar&.baz
```

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


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

```yaml
AllCops:
  TargetRubyVersion: 2.3
```


Security/MarshalLoad
--------

- PR https://github.com/bbatsov/rubocop/pull/3816
- Document http://rubocop.readthedocs.io/en/latest/cops_security/#securitymarshalload


`Marshal.load` 及び `Marshal.restore` メソッドには、設計上任意コードを実行できる危険性が存在します。
[Module: Marshal (Ruby 2.3.3)](http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations)

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

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

```ruby
Marshal.load(dump)
```

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

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

```yaml
Security/MarshalLoad:
  Enabled: false
```

Security/YAMLLoad
-------

- PR https://github.com/bbatsov/rubocop/pull/3821
- Document http://rubocop.readthedocs.io/en/latest/cops_security/#securityyamlload

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

```ruby
YAML.load(dump)
```

Performance/RegexpMatch
--------

- PR https://github.com/bbatsov/rubocop/pull/3824
- Document http://rubocop.readthedocs.io/en/latest/cops_performance/#performanceregexpmatch


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

`String#match?`メソッドの詳細については以下の記事がよくまとまっているので是非ご覧ください。
[サンプルコードでわかる！Ruby 2.4の新機能と変更点 - Qiita](http://qiita.com/jnchito/items/9f9d45581816f121af07#%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE%E3%81%AB%E6%96%87%E5%AD%97%E5%88%97%E3%81%8C%E3%83%9E%E3%83%83%E3%83%81%E3%81%97%E3%81%9F%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E3%81%A0%E3%81%91%E3%82%92%E8%BF%94%E3%81%99regexpmatchstringmatchsymbolmatch)

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

```ruby
# `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`に以下のように追記してください。

```yaml
AllCops:
  TargetRubyVersion: 2.4
```


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

- [Performance/RegexpMatch Cop の概要と実装 - pockestrap](http://pocke.hatenablog.com/entry/2016/12/27/174436)
- [RuboCopのASTの書き方の例 - koicの日記](http://koic.hatenablog.com/entry/2017/01/05/000000)
- [Ruby 2.4 のための RuboCop](http://me.pocke.me/slide-ruby24-rubocop/#/19)



Rails/FilePath
----------

- PR https://github.com/bbatsov/rubocop/pull/3822
- Issue https://github.com/bbatsov/rubocop/issues/3779
- Document http://rubocop.readthedocs.io/en/latest/cops_rails/#railsfilepath


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

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

```ruby
# 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
--------

- PR https://github.com/bbatsov/rubocop/pull/3825
- Issue https://github.com/bbatsov/rubocop/issues/3743
- Document http://rubocop.readthedocs.io/en/latest/cops_rails/#railsskipsmodelvalidations


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

バリデーションについて、詳しくはこの記事をご覧ください。 [Active Record Validations — Ruby on Rails Guides](http://guides.rubyonrails.org/active_record_validations.html#skipping-validations)


```ruby
# 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
--------

- PR https://github.com/bbatsov/rubocop/pull/3854
- Document http://rubocop.readthedocs.io/en/latest/cops_rails/#railsreversiblemigration
- Document https://github.com/bbatsov/rails-style-guide#reversible-migration


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


```ruby
# 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かどうかチェックしたかったので作った - くりにっき](http://sue445.hatenablog.com/entry/2017/01/16/120405)


Style/MethodCallWithArgsParentheses
----------

- PR https://github.com/bbatsov/rubocop/pull/3797
- Document http://rubocop.readthedocs.io/en/latest/cops_style/#stylemethodcallwithargsparentheses

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

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

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

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

```yaml
Style/MethodCallWithArgsParentheses:
  IgnoredMethods:
    - require
    - include
```



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

```yaml
Style/MethodCallWithArgsParentheses:
  Enabled: true
```






# まとめ

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

[Release RuboCop 0.47 · bbatsov/rubocop](https://github.com/bbatsov/rubocop/releases/tag/v0.47.0)



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

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">0.46.0で追加されたCop、どれも面白くないな</p>&mdash; Pocke (@p_ck_) <a href="https://twitter.com/p_ck_/status/803945777655468034">November 30, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">次のリリースでは本当のCopってものをお見せしますよ</p>&mdash; Pocke (@p_ck_) <a href="https://twitter.com/p_ck_/status/803945866365050880">November 30, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

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


# 過去のリリース

- [RuboCop 0.46.0 のCHANGELOGを読む - SideCI TechBlog](http://tech.sideci.com/entry/2016/12/01/150100)
- [RuboCop 0.45.0 のCHANGELOGを読む - SideCI TechBlog](http://tech.sideci.com/entry/2016/11/01/105900)
- [RuboCop 0.44.0 / 0.44.1 のCHANGELOGを読む - SideCI TechBlog](http://tech.sideci.com/entry/2016/10/20/110000)
- [RuboCop 0.43.0 の CHANGELOG を読む - SideCI TechBlog](http://tech.sideci.com/entry/2016/09/20/133051)
- [RuboCop 0.41 / 0.41.1 がリリースされました。 - SideCI TechBlog](http://tech.sideci.com/entry/2016/06/28/100415)