Rails::SessionCookie
Fast, loosely coupled requests specs for a cookie-authenticated application.
Why
Probably, you might have seen a lot code like this:
# config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store
# authentificaing method (maybe Devise or whatever)
session[:current_user_id] = current_user.id
# somewhere in helper for request specs
def login(current_user)
post '/login', auth_data(current_user)
end
# now every request spec is calling login request
RSpec.describe 'User inferface', type: :request do
let(:user) { create :user }
before do
login(user)
end
it 'shows private data' do
get '/dashboard'
end
end
In a usual user-driven application this tightly couples all request specs, which require authentication, to the login process. If it fails - everything fails. If it's not blazingly fast - it slows the whole suite down.
One may move to token-based authentification, especially when having API. That's reasonable and nice. But we can think about a session cookie as a token passed in a special header!
This gem replaces your usual process with the simplest 2 rails middleware pass. Rails is modular, that's cool :)
Installation
# Gemfile
gem 'rails-session_cookie', group: :test
Usage
# spec_helper.rb
require 'rails/session_cookie'
def login(current_user)
# depending on Rails version and session configuration this looks like "cookie_store_key=data--digest; path=/; HttpOnly"
= Rails::SessionCookie::App.new(current_user_id: current_user.id).
# note, it's raw, not `<<`
.merge()
end
# ...everything else the same
Now you can cache raw_session_cookie globally or per-thread depending on current_user_id to get things even faster!
You can also use the raw_session_cookie directly like this:
get "/", {}, { "HTTP_COOKIE" => }
Strictly speaking, you may cache Set-Cookie response header from /login URL to achieve same speed (but not coupling ;)
However, never saw this in practice, and consider caching of requests in before-phase bad. YMMV.
Advanced usage
If you need more sophisticated logic:
auth_app = proc { |env|
# do your magic
env[Rails::SessionCookie::RACK_SESSION].merge!(context)
[200, {}, []]
}
= Rails::SessionCookie::App.new(auth_app).
end
Of course, you can just make use (and reuse!) of as many procs as you wish.
This effectively achieves the effect as this PR#18230, which allows session mutation in a less invasive way in regard to Rails itself ;)
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/razum2um/rails-session_cookie.

