【Otemachi.rb#12】Atomでrubocop-auto-correctを使う際の注意点

##自己紹介
堀崎誠治

- https://qiita.com/horisakis
- https://twitter.com/horisakis
- 昔はCとかC++とか（もう書けない。。）エクセルお絵描きマンとか
- Ruby,Rails歴はPGスクール半年と業務委託3ヶ月


---
##事の始まり
Otemachi.rb #10でボッチ演算子を知ったので使ってみたところ、rubocop-auto-correctが反応しなくなった。

---
##rubocop-auto-correct
Atomの拡張機能。
Atom上でrubocopを-aオプションで実行してくれる。
（ファイル保存時に実行する設定もできる）


---
##期待する動作
![ok.png](https://qiita-image-store.s3.amazonaws.com/0/240839/9822822d-1788-45d2-52e1-9d50ef8f09b3.png)

---
##実際の動作
![ng.png](https://qiita-image-store.s3.amazonaws.com/0/240839/b2f5cd2a-abf4-cd67-0ee3-60691f51de07.png)

---
#無反応。。

---
##設定を変更して情報を増やす
<img width="732" alt="スクリーンショット 2018-12-11 13.33.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/240839/918f4cd0-bc37-4dfb-fe17-71277a61d2f2.png">
⬇️
<img width="612" alt="スクリーンショット 2018-12-11 13.33.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/240839/8bc9962c-0918-8c8c-a657-d1acc670b412.png">

---
<img width="497" alt="スクリーンショット 2018-12-11 13.33.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/240839/30edf0f2-0c98-be01-789a-395929fd4a93.png">

---
#Ruby2.2で動作してる<br>(ボッチ演算子は2.3から)

---
##RubocopのRubyバージョン特定方法
rubocopがrubyのバージョンを特定する優先順位は以下のとおり。

.rubocop.ymlのTargetRubyVersion の値 >
.ruby-versionの値 >
Gemfile.lockのRubyバージョンの値 >
デフォルト値(2.2)

---
##当時の環境
- .rubocop.ymlなし

```bash
$ ls .rubocop.yml ~/.rubocop.yml
ls: .rubocop.yml: No such file or directory
ls: /Users/user/.rubocop.yml: No such file or directory
```

-  .ruby-veversionあり

```bash
$ cat .ruby-version
2.3.1
```

- Gemfile.lockにrubyのバージョン指定なし

```bash
$ grep -A 1 "RUBY VERSION" Gemfile.lock | wc -l
       0
```
---
##手動で実行してみる
```
$ rubocop -a sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:1:1: C: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
def sample(sample_arg)
^

1 file inspected, 1 offense detected, 1 offense corrected
```
---
##手動なら動くので、拡張機能での実行時との差分を詰めていく


---
##rubocop-auto-correctの実装

```javascript:rubocop-auto-correct/lib/rubocop-auto-correct.coffee
  autoCorrectBuffer: (editor)  ->
    buffer = editor.getBuffer()

    tempFilePath = @makeTempFile("rubocop.rb")
    fs.writeFileSync(tempFilePath, buffer.getText())

    rubocopCommand = @rubocopCommand()
    command = rubocopCommand[0]
    args = rubocopCommand[1]
      .concat(['-a', tempFilePath])
      .concat(@rubocopConfigPath(buffer.getPath()))

    which command, (err) =>
      return @rubocopNotFoundError() if (err)
      rubocop = spawnSync(command, args, { encoding: 'utf-8', timeout: 5000 })
```
https://github.com/tomoya/rubocop-auto-correct/blob/23c91f2af7f1484dd97d311240f2dcd9df04f5ef/lib/rubocop-auto-correct.coffee#L99-L116

---
##ログを埋め込んでみる

```javascript:
    args = rubocopCommand[1]
      .concat(['-a', tempFilePath])
      .concat(@rubocopConfigPath(buffer.getPath()))

//ログ埋め込み
    console.log (command + " " + args.join(" "))
```
---
##実行されたコマンド

- rubocop.ymlなし

/Users/user/.anyenv/envs/rbenv/shims/rubocop --format json -a /var/folders/09/xr1wrst90lb_295d2pprm6f80000gn/T/d-118916-6808-ed0mea.2qqbj/rubocop.rb

- rubocop.ymlあり

/Users/user/.anyenv/envs/rbenv/versions/2.5.1/bin/rubocop --format json -a /var/folders/09/xr1wrst90lb_295d2pprm6f80000gn/T/d-1181111-42791-1nbrcij.sdpz/rubocop.rb --config /Users/user/develop/work/qiita/rubocop_auto/.rubocop.yml

- HOMEディレクトリにのみ.rubocop.ymlあり

/Users/user/.anyenv/envs/rbenv/versions/2.5.1/bin/rubocop --format json -a /var/folders/09/xr1wrst90lb_295d2pprm6f80000gn/T/d-1181111-42791-qw10yv.5336/rubocop.rb --config /Users/user/.rubocop.yml

---

##Rubocopの実装

```ruby:rubocop/lib/rubocop/config.rb
    def target_ruby_version
      @target_ruby_version ||=
        if for_all_cops['TargetRubyVersion']
          @target_ruby_version_source = :rubocop_yml


          for_all_cops['TargetRubyVersion']
        elsif target_ruby_version_from_version_file
          @target_ruby_version_source = :ruby_version_file


          target_ruby_version_from_version_file
        elsif target_ruby_version_from_bundler_lock_file
          @target_ruby_version_source = :bundler_lock_file


          target_ruby_version_from_bundler_lock_file
        else
          DEFAULT_RUBY_VERSION
        end
    end
```

---
```ruby:rubocop/lib/rubocop/config.rb
    def target_ruby_version_from_version_file
      file = ruby_version_file
      return unless file && File.file?(file)


      @target_ruby_version_from_version_file ||=
        File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
          md[:version].to_f
        end
    end
```
```ruby:rubocop/lib/rubocop/config.rb
    def ruby_version_file
      @ruby_version_file ||=
        find_file_upwards(RUBY_VERSION_FILENAME, base_dir_for_path_parameters)
    end
```

---
```ruby:rubocop/lib/rubocop/config.rb
    def ruby_version_file
      @ruby_version_file ||=
        find_file_upwards(RUBY_VERSION_FILENAME, base_dir_for_path_parameters)
    end
```

---
```ruby:rubocop/lib/rubocop/config.rb
    # Paths specified in configuration files starting with .rubocop are
    # relative to the directory where that file is. Paths in other config files
    # are relative to the current directory. This is so that paths in
    # config/default.yml, for example, are not relative to RuboCop's config
    # directory since that wouldn't work.
    def base_dir_for_path_parameters
      @base_dir_for_path_parameters ||=
        if File.basename(loaded_path).start_with?('.rubocop') &&
           loaded_path != File.join(Dir.home, ConfigLoader::DOTFILE)
          File.expand_path(File.dirname(loaded_path))
        else
          Dir.pwd
        end
    end
```

---
https://github.com/rubocop-hq/rubocop/blob/8a5be5fdb0d9de92308c9f1b399cb6abe9a82bdf/lib/rubocop/config.rb#L425-L442

https://github.com/rubocop-hq/rubocop/blob/8a5be5fdb0d9de92308c9f1b399cb6abe9a82bdf/lib/rubocop/config.rb#L588-L596

https://github.com/rubocop-hq/rubocop/blob/8a5be5fdb0d9de92308c9f1b399cb6abe9a82bdf/lib/rubocop/config.rb#L583-L586

https://github.com/rubocop-hq/rubocop/blob/8a5be5fdb0d9de92308c9f1b399cb6abe9a82bdf/lib/rubocop/config.rb#L410-L423

---

##ログを仕込んでみる
```ruby:rubocop/lib/rubocop/config.rb
        File.open("/Users/user/rubocop_auto.out", "a") {|f|
          f.puts "Dir.pwd: #{Dir.pwd}"
        }

```

---
#結果

```bash:/Users/user/rubocop_auto.out
Dir.pwd: /
```

---
##rootディレクトリから実行されていたので設定ファイルが見つけられていなかった

---
##あれ?

---
```ruby
File.basename(loaded_path).start_with?('.rubocop')
```

---
##.rubocopで始まるファイルさえあればいい

---
```bash
$ wc -l .rubocop.yml
       0 .rubocop.yml
$ cat .ruby-version
2.3.1
```

---
![ok.png](https://qiita-image-store.s3.amazonaws.com/0/240839/a432a6b8-d0e3-6719-bbe3-060728ee3693.png)

---
##動いた！

---
##調査結果
||ファイル|結果||ファイル|結果|
|---|---|:---|---|---|---|
|1|.rubocop.yml|○|6|1+3|○|
|2|~/.rubocop.yml|×|7|1+4|○|
|3|.ruby-version|×|8|1+5|○|
|4|~/.ruby-version|×|9|2+3|×|
|5|Gemfile.lock|×|10|2+4|×|
||||11|2+5|×|
※6~11の.rubocop.ymlは空ファイル

---
##まとめ
- --configオプションを指定しない場合はカレントディレクトリからファイルを探す。
- atomから見たカレントディレクトリはrootディレクトリ。
- プロジェクトディレクトリに.rubocop.ymlを配置する以外の手段は不可。
- 拡張機能側で~/.rubocop.ymlを探してコマンドに渡しているがコマンド側で受け付けていない。(コマンド側の動きが変わった？)
- rbenvを使っているならtouch .rubocop.ymlするだけで使える。
- とはいえデフォルト設定ではrubocopの指摘は厳しいので.rubocop.ymlは設定したほうがいい。

---
##参考
 - RuboCopが静的解析のRubyバージョンを決める流れ

http://koic.hatenablog.com/entry/target-ruby-version-of-rubocop