open_sessionで特異メソッドを定義するときの注意

open_sessionで特異メソッドを定義したら、変数の持ち主に注意しましょうというお話。


次のように定義したとします。

def user
  open_session do |u|
    def u.access_to_home
      get '/home/index'
    end
  end
end


まず、これはエラーなく実行できます。

get '/home/index'
assert_response :success


ところが、これはエラーしてしまいます。

@user = user
@user.access_to_home
assert_response :success   #=> NoMethodError: undefined method `success?' for nil:NilClass

assert_response内で呼びだそうとした@responseは、テストを実行するコントローラがもつ変数です。
一方、@user.access_to_homeのレスポンスを受ける変数は@userセッションがもっているもので、コントローラから見た@responseとは別物なのですね。(あるいは、コントローラ自体がひとつのセッションだと見做すこともできる)


@user.access_to_homeのレスポンスを見るには、userの定義内で呼び出してやる必要があります。

def user
  open_session do |u|
    def u.access_to_home
      get '/home/index'
      assert_response :success
    end
  end
end


今度はエラーなく実行できました。

@user = user
@user.access_to_home


@user.responseと明示してやる方法もあります。

def user
  open_session do |u|
    def u.access_to_home
      get '/home/index'
    end
  end
end
@user = user
@user.access_to_home
@user.response.success?

# こちらも実行可
@user.assert_response :success


もしくは、instance_evalとか。

def user
  open_session do |u|
    def u.access_to_home(&block)
      get '/home/index'

      instance_eval(&block) if block
    end
  end
end
@user = user
@user.access_to_home do
  assert_response :success
end