Ruby
RuboCop

rubocopのエラー(offense)が多い順にソートするには

rubocopのエラー(offense)が多い順にソートするには

こういうことをやりたい:

# rule to counter
{"Layout/SpaceInsideHashLiteralBraces"=>289,
 "Layout/SpaceBeforeBlockBraces"=>79,
 "Style/RedundantSelf"=>36,
 "Layout/DotPosition"=>31,
 "Style/TrailingCommaInLiteral"=>30,
 "Style/BracesAroundHashParameters"=>29,
 "Layout/SpaceAroundOperators"=>19,
 "Layout/EmptyLinesAroundClassBody"=>16,
 "Lint/UselessAssignment"=>16,
 "Style/GuardClause"=>14,
 "Style/AndOr"=>13,
 "Style/IfUnlessModifier"=>11,
 "Rails/Date"=>10,
 "Layout/SpaceInsideParens"=>7,
 "Layout/AlignParameters"=>7,
 "Metrics/PerceivedComplexity"=>7,
 "Rails/TimeZone"=>5,
..
}

code

こんな感じにする:

require 'rubocop'

def count_offences(paths = [])
  runner = RuboCop::Runner.new({}, RuboCop::ConfigStore.new)
  files = runner.send(:find_target_files, paths)
  files_offences = files.map {|file| runner.send(:file_offenses, file)}

  counter = {}
  files_offences.each do |file_offences|
    file_offences.group_by(&:cop_name).each do |cop_name, arr|
      counter[cop_name] ||= 0
      counter[cop_name] += arr.size
    end
  end
  counter
end

count_offences.sort_by { | k,v | -v}.to_hとすれば、上記の結果のように降順に並べることができる。

ちょっと解説

どうやら、CLIのほうでは昇順降順にできないみたい。(--auto-gen-configつけてもちろんダメです。)そこで、スクリプトを書いて対処しよう。

追記)実は出来ました。コメント参考に。

http://www.rubydoc.info/gems/rubocop/RuboCop/CLI#run-instance_methodRuboCop::CLI#runがエントリーポイントであることが書いてるので、コードをちょっと追って行くと、

https://github.com/bbatsov/rubocop/blob/v0.53.0/lib/rubocop/cli.rb#L152-L156

にぶち当たる。runner.run(paths)が実体なので、https://github.com/bbatsov/rubocop/blob/v0.53.0/spec/rubocop/runner_spec.rb#L20 
とか参考にしつつ、上のコードを書けば良い。find_target_filesfile_offensesはprivate methodなので、Object#sendを使えばこれらを呼び出せる。

上記のコードのarrの要素の中身はRuboCop::Cop::Offenseクラスで、こんな感じになっている:

#<RuboCop::Cop::Offense:0x007fa0e28009c8
 @cop_name="Lint/UnusedBlockArgument",
 @location=
  #<Parser::Source::Range /Users/kenta.nakajima/Documents/rails/test.rb 503...504>,
 @message=
  "Lint/UnusedBlockArgument: Unused block argument - `k`. If it's necessary, use `_` or `_k` as an argument name to indicate that it won't be used.",
 @severity=#<RuboCop::Cop::Severity:0x007fa0e2800950 @name=:warning>,
 @status=:uncorrected>

今回はルールを取り出したかったので、cop_nameで所要の結果が得られたわけだ。