RuboCop 0.50.0 のCHANGELOGを読む
RuboCop のバージョン 0.50.0 がリリースされました。
https://github.com/bbatsov/rubocop/releases/tag/v0.50.0
CHANGELOG から変更を見ていこうと思います。
破壊的変更
Naming Department
- Issue: https://github.com/bbatsov/rubocop/issues/4521
- PR: https://github.com/bbatsov/rubocop/pull/4575
今回のリリースでは、新たに Naming Department が新設されました(ちなみに、Department とは RuboCop 用語で、Cop のグループのことです)。 Naming Department には、メソッドやクラスの名前付けに関する Cop が所属します。
今回の Naming Department の新設に伴い、多くの Cop が Style Department から移されました。
そのため、移動対象となる Cop の設定を.rubocop.yml
で行っている場合、設定の書き換えが必要となります。
例:
# 旧 Style/VariableName: EnforcedStyle: snake_case # 新 Naming/VariableName: EnforcedStyle: snake_case
この書き換えを自動で行うツールを提供していますので、アップデートの際は是非お使い下さい。
新規Cop追加
Copとは、RuboCopにおいてひとつのルールを指す言葉です。例えば、「インデントが正しいかチェックする」「非推奨メソッドを使っていないかチェックする」などが1つのCopの単位になります。
この章では、0.50.0で新たに追加されたCopをひとつずつ紹介します。
Style/RedundantConditional
- Issue: https://github.com/bbatsov/rubocop/issues/4130
- PR: https://github.com/bbatsov/rubocop/pull/4453
- Document: http://rubocop.readthedocs.io/en/latest/cops_style/#styleredundantconditional
x == y ? true : false x == y ? false : true if x == y true else false end
上記のコードでは、三項演算子や if 式を使う必要がありません。何故ならば、==
演算子は通常 true / false を返すため、三項演算子の部分がなくても全く同じ意味になります。
この Cop は、上記のような冗長な三項演算子や if 式を使っているコードに対して警告を出します。
Naming/HeredocDelimiterNaming
- PR: https://github.com/bbatsov/rubocop/pull/4467
- Document: http://rubocop.readthedocs.io/en/latest/cops_naming/#namingheredocdelimiternaming
この Cop は、ヒアドキュメントのデリミタに説明的でない名前をつけているコードに対して警告します。
code = <<~END puts 'Hello' END code = <<~RUBY puts 'Hello' RUBY
例えば、上記のコードはヒアドキュメントのデリミタが異なりますが、意味は変わりません。
ですが、後者の方がヒアドキュメントの中身がRubyのコードであることを説明している分、より説明的です。
また、使用しているテキストエディタによっては後者のヒアドキュメントの中身を適切にハイライトしてくれる場合もあるようです。
この Cop は、上記のように"END"などの説明的でない名前を使っているコードに対して警告を出します。
Lint/ReturnInVoidContext
- PR: https://github.com/bbatsov/rubocop/pull/4468
- Issue: https://github.com/bbatsov/rubocop/issues/4153
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#lintreturninvoidcontext
Ruby では、initialize
メソッドやfoo=
メソッドなど、一部のメソッドの戻り値は無視されます。
例:
class A def initialize return 1 end def foo=(var) @foo = var return 2 end end # initialize メソッドの戻り値にかかわらず、A class のインスタンスが返る a = A.new p a # => #<A:0x0000560a195ebe28> # foo= メソッドの戻り値にかかわらず、代入した値が返る b = (a.foo = 100) p b # => 100
そのような戻り値が無視されるメソッドで値付きのreturn
を使っている場合、意図しない挙動となってしまう場合があります。
この Cop はそのようなコードに警告を出します。
Lint/BooleanSymbol
- PR: https://github.com/bbatsov/rubocop/pull/4515
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#lintbooleansymbol
:true
のようなシンボルを書いた時、それはtrue
リテラルの間違いではないか?と警告する Cop です。
Rails/HasManyOrHasOneDependent
- PR: https://github.com/bbatsov/rubocop/pull/4541
- Document: http://rubocop.readthedocs.io/en/latest/cops_rails/#railshasmanyorhasonedependent
ActiveRecord では、has_many
やhas_one
の関係を指定する時、dependent
オプションを指定することが出来ます。
class Post < ActiveRecord::Base has_many :comments, dependent: :destroy end
これを指定することで、親レコードが削除された時、子レコードも同時に削除することが出来ます。
dependent
オプションの指定が無い場合、RDBMS側で設定されたForeign KeyにON DELETE
が指定されていれば子レコードも消すことが出来ますが、その指定が無い場合子レコードは放置されてしまいます。
そのような状況はデータベース上に壊れたデータが存在してしまうので、好ましくありません。
この Cop は、has_many
関連を指定した時にdependent
オプションをつけることを強制します。
これにより、dependent
オプションを忘れてしまうことを防ぐことが出来ます。
Style/Dir
- PR: https://github.com/bbatsov/rubocop/pull/4552
- Document: http://rubocop.readthedocs.io/en/latest/cops_style/#styledir
Ruby には、__dir__
というメソッドが定義されています。
これはRubyプログラムが書かれたファイルのディレクトリを絶対パスで返します。
See. https://docs.ruby-lang.org/ja/latest/method/Kernel/m/dir.html
同様の出力を得ることは、以下のコードでも可能です。
File.expand_path(File.dirname(__FILE__)) File.dirname(File.realpath(__FILE__))
ですが、__dir__
を使用したほうがよりスマートにディレクトリを得ることが出来ます。
この Cop は、上記のような冗長なディレクトリの取得を行っているコードに警告を出し、__dir__
を使用するように促します。
Naming/HeredocDelimiterCase
- PR: https://github.com/bbatsov/rubocop/pull/4548
- Document: http://rubocop.readthedocs.io/en/latest/cops_naming/#namingheredocdelimitercase
ヒアドキュメントのデリミタには、大文字小文字どちらのアルファベットも用いることが出来ます。
code = <<~ruby puts 'hello' ruby code = <<~RUBY puts 'hello' RUBY
ですが、多くの場合大文字のみを使用したほうが、ヒアドキュメントのデリミタであることがよりわかりやすくなるため良いでしょう。
この Cop は、ヒアドキュメントのデリミタが大文字だけであることを検査し、小文字が含まれていた場合には警告を出します。
また、場合によっては大文字ではなく小文字のみに制限したい場合もあるでしょう。そのような場合には、.rubocop.yml
にて設定を変更することが出来ます。
Naming/HeredocDelimiterCase: EnforcedStyle: lowercase
Lint/RescueWithoutErrorClass
- Issue: https://github.com/bbatsov/rubocop/issues/2943
- PR: https://github.com/bbatsov/rubocop/pull/4564
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#lintrescuewithouterrorclass
begin foo rescue bar end begin foo rescue StandardError bar end
Rubyでは上記のようにrescue
節で例外クラスの指定を省略した場合、StandardError
を指定したことと同じ扱いになります。
これにより、想定より多くの例外をキャッチしてしまい、思いもよらないバグを生む可能性があります。
そのため、rescue
節には想定する例外クラスのみを指定するべきでしょう。
この Cop はこのような例外クラスが指定されていないrescue
節に対して警告を出します。
Performance/UnfreezeString
- PR: https://github.com/bbatsov/rubocop/pull/4586
- Document: http://rubocop.readthedocs.io/en/latest/cops_performance/#performanceunfreezestring
Ruby 2.3 からString#+@
(単項+
演算子)が追加されました。
このメソッドは freeze されていない String を返します。
str = 'foo'.freeze p str.frozen? # => true p (+str).frozen? # => false
String#dup
などでもこれと同様にfreezeされていない String を得ることが出来ますが、String#+@
の方がより高速です。
この Cop は、String#dup
の代わりにString#+@
を使うように推奨します。
See. https://qiita.com/k0kubun/items/1c3e605645ba5ff683a1
Style/OrAssignment
- PR: https://github.com/bbatsov/rubocop/pull/4648
- Issue: https://github.com/bbatsov/rubocop/issues/3965
- Document: http://rubocop.readthedocs.io/en/latest/cops_style/#styleorassignment
x = x ? x : 3 x = 3 unless x
上記のようなコードは、||=
を使うことでより簡潔に書くことが可能です。
x ||= 3
この Cop は、上記のような冗長な代入を検出し、警告します。
Style/ReturnNil
- PR: https://github.com/bbatsov/rubocop/pull/4638
- Issue: https://github.com/bbatsov/rubocop/issues/4508
- Document: http://rubocop.readthedocs.io/en/latest/cops_style/#stylereturnnil
Ruby ではreturn
に何も値を渡さない場合、メソッドはnil
を返します。
def foo return end p foo.nil? # => true
そのため、return nil
といった記述は冗長です。
この Cop は、そのような冗長な記述に警告を出し、単にreturn
と書くように推奨します。
なお、この Cop はデフォルトで無効になっているため、有効にするには明示的に.rubocop.yml
に指定をする必要があります。
Style/ReturnNil: Enabled: true
また、return nil
と冗長に記述したい場合は.rubocop.yml
に以下のように設定することが可能です。
Style/ReturnNil: EnforcedStyle: return_nil
Lint/UriEscapeUnescape
- PR: https://github.com/bbatsov/rubocop/pull/4702
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#linturiescapeunescape
URI.escape
, URI.encode
, URI.unescape
, URI.decode
の各メソッドは obsolete です。
そのため、代わりにCGI.escape
, URI.encode_www_form
, URI.encode_www_form_component
などのメソッドを使用すべきでしょう。
この Cop は、上記の obsolete なメソッドに対して警告を出します。
Performance/UriDefaultParser
- PR: https://github.com/bbatsov/rubocop/pull/4696
- Document: http://rubocop.readthedocs.io/en/latest/cops_performance/#performanceuridefaultparser
URI に対する正規表現を作成する為に、URI::Parser.new.make_regexp(%w[https http])
のようなコードを用いることが出来ます。
これにはより高速な書き方があり、URI::Parser.new
をURI::DEFAULT_PARSER
に置き換えることでより高速に実行することが出来ます。
require 'benchmark' require 'uri' Benchmark.bm(20) do |x| puts 'empty arg' x.report('URI::Parser.new'){ 2000.times{URI::Parser.new.make_regexp}} x.report('URI::DEFAULT_PARSER'){2000.times{URI::DEFAULT_PARSER.make_regexp}} puts 'with arg' x.report('URI::Parser.new'){ 2000.times{URI::Parser.new.make_regexp('http://example.com')}} x.report('URI::DEFAULT_PARSER'){2000.times{URI::DEFAULT_PARSER.make_regexp('http://example.com')}} end
user system total real empty arg URI::Parser.new 2.010000 0.010000 2.020000 ( 2.016804) URI::DEFAULT_PARSER 0.000000 0.000000 0.000000 ( 0.000144) with arg URI::Parser.new 2.460000 0.000000 2.460000 ( 2.451649) URI::DEFAULT_PARSER 0.330000 0.000000 0.330000 ( 0.331841)
これはURI::DEFAULT_PARSER
ではURI::Parser
のインスタンスを使いまわすためです。
https://github.com/ruby/ruby/blob/4b9aeef1fa2f32a4555f88c6e83539374a1bd8d1/lib/uri/common.rb#L22
この Cop は、URI::Parser.new
に警告を追加し、より速いURI::DEFAULT_PARSER
を使うように促します。
Lint/UriRegexp
- PR: https://github.com/bbatsov/rubocop/pull/4694
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#linturiregexp
URI.regexp
というURIの正規表現を生成するメソッドがありますが、これは obsolete です。
そのため、代わりにURI::DEFAULT_PARSER.make_regexp
を使用すべきです。
この Cop は obsolete なURI.regexp
の使用を警告します。
Style/MinMax
- PR: https://github.com/bbatsov/rubocop/pull/4711
- Document: http://rubocop.readthedocs.io/en/latest/cops_style/#styleminmax
Enumerable には、最小値と最大値を同時に入手するための#minmax
が定義されています。
a = [2, 4, 1, 3] p [a.min, a.max] == a.minmax # => true
この Cop は、[a.min, a.max]
のようなコードに対してminmax
メソッドを使用するように警告を出します。
Bundler/InsecureProtocolSource
- PR: https://github.com/bbatsov/rubocop/pull/4720
- Document: http://rubocop.readthedocs.io/en/latest/cops_bundler/#bundlerinsecureprotocolsource
Bundler は、source :rubygems
のような Symbol による source の指定に対して警告を出します。
この Cop はこのような Symbol による source の指定に警告を出します。
また、この Cop は auto-correct に対応しています。そのため、rubocop -a
として実行することで、:rubygems
を'https://rubygems.org'
に自動的に書き換えることが可能です。
Lint/RedundantWithIndex
- PR: https://github.com/bbatsov/rubocop/pull/4708
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#lintredundantwithindex
この Cop は、使用されていないwith_index
を検出します。
ary.each.with_index do |v| do_something v end
上記のコードでは.with_index
が呼び出されていますが、これは使用されていません。
この Cop はこのような使用されていないwith_index
に対して警告を出します。
Lint/InterpolationCheck
- PR: https://github.com/bbatsov/rubocop/pull/4480
- Document: http://rubocop.readthedocs.io/en/latest/cops_lint/#lintinterpolationcheck
Ruby では、ダブルクオート文字列内で式展開を行うことが出来ます。
"foo#{100}baz" # => "foo100baz"
ですが、シングルクオートで囲まれた文字列の中では式展開を行うことが出来ません。
'foo#{100}baz' # => "foo\#{100}baz"
この Cop は、このようなシングルクオート文字列の中で式展開をしようとしているコードに対して警告を出します。
まとめ
この記事は以上になりますが、RuboCop 0.50.0ではこの他にも多くの機能追加、バグ修正が行われています。 より詳しい変更を知りたい方は、リリースノートをご覧ください。
Release RuboCop 0.50 · bbatsov/rubocop
過去のリリース
- RuboCop 0.49.0 のCHANGELOGを読む - Qiita
- RuboCop 0.48.0 のCHANGELOGを読む - Qiita
- RuboCop 0.47.0 のCHANGELOGを読む - Qiita
- RuboCop 0.46.0 のCHANGELOGを読む - SideCI TechBlog
- RuboCop 0.45.0 のCHANGELOGを読む - SideCI TechBlog
- RuboCop 0.44.0 / 0.44.1 のCHANGELOGを読む - SideCI TechBlog
- RuboCop 0.43.0 の CHANGELOG を読む - SideCI TechBlog
- RuboCop 0.41 / 0.41.1 がリリースされました。 - SideCI TechBlog