Closed onk closed 4 years ago
https://github.com/bbatsov/rubocop/pull/4588 これ、めんどくさくて挫折してたんですが、実は手元に
みたいなコードがあります。 この時はLineLengthはターゲットにしていなかったのでいい感じの結果が出るかはわかりませんが、あとでコードを公開しますね
↑のコメントで言及したコードです。
環境に依存するかも知れないです。回数は適当に調整してください。
require 'pathname'
STATS_URL = 'https://rubygems.org/stats?page=%{page}'
def sh!(cmd)
puts "$ #{cmd}"
system(cmd)
end
GEM_HOME = Pathname(Dir.pwd).join('gem_home')
GEM_HOME.mkdir unless GEM_HOME.exist?
1.upto(50) do |i|
puts "page: #{i}"
gems = `curl '#{STATS_URL % {page: i}}'`
.each_line
.grep(/class="stats__graph__gem__name"/)
.map {|line| line[%r!([^>]+)\</a\>\</h3\>$!, 1]}
.map(&:chomp)
sh! "GEMSRC_SKIP=true GEM_HOME=#{GEM_HOME} gem install #{gems.join(' ')} --no-user-install"
sleep 1
end
なお、この手法にはdisable commentを無視できないというバグがあります。(disable commentで無効化されているコードを計測できない)
各ライブラリの設定を無視するため
rm **/*rubocop*yml
計測したいCopのMaxを0にする 例
MethodLength:
Max: 0
AbcSize:
Max: 0
AllCops:
TargetRubyVersion: 2.4
なお、実はこの辺の変更によって、masterとv0.51.0では違う結果が出る気がします。
only に対象のCopを指定、formatはJSONで。parallelはあると便利
rubocop --parallel --only MethodLength,AbcSize --format json . > out.json
output
というファイルに結果を吐き出します。これは、Metrics/MethodLength
の例
< out.json| ruby -rjson -e 'puts JSON[ARGF.read]["files"].flat_map{|file|file["offenses"].map{|offense| offense["cop_name"] == "Metrics/MethodLength" ? offense["message"][/([\d.]+)\/0\]$/, 1].to_i : nil}.compact}' > output
~ちょっと何をやっているか覚えていないのですが、多分なにか数字が出てきます。~
多分このコードは「N
に指定した数値に.rubocop.yml
内のMax
を指定した時、何パーセンタイルのコードをカバーできるか」という意味です。
sort -n < output | uniq -c | ruby -e 'N=17; a=STDIN.read.each_line.map(&:split).map{|x|x.map{|y|y.to_i}}; sum = a.map{|x|x[0]}.sum; sum2 = a.select{|_, n| n<=N}.map{|x|x[0]}.sum; p(sum2 / sum.to_f)'
ちなみに、Metrics/LineLength
に対して上記コードを適用してみたところ、Max
が80の時92.34パーセンタイルのコードに対しては警告が出ない、ということが分かりました。
この辺もし見てみたい人がいたらデータをアップロードするので、声をかけてください。上のスクリプトは結構時間がかかるので最初から実行するのはたいへんです
LineLength もパーセンタイルで許容するのが有効なのかな……? であればそっちを進めると他の Metrics Cop でも便利になるので十分かもしれない。
余談:
rubocop 本体の場合は 80 ギリギリに調整してあるコード/コメントが異常に多く、Max: 79
にするだけで 1062 files inspected, 339 offenses detected
となって明確に人の手による調整コストが掛かっているので、このコストは下げたいなと思う。
stars:>1500 language:ruby
, filename:.rubocop.yml repo:foo/bar
で検索してダウンロードする# 概念コード。sleep とかページネートとか要ります。
client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
client.search_repos("stars:>1500 language:ruby").each do |repo|
client.search_code("filename:.rubocop.yml repo:#{repo.full_name}") do |code|
f = client.contents(code.repository.full_name, path: code.path)
FileUtils.mkdir_p(path.dirname)
path.write(Base64.decode64(f.content))
end
end
RuboCop::ConfigLoader
にパッチを当てるmry すごい簡単便利
Dir.glob("repos/*/*").each do |dir|
Dir.chdir(dir) do
cli = RuboCop::CLI.new
config_for_line_length = cli.config_store.for(Dir.pwd).for_cop("Metrics/LineLength")
if config_for_line_length["Enabled"]
p config_for_line_length["Max"]
else
p "Disabled"
end
end
rescue => e
# nop
end
的な感じでやっています。
RuboCop::ValidationError
に引っかかったり、inherit_gem
が書かれていたりと例外対応が結構要ります。ここを綺麗に書けてない 😢
RuboCop 1.0 でデフォルト 120 になります。 https://github.com/rubocop-hq/rubocop/pull/7952
デフォルト 80 からのアップデートということでイシューの役割を果たしたと思うためクローズします。
統計で殴るのが変更を訴える理由として一番有効そうかなと思っているんですが、 第一報としてこんな感じです。
雑に star 数 1500 以上で
.rubocop.yml
を持っているプロダクトを 見て回ったところ、以下のような感じになりました。(いっぱい取りこぼしがあるので雰囲気として掴んでください) (追試可能なようにコード化していきます……)