Permalink
Browse files

Initial commit

  • Loading branch information...
ayanko committed Jan 7, 2012
0 parents commit 35fd0216cdce7fceba22ddf198f7b66d4afa76c7
@@ -0,0 +1,4 @@
*.gem
.bundle
Gemfile.lock
pkg/*
10 Gemfile
@@ -0,0 +1,10 @@
source "http://rubygems.org"
gemspec
gem 'rspec'
gem 'capybara'
gem 'rack'
gem 'sinatra'
gem 'rails'
105 README.md
@@ -0,0 +1,105 @@
# rack_session_access
RackSessionAccess provides rack middleware for 'rack.session' environment management.
## Problem
Acceptance testing assumes that you can't directly access application session.
For example if you use capybara with selenium webdriver you can't change some session value
because your test use browser that access application via backend server.
## Solution
But if you still want to change session values?
Possible solution is inject into application some code that will manage session.
If you use rack based framework this gem does it!
## Installation
gem install rack_session_access
## Using with Rails3
Add to `Gemfile`:
gem 'rack_session_access'
Add to `config/application.rb`
module MyRailsApplication
class Application < Rails::Application
config.middleware.use RackSessionAccess::Middleware if Rails.env.test?
end
end
*Note* Ensure you include rack_session_access middleware only for test environment
otherwise you will have security issue.
If you use rspec you may prefer to inject middleware only for rspec tests:
Put into `spec/spec_helper`:
Rails.application.configure do
config.middleware.use RackSessionAccess::Middleware
end
## Using with Sinatra
Add to your sinatra application:
class MySinatraApplication < Sinatra::Base
enable :sessions
use RackSessionAccess if environment == :test
...
end
If you use rspec you may prefer to inject middleware only for rspec tests:
Put into `spec/spec_helper`:
MySinatraApplication.configure do
use RackSessionAccess::Middleware
end
## Using with Rack builder
Rack::Builder.new do
...
use Rack::Session::Cookie
use RackSessionAccess::Middleware
use MyRackApplication
end.to_app
## Testing with Capybara
Add to `spec/spec_helper.rb`
require "rack_session_access/capybara"
And use `page.set_rack_session` to set your desired session data!
Example:
require 'spec_helper'
feature "My feature" do
background do
@user = Factory(:user, :email => 'jack@daniels.com')
end
scenario "logged in user access profile page" do
page.set_rack_session(:user_id => user.id)
page.visit "/profile"
page.should have_content("Hi, jack@daniels.com")
end
end
## Notes
Thus we use marshalized data it's possible to set any ruby object into application session hash.
Enjoy!
## References
* [capybara](https://github.com/jnicklas/capybara)
@@ -0,0 +1 @@
require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
require 'rack'
TestRackApp = Rack::Builder.new do
use Rack::Session::Cookie
use RackSessionAccess::Middleware
run lambda { |env| [200, { 'Content-Type' => 'text/plain'}, [ "DUMMY"] ] }
end.to_app
@@ -0,0 +1,35 @@
require 'rails'
require 'action_controller/railtie'
module TestRailsApp
class Application < Rails::Application
config.secret_token = '572c86f5ede338bd8aba8dae0fd3a326aabababc98d1e6ce34b9f5'
routes.draw do
get '/login' => 'test_rails_app/sessions#new'
post '/login' => 'test_rails_app/sessions#create'
get '/profile' => 'test_rails_app/profiles#show'
end
end
class SessionsController < ActionController::Base
def new
render :text => "Please log in"
end
def create
session[:user_email] = params[:user_email]
redirect_to '/profile'
end
end
class ProfilesController < ActionController::Base
def show
if user_email = session[:user_email]
render :text => "Welcome, #{user_email}!"
else
redirect_to '/login'
end
end
end
end
@@ -0,0 +1,22 @@
require 'sinatra'
class TestSinatraApp < Sinatra::Base
enable :sessions
get '/login' do
body "Please log in"
end
post '/login' do
session[:user_email] = params[:user_email]
redirect to('/profile')
end
get '/profile' do
if user_email = session[:user_email]
body "Welcome, #{user_email}!"
else
redirect to('/login')
end
end
end
@@ -0,0 +1,30 @@
module RackSessionAccess
autoload :Middleware, 'rack_session_access/middleware'
class << self
# session resource path
attr_accessor :path
# session resource edit path
attr_accessor :edit_path
# encode session hash to string
def encode(hash)
Array(Marshal.dump(hash)).pack('m')
end
# decode string to session hash
def decode(string)
Marshal.load(string.unpack('m').first)
end
def configure
yield self
end
end
end
RackSessionAccess.configure do |config|
config.path = '/rack_session'
config.edit_path = '/rack_session/edit'
end
@@ -0,0 +1,16 @@
module RackSessionAccess
module Capybara
def set_rack_session(hash)
data = ::RackSessionAccess.encode(hash)
visit ::RackSessionAccess.edit_path
has_content?("Update rack session")
fill_in "data", :with => data
click_button "Update"
has_content?("Rack session data")
end
end
end
require 'capybara/session'
Capybara::Session.send :include, RackSessionAccess::Capybara
@@ -0,0 +1,136 @@
require 'rack/request'
require 'builder'
module RackSessionAccess
class Middleware
# Initialize RackSessionAccess middleware
#
# @param app a rack application
# @param options
#
# Options:
# * :key - rack session key
def initialize(app, options = {})
@app = app
@key = options[:key] || 'rack.session'
@routing = [
[ 'GET', RackSessionAccess.path, :show ],
[ 'GET', RackSessionAccess.edit_path, :edit ],
[ 'PUT', RackSessionAccess.path, :update ]
]
end
def call(env)
return render(500) do |xml|
xml.h2("#{@key} env is not initialized")
end unless env[@key]
request = ::Rack::Request.new(env)
if action = dispatch_action(request)
send(action, request)
else
@app.call(env)
end
end
protected
# List session data
def show(request)
# call inspect because session can be lazy loaded
request.env[@key].inspect
render do |xml|
xml.h2 "Rack session data"
xml.ul do |xml|
request.env[@key].each do |k,v|
xml.li("#{k.inspect} : #{v.inspect}")
end
end
xml.p do |xml|
xml.a("Edit", :href => action_path(:edit))
end
end
end
# Render form for submit new session data
def edit(request)
render do |xml|
xml.h2 "Update rack session"
xml.p "Put marshalized and encoded with base64 ruby hash into the form"
xml.form({
:action => action_path(:update),
:method => 'post',
:enctype => 'application/x-www-form-urlencoded'
}) do |xml|
xml.input(:type => 'hidden', :name =>'_method', :value => 'put')
xml.textarea("", :cols => 40, :rows => 10, :name => 'data')
xml.p do |xml|
xml.input(:type => 'submit', :value => "Update")
end
end
end
end
# Update session data
def update(request)
begin
data = request.params['data']
hash = RackSessionAccess.decode(data)
hash.each { |k, v| request.env[@key][k] = v }
rescue => e
return render(400) do |xml|
xml.h2("Bad data #{data.inspect}: #{e.message} ")
end
end
redirect_to action_path(:show)
end
private
# Dispatch action from request
def dispatch_action(request)
method = request_method(request)
path = request.path
route = @routing.detect { |r| r[0] == method && r[1] == path }
route[2] if route
end
# Return HTTP method, detect emulated method with _method param
def request_method(request)
return request.request_method if request.request_method != 'POST'
return request.params['_method'].upcase if request.params['_method']
request.request_method
end
# @return path for given action name
def action_path(action)
@routing.detect { |r| r[2] == action }[1]
end
# @return redirect response to specified url
def redirect_to(url)
render(302, {"Location" => url}) do |xml|
xml.a "You are being redirected", :href => url
end
end
# @return html response
def render(code = 200, headers = {})
headers["Content-Type"] ||= "text/html"
builder = Builder::XmlMarkup.new(:indent => 2)
builder.html do |xml|
xml.body do |xml|
yield xml
end
end
[ code, headers, [builder.target!] ]
end
end
end
@@ -0,0 +1,3 @@
module RackSessionAccess
VERSION = "0.0.1"
end
Oops, something went wrong.

0 comments on commit 35fd021

Please sign in to comment.