vimからdocker-compose上のrspecを実行する

# 結論

`Vim`で開いている`rspec`ファイルのカーソル位置にあるテストを、自分の開発環境の`docker compose`上の`rspec`で、気軽に実行出来る様にしました。

- 結果は[vim-dispatch](https://github.com/tpope/vim-dispatch)を使って`quickfix`に表示。
- あとは温かみのある手作り。
  - `rspec`に渡すパスは、`docker-compose`のパスに合うように、`rspec`の絶対パスから切り出して作成
  - カーソル行を取得してテスト対象を指定

```vim
" docker-composeでのテスト用定義。環境が変わったら書き換える
function! RunLineSpec() abort
  let absolute_path = expand("%:p")
  let relation_path = absolute_path[stridx(absolute_path, "spec/"):]
  let rspec_cmd = 'docker compose run {docker-composeのサービス名} 
  bundle exec rspec ' . relation_path . ':' . line(".")
  execute 'Dispatch' rspec_cmd
endfunction

nmap <silent> <Leader>test :call RunLineSpec()<CR>
```

# 経緯

最近、なんでもVimでやりたい病にかかっています。
先日も発作的に「docker-composeで実行するrubyのRspecをVimから呼び出したい」と思い立ち、いろいろと検討をしてみました。

## vim-rspec 導入

まずは [vim-rspec](https://github.com/thoughtbot/vim-rspec)がシンプルで便利そうだったから入れてみました。
とはいえ、元々素直な開発環境ではないので、何とか頑張って設定して動けばいいなぁという気持ちからのスタートです。

自分の開発環境は
- 編集は`WSL（ubuntu20.04）`上のコードを、`WindowsTerminal`で`Vim`
- 実行環境は`WSL`で立ち上げた`docker compose`のサービス上に、コードをマウントして実行

という形にしてあります。

なので、`Vim`と`Rspec`は実行環境が別々になっていて、`Vim`から`bin/rspec`を呼んでも、テストは動いてはくれないのです。

幸い、`vim-rspec`には`実行するコマンドを変更する設定が用意されていたので、まずはこれを、自分の環境に合うように指定しました。

```vim
let g:rspec_command = "docker compose run {サービス名} bundle exec rspec {spec}"
```

{spec}には、カレントディレクトリから`buffer`への相対パスが入る様です。
なのでVimを開く前にカレントディレクトリをプロジェクトルートに変更するという運用にする事にしました。
これによって、「`docker compose run`の際のコンテナ上のカレントディレクトリ=Vimを開いた際のカレントディレクト」となるので、`{rspec}`に入っているパスが、サービスのコンテナ上のカレントディレクトリからも見れる事になります。
これで、`vim-rspec`を用いた、`docker compose`経由の実行は可能になりました。

ついでに`https://github.com/tpope/vim-dispatch`も使って、結果を`quickfix`に表示するようにもしました。

```vim
let g:rspec_command = "Dispatch docker compose run {サービス名} bundle exec rspec {spec}"
```

それから、実際にテスト実行につかうのは、`カーソル付近のテストの実行`だけなので、以下のマッピングも追加しました。

```vim
nmap <silent> <Leader>test call :RunNearestSpec()<CR>
```

これで、個別テストがしたい時は、そのテストにカーソルを合わせて`<space>（<Leader>をスペースにしてるので）test`でそのテストだけが動くし、全体のテストがしたければ、`gg<space>test`で全テストが動く様になります。

`vim-rspec`の使用感としては、これで全く問題なく運用出来ていました。

## カレントディレクトリの制約を取り除きたい

Vimを開いた後の使用感の問題は無くなったけれども、`プロジェクトルートでVimを開かないとVim上からテストが出来ない`ストレスは残り、カレントディレクトリがどうであれ、ちゃんとテストを実行出来る様にならないかと考えはじめました。

`buffer`の絶対パスから、アプリケーションルート基準の相対パスを作り、`docker-compose`に送る事が出来れば実現出来そうです。

`rspec`は、アプリケーションルートの直下の`spec`ディレクトリに入っているので、ここで絶対パスを区切れば、`spec/`からの相対パスが取れると考えました。
（アプリケーションルートより前に`spec`ディレクトリがある様な場合の事はもう考えなくて良いかなと諦めました。）

```vim
function! RunLineSpec() abort
  let absolute_path = expand("%:p")
  let relation_path = absolute_path[stridx(absolute_path, "spec/"):]
  let rspec_cmd = 'docker compose run {docker-composeのサービス名} 
  bundle exec rspec ' . relation_path
  let g:rspec_command = "Dispatch docker compose run operator_web bundle exec rspec " . relation_path
  call RunNearestSpec()
endfunction

nmap <silent> <Leader>test call :RunLineSpec()<CR>
```


これで、`Vim`側のカレントディレクトリに関係無く、`rspec`が実行出来る様になりました！
万事解決！

と思いきや、まだ課題がありまして。

この時点で、`vim-rspec`が提供する{rspec}がどこにも入っていないコマンドが出来上がっていることになってます。

なので、実行時のカーソル位置を受け取る場所もなくなってしまい、`RunNearestSpec()`を実行しているのに、対象テストだけでなく、ファイル内の全テストが実行されるようになってしまったのです。

これじゃあ気軽なテストとしては使い物にならないですね…。

仕方ない、カーソル行を渡す記述も追加して…と考え始めたんですけど、このあたりでふと気づきました。

「考えてみたらこれ、もう`vim-rspec`の機能は一つも使ってないな…」

## じゃあ素のvimscriptで良いのでは

というわけで冒頭の結論の記述になりました。

（この時、作成したパスの変数を`Dispatch`とつなげて展開させる方法がわからず、`Slack`の`vim-jp`ではじめて質問を投げてみたんですけど、一瞬で返答を貰えて、大変ありがたかったです。
`Vim`界は強いし、`vim-jp`も強かった。

元々rspecは、全テスト実行か特定行の実行かしかしないんで、これで必要充分です。

`vim`の勉強にもなったし、良い経験でした。

今後もvimを使い続けるのであれば、人様のプラグインを参考にしながら、自分でカスタマイズしたscriptを書いた方が、幸せになれるという事も多そうですね。