rails, rspecでVimのsnippetを使うためのベストプラクティス (自己流)

Vimにはtextmateちっくなsnippet補完を実現するためのプラグインがいくつかあります。
[neocomplcache-snippets-complete](https://github.com/Shougo/neocomplcache-snippets-complete), [snipMate](https://github.com/garbas/vim-snipmate), [UltiSnip](https://github.com/guns/ultisnips)などなど。

私が利用しているのは、neocomplcache-snippets-completeなので、それを利用する時の流れについての話です。
neocomplecacheを使っていると、補完が統合されるので、neocomplecacheユーザーは大体これに落ち着くと思います。
ちょっと調べたところだと、UltiSnipはかなり高機能っぽいのですが、pythonインターフェースが必要で、ちょっと複雑過ぎるかもしれません。

## 読み込みのルール
現在のneocomplecacheは、内部でvimのバッファが持っているファイルタイプとは別に、
独自のファイルタイプ情報を持っています。
最近になって(8月後半ぐらい)、NeoComplCacheSetFileTypeというコマンドが追加され、
neocomplecacheがどのfiletypeで補完を行うかを、vim本体のfiletypeとは独立して設定できるようになりました。

また、neocomplecacheでは複合ファイルタイプに対応しているので、
ruby.railsというファイルタイプを指定すれば、rubyというファイルタイプについての補完設定と、
railsというファイルタイプについての補完設定が動作するようになります。

一方で、railsで開発をしているとvim-railsというプラグインを使っていることが多いと思いますが、
そのプラグインがrailsで宣言的に記述するようなメソッド名をハイライトしてくれます。
しかし、vimのfiletypeをruby.railsにすると、そのハイライトが消えてしまいます。
ソースを読んだところ、filetypeがrubyじゃないと駄目みたいで、そういう仕様のようです。

なので、railsで書いている場合は出来れば複合ファイルタイプは使いたくない。
で、以前ならば、snippetを全部、ruby用のファイルに書くという手段が取れたのですが、
rails外でコード書いてる時にrailsのsnippetは必要無いわけで、どうもスマートではない。

そこで、NeoComplCacheSetFileTypeが活用できます。

## 設定例
vim-railsは、railsっぽいファイルを開くとb:rails_rootというバッファ変数を定義します。
この変数の有無でrailsかどうかを容易に判別できるので、これを利用します。

```vim
autocmd BufEnter * if exists("b:rails_root") | NeoComplCacheSetFileType ruby.rails | endif
autocmd BufEnter * if (expand("%") =~ "_spec\.rb$") || (expand("%") =~ "^spec.*\.rb$") | NeoComplCacheSetFileType ruby.rspec | endif
```

こんな感じで書いておけば、railsの時だけ、neocomplecacheのみrubyとrailsの両方の補完設定を活用できます。
下のrspec向けの設定は、ファイルパスでrspecかどうかを判断しています。
これで、ruby.snipに定義してあるsnippetも、rails.snipに定義してあるsnippetも、
望む通りの環境で利用することができます。

```vim
let g:neocomplcache_snippets_dir = $HOME . '/.vim/snippets'
nnoremap <Space>se :<C-U>NeoComplCacheEditSnippets<CR>
```

後はこんな感じで、snippetの格納場所を設定し、snippet編集用のコマンドをさっと呼べるようにしておけば、確認するのも楽です。


```vim
" SuperTab like snippets behavior.
imap <expr><TAB> neocomplcache#sources#snippets_complete#expandable() ? "\<Plug>(neocomplcache_snippets_expand)" : pumvisible() ? "\<C-n>" : "\<TAB>"
```

今のところ、snippetの展開については、neocomplecacheの設定例にあるようにこんな感じにしてます。

後は、私のsnippetについて張っておきます。
書式についてはマニュアルを参照してください。
railsのsnipはデフォルトで定義されているものがあるのですが、
どうも貧弱な感じなので、一旦削除して書き換えているものがあります。

一部、snipMateやUltiSnipを参考にパクってきてるものもあります。
rspec周りはコピペベースで、まだちょっと適当なのでご了承ください。

```snippet:ruby.snip
snippet encutf
abbr # coding: utf-8
    # coding: utf-8

alias encutf magic

snippet class
abbr    class end
    class ${1:name}
      ${2}
    end

snippet mod
abbr    module end
    module ${1:name}
      ${2}
    end

snippet defi
abbr    def initialize(...
    def initialize${1:(${2:args\})}
        ${3}
    end

snippet unless
abbr    unless end
    unless ${1:condition}
        ${2}
    end

snippet ife
abbr    if else end
    if ${1:condition}
        ${2}
    else
        ${3}
    end

snippet ifei
abbr    if elsif end
    if ${1:condition}
        ${2}
    elsif ${3:condition}
        ${4}
    end

snippet case
abbr    case when end
    case ${1:var}
    when ${2:expr}
      ${3}
    end

alias case sw

snippet inj
abbr    inject(n) do end
    inject(${1:initial}) do |${2:acc}, ${3:val}|
        ${4}
    end

snippet injp
abbr    inject(n, &:method)
    inject(${1:initial}, &:${2:method})

snippet eacho
abbr    each_with_object(obj) do end
    each_with_object(${1:memoobj}) do |${2:val}, ${3:memoobj}|
        ${4}
    end

snippet map
abbr    map do end
    map do |${1:var}|
        ${2}
    end

snippet mapp
abbr    map(&:method)
    map(&:${1:method})

snippet sortby
abbr    sort_by(&:method)
    sort_by(&:${1:method})

alias sortby sort_by

snippet sortbydo
abbr    sort_by do end
    sort_by do |${1:var}|
        ${2}
    end

snippet begin
abbr    begin rescue end
    begin
        ${1:# TODO}
    rescue
        ${2:# TODO}
    end
```

```snippet:rails.snip
snippet sc
abbr    scope :method, lambda
prev_word   '^'
    scope :${1}, lambda { ${2} }

snippet dsc
abbr    default_scope lambda
prev_word   '^'
    default_scope lambda { ${1} }

snippet bff
abbr    before_filter
prev_word   '^'
    before_filter :${1:method}

delete      hm
snippet     hm
abbr        has_many :objects
prev_word   '^'
    has_many :${1:objects}

alias hm has_m

snippet     hmo
abbr        has_many with options
prev_word   '^'
    has_many :${1:objects}${2:, class_name: "${3\}", foreign_key: "${4:reference\}_id"}

snippet     hmt
abbr        has_many :objects, :through
prev_word   '^'
    has_many :${1:objects}, through: ${2:relation}

delete      ho
snippet     ho
abbr        has_one :object
prev_word   '^'
    has_one :${1:object}

alias ho has_o

delete      bt
snippet     bt
abbr        belongs_to :object
prev_word   '^'
    belongs_to :${1:object}

alias bt bel

snippet bto
abbr        belongs_to with options
prev_word   '^'
    belongs_to :${1:object}${2:, class_name: "${3\}", foreign_key: "${4:reference\}_id"}

delete      logd
snippet     logd
abbr        logger.debug
    logger.debug(${1})

delete      logi
snippet     logi
abbr        logger.info
    logger.info(${1})

delete      logw
snippet     logw
abbr        logger.warn
    logger.warn(${1})

delete      loge
snippet     loge
abbr        logger.error
    logger.error(${1})

delete      logf
snippet     logf
abbr        logger.fatal
    logger.fatal(${1})

snippet     fla
abbr        flash[...]
prev_word   '^'
    flash[:${1:notice}] = "${2:Successfully created...}"

alias fla flash

snippet rep
abbr redirect_to (path)
    redirect_to ${1:model}_path${2:(${3:params\})}

delete      va
snippet     va
abbr        validates_associated
    validates_associated :${1:attr}

delete      vb
snippet     vb
abbr        validates_acceptance_of
    validates_acceptance_of :${1:attr}

delete      vc
snippet     vc
abbr        validates_confirmation_of
    validates_confirmation_of :${1:attr}

delete      ve
snippet     ve
abbr        validates_exclusion_of
    validates_exclusion_of :${1:attr}, in: ${2:%w( ${3:mov avi\} )}

delete      vf
snippet     vf
abbr        validates_format_of
    validates_format_of :${1:attr}, with: /${2:regexp}/

delete      vi
snippet     vi
abbr        validates_inclusion_of
    validates_inclusion_of :${1:attr}, in: ${2:%w( ${3:mov avi\} )}

delete      vl
snippet     vl
abbr        validates_length_of
    validates_length_of :${1:attr}, within: ${2:3..20}

delete      vn
snippet     vn
abbr        validates_numericality_of
    validates_numericality_of :${1:attr}

snippet     vng
abbr        validates_numericality_of greater_than: n
    validates_numericality_of :${1:attr}, greater_than: ${2:num}

snippet     vnl
abbr        validates_numericality_of less_than: n
    validates_numericality_of :${1:attr}, less_than: ${2:num}

delete      vp
snippet     vp
abbr        validates_presence_of
    validates_presence_of :${1:attr}

delete      vu
snippet     vu
abbr        validates_uniqueness_of
    validates_uniqueness_of :${1:attr}

snippet     aln
abbr        allow_nil
    allow_nil: ${1:true}

delete      ra
snippet     ra
abbr        render :action
    render action: ${1}

delete      rc
snippet     rc
abbr        render :controller
    render controller: ${1}

delete      rf
snippet     rf
abbr        render :file
    render file: ${1}

delete      ri
snippet     ri
abbr        render :inline
    render inline: ${1}

delete      rj
snippet     rj
abbr        render :json
    render json: ${1}

delete      rl
snippet     rl
abbr        render :layout
    render layout: ${1}

delete      rp
snippet     rp
abbr        render :partial
    render partial: ${1}

delete      rt
snippet     rt
abbr        render :text
    render text: ${1}

delete      rx
snippet     rx
abbr        render :xml
    render xml: ${1}

delete id

delete object

delete partial

delete      action
snippet     action
abbr        action: name
    action: ${1}

snippet     tzn
abbr        Time.zone.now
  Time.zone.now

snippet     res
abbr        resources
prev_word   '^'
    resources :${1:resources}

alias res resources

delete      rst
snippet     rst
abbr        respond_to
prev_word   '^'
    respond_to do |format|
        format.html${1: { ${2\} \}}
        format.json { render json: ${3} }
    end

snippet     befs
abbr        before_save
prev_word   '^'
    before_save :${1:method}

snippet     befc
abbr        before_create
prev_word   '^'
    before_create :${1:method}

snippet     befu
abbr        before_update
prev_word   '^'
    before_update :${1:method}

snippet     befv
abbr        before_validation
prev_word   '^'
    before_validation :${1:method}

snippet     befvc
abbr        before_validation_on_create
prev_word   '^'
    before_validation_on_create :${1:method}

snippet     befvu
abbr        before_validation_on_update
prev_word   '^'
    before_validation_on_update :${1:method}

snippet     befd
abbr        before_destroy
prev_word   '^'
    before_destroy :${1:method}

snippet     afts
abbr        after_save
prev_word   '^'
    after_save :${1:method}

snippet     aftc
abbr        after_create
prev_word   '^'
    after_create :${1:method}

snippet     aftu
abbr        after_update
prev_word   '^'
    after_update :${1:method}

snippet     aftv
abbr        after_validation
prev_word   '^'
    after_validation :${1:method}

snippet     aftvc
abbr        after_validation_on_create
prev_word   '^'
    after_validation_on_create :${1:method}

snippet     aftvu
abbr        after_validation_on_update
prev_word   '^'
    after_validation_on_update :${1:method}

snippet     aftd
abbr        after_destroy
prev_word   '^'
    after_destroy :${1:method}

snippet     try
abbr        try(:method)
    try(:${1:method})
```

```snippet:rspec.snip
snippet desc
abbr    describe end
prev_word '^'
    describe "${1:name}" do
        ${2}
    end

snippet desch
abbr    spec_helper describe
    require 'spec_helper'

    describe ${1:`Snippet_RubySpecNameFromFilename()`} do
        ${2}
    end

snippet desccon
    describe "${1:GET|POST|PUT|DELETE} ${2:/some/path}${3}" do
        ${4}
    end

snippet bef
    before do
        ${1}
    end

alias bef before

snippet befa
    before(:all) do
        ${1}
    end

alias befa beforeall

snippet aft
    after do
        ${1}
    end

alias aft after

snippet afta
    after(:all) do
        ${1}
    end

alias afta afterall

snippet con
    context "${1}" do
        ${2}
    end

alias con context

snippet cona
abbr context after callback
    context "after #${1:save!}" do
        before do
            subject.$1
        end
        ${2}
    end

snippet subjc
    subject { ${1:FactoryGirl.create(:${2:name\})} }

snippet subjb
    subject { ${1:FactoryGirl.build(:${2:name\})} }

snippet it
    it "${1}" do
        ${2}
    end

snippet itb
abbr        it {}
        it { ${1} }

snippet its
    it "should ${1:work correctly}" do
        ${2}
    end

snippet is
    it { should ${1} }

snippet isn
    it { should_not ${1} }

snippet sh
    should == ${1:value}
    ${2}

snippet shn
    should_not == ${1:value}
    ${2}

snippet shs
    should satisfy { |${1:obj}| ${2} }
    ${3}

snippet shp
    should be_${1:predicate}

alias shp shbe

snippet shh
    should have(${1:num}).${2:things}
    ${3}

snippet she
    should eq(${1:value})
    ${3}

snippet shne
    should_not eq(${1:value})
    ${2}

snippet shnredt
    response.should_not redirect_to(${1:url})
    ${2}

snippet shbw
    should be_within(${1:tolerance}).of(${2:result})
    ${3}

snippet shnbw
    should_not be_within(${1:tolerance}).of(${2:result})
    ${4}

snippet shhal
    should have_at_least(${1:num}).${2:things}
    ${3}

snippet shhi
    should have(${1:n}).records
    ${2}

snippet shns
    should_not satisfy { |${1:obj}| ${2} }
    ${3}

snippet shbko
    should be_a_kind_of(${1:class})
    ${2}

snippet shnbko
    should_not be_a_kind_of(${1:klass})
    ${2}

snippet shnbe
    .should_not be_${1:predicate}

snippet shre
    should raise_error(${1:error})
    ${2}

snippet shnre
    should_not raise_error(${1:error})
    ${2}

alias shre shraise

snippet shc
    expect {
        ${1}
    }.should change(${2:described_class}, :${3:count}).by(${4:1})

alias shc exshc

snippet shnc
    expect {
        ${1}
    }.should_not change(${2:target}, :${3:method})

snippet shrt
    should respond_to(:${1:sym})
    ${2}

snippet shnrt
    should_not respond_to(:${1:sym})
    ${2}

snippet shr
    should_receive(:${1:message})${2}
    ${3}

snippet shnr
    should_not_receive(:${1:message})${2}
    ${3}

snippet wia
    with(${1:args})
    ${2}

snippet shm
    should match(/${1:regexp}/)
    ${2}

snippet shnm
    should_not match(/${1:regexp}/)
    ${2}

snippet shredt
    response.should redirect_to(${1:url})
    ${2}

snippet shbr
    response.should be_redirect
    ${1}

snippet shnbr
    response.should_not be_redirect
    ${1}

snippet shbs
    response.should be_success
    ${1}

snippet shnbs
    response.should_not be_success
    ${1}

snippet shtemp
    response.should render_template(:${1:template})
    ${2}

snippet shbio
    should be_instance_of(${1:class})
    ${2}

snippet shnbio
    should_not be_instance_of(${1:klass})
    ${2}

snippet sc
abbr shared_context do end
    shared_context "${1:condition}" do
        ${2}
    end

alias sc shared_context

snippet scm
abbr shared_context with metadata
    shared_context "${1:condition}", :${2:key} => ${3:value} do
        ${4}
    end

snippet inc
abbr include_context
    include_context "${1}"

alias inc include_context

snippet se
abbr shared_examples do end
    shared_examples "${1:do something}" do
        ${2}
    end

alias se shared_examples

snippet sed
    shared_examples "${1:}" do
        describe "as $1" do
            ${2}
        end
    end

snippet ibl
abbr it_behaves_like
    it_behaves_like '${1}'

alias ibl it_behaves

snippet ine
abbr include_examples
    include_examples "${1}"

alias ine include_examples

snippet let
abbr let {}
    let(:${1}) { ${2} }

snippet let!
abbr let! {}
    let!(:${1}) { ${2} }

snippet letf
    let(:${1:model}) do 
        FactoryGirl.create(:${2:$1})
    end

snippet atl
    at_least(${1:n}).times

snippet atm
    at_most(${1:n}).times

snippet on
    once

snippet tw
    twice

snippet ber
    be_redirect

snippet ex
    exactly(${1:n}).times

alias ex exact

snippet annot
    any_number_of_times

snippet shham
    ${1:target}.should have_at_most(${2:num}).${3:things}
    ${4}

snippet ant
    and_throw(${1:sym})

snippet any
    and_yield(${1:values})

snippet mat
    RSpec::Matchers.define :${1:matcher_name} do
        match do |model|
            # return Boolean
            ${2}
        end
    end

alias mat matcher


snippet anr
    and_return(${1:value})

snippet anrb
    and_return { ${1} }

snippet anra
    and_raise(${1:exception})

snippet st
    stub(:${1:method}).and_returns(${2:return})

alias st stub

snippet sm
    stub_model(${1:model}, {${2}})
snippet mm
    mock_model(${1:model})${2}

snippet moc
    ${1:var} = mock("${2:mock_name}"${3:, :null_object => true})
    ${4}

snippet facb
    FactoryGirl.build(:${1})

snippet fac
    FactoryGirl(:${1}, ${2})

snippet facc
    FactoryGirl.create(:${1})

snippet facs
    sequence(:${1}) {|n| "${2}#{n}"}

snippet facn
    FactoryGirl.next(:${1:sequence-name})

snippet faca
    f.${1:model} {|a| a.association(:${2:$1})}

snippet facd
    FactoryGirl.define do
        ${1}
    end

snippet facf
    factory ${1:name} do
        ${2}
    end
```