Vim で快適 Ruby 開発環境

仕事で Rubyのバッチ処理を書かなければならず、Neovim上で開発環境を 整えてみました。

NeovimからRubyの言語リファレンスを参照する

そもそも、真面目に Ruby を仕事で書くのは初めてなので、言語リファレンス が編集環境から参照できると便利なので整備してみました。

Rubyの言語リファレンスは refe2 という日本語で提供されてるリファレンスが あります。

導入方法は、gem の環境が導入されて入れば以下の通りです。

$ gem install refe2       # refe2のインストール
$ bitclust setup          # refeのセットアップ
$ refe Array push         # 試しにArray#pushのリファレンスを表示
Array#push
--- push(*obj)        -> self

指定された obj を順番に配列の末尾に追加します。
引数を指定しなければ何もしません。

うまく動いているようなので、Neovimから参照できるようプラグインを導入します。 私はプラグイン・マネージャーにdeinを使っているので、以下の通りです。

[[plugins]]
repo = 'thinca/vim-ref'

Neovimからは、コマンドモードで、 :Ref refe Array push などとやると画面 が分割されてリファレンスが参照できるようになります。

Refe

vim-ref

Neovimから非同期で静的テスト

Ruby の静的テスト、インスペクションツールと言えば Rubocupです。

Rubocop 自体は gemでインストールできます。まとめて、Rakeタスクを 設定して bundle exec rake rubocop で全体を検証できるようにして いますが、コーディング時にも検証できた方が便利です。

これには、Vim のプラグインで Asynchronous Lint Engine (以下、ALE)を使用します。 ALE は予め多くの言語とそのチェックツールに対応しており、 Ruby/Rubocop も 標準で組み込まれているので、導入するだけでチェックを有効にできます。

# --------------------------------------------
#  ALE
#   Asynchronous Lint Engine
#   非同期コードチェック
# --------------------------------------------
[[plugins]]
repo = 'w0rp/ale'
hook_add = '''
    let g:ale_sign_column_always = 1
    let g:ale_sign_error = '>>'
    let g:ale_sign_warning = '--'
'''

導入すると、非同期でRubocopを実行してくれるので、コードを書いていくと 次々とチェックして指摘を表示してくれます。指摘は行頭にマークをつける ことで示され、カーソルを合わせるとコマンドライン下部にエラー内容を表示してくれ ます。

ALE

Asynchronous Lint Engine

ほぼリアルタイムに検証してくれるので、書きながら都度修正を加えていくことができ るため、非常に生産性が上がります。

Vimでリファクタリングのサポート

Ruby で Rubocop をかけていると、コード改善のためにリファクタリングが必要と なってくる場合があります。RubyMineのような統合開発環境を使っていると リファクタリングの支援機能がありますが、同様な機能を提供する vim-ruby-refactoring を導入してみました。

導入は例によって dein を使って遅延読み込みの設定で導入しました。

# --------------------------------------------
# Ruby Refactoring
# --------------------------------------------
[[plugins]]
repo = 'ecomba/vim-ruby-refactoring'
on_ft = 'ruby'

いくつかの機能が用意されていますが、私が使うのは以下のものだけです。

Convert Post Conditional

Rubocop では 一行の条件であれば ifブロックでなく一行にまとめろと指摘されます。 これを :RConvertPostConditional とタイプするだけで自動的に変換してくれます

# ダメな例
if conditional
    do_something
end

# 改善した例
do_something if conditional

Extract Constant

コードに埋め込んだリテラルもよく注意されます。 :RExtractConstant を使うとリテラルを Constant 変数にくくり出してくれます。

# ダメな例
class MailChecker

  def checkmail(mail)
    if mail == "gmail.com"
      do_something
      do_something2
    end
  end

end


# 改善例
class MailChecker

  GMAIL = "gmail.com"

  def checkmail(mail)
    if mail == GMAIL
      do_something
      do_something2
    end
  end

end

Extract Method

実は一番使用するのが、コードをメソッドにくくり出してくれる :RExtractMethod です。Rubocopの指摘でメソッドが長すぎるとか、ABCSizeが大きすぎるなどの指摘 への対応は、ほぼメソッドを分割することになるからです。

:RExtractMethod を使うと選択した範囲を削除し、新たに別メソッドを生成し 削除した内容を貼り付けてくれます。もともとコードが書かれていた場所には生成した メソッドを呼び出すコードを貼り付けてくれます。

ただ、生成したメソッドに引き渡す引数などはあまり精度がよくないので、生成後に 手動で修正する必要はありますが。

ctags + vim-tags で関数を行き来する

テストコードを書いていると、テストコードと本体のコードの間を参照のため に何度も行き来する必要があります。

Vimでは 多くの言語に対応したctagの出力するtagファイルを利用して、関数の 定義にジャンプすることができます。

Macの場合、導入されているctagsが古いので最新のバージョンを導入します。

$ brew install ctags

そして、毎回ctagsを実行するのが面倒なので、自動化してくれる vim-tagsという プラグインを導入します。

# --------------------------------------------
# vim-tags
#   Tagを操る
# --------------------------------------------
    [[plugins]]
    repo = 'szw/vim-tags'
    hook_add = '''
        let g:vim_tags_project_tags_command = "/usr/local/bin/ctags -R {OPTIONS} {DIRECTORY} 2>/dev/null"
        let g:vim_tags_gems_tags_command = "/usr/local/bin/ctags -R {OPTIONS} `bundle show --paths` 2>/dev/null"
    '''

最初に :TagsGenerate コマンドでctagsを読み出せば、あとは自動的にファイルを 更新するたびに再生成してくれます。

tagファイルを生成しておけば、 <C-]> で関数定義に飛ぶことができます。

まとめ

上記で紹介したものの他に、deoplete などでコード補完もやってくれます。

当初はRubyMineを導入しようかと思ったのですが、Neovimだけで以外にもかなり 生産性の高い開発環境ができてしまいましたので、慣れ親しんだエディタから から別のツールを使う理由がなくなりました。

他にもRuby 開発を支援してくれるツールはありそうですが、とりあえずここまで。


Other articles