rack_session_access 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rspec'
6
+ gem 'capybara'
7
+
8
+ gem 'rack'
9
+ gem 'sinatra'
10
+ gem 'rails'
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # rack_session_access
2
+
3
+ RackSessionAccess provides rack middleware for 'rack.session' environment management.
4
+
5
+ ## Problem
6
+
7
+ Acceptance testing assumes that you can't directly access application session.
8
+ For example if you use capybara with selenium webdriver you can't change some session value
9
+ because your test use browser that access application via backend server.
10
+
11
+ ## Solution
12
+
13
+ But if you still want to change session values?
14
+ Possible solution is inject into application some code that will manage session.
15
+ If you use rack based framework this gem does it!
16
+
17
+ ## Installation
18
+
19
+ gem install rack_session_access
20
+
21
+ ## Using with Rails3
22
+
23
+ Add to `Gemfile`:
24
+
25
+ gem 'rack_session_access'
26
+
27
+ Add to `config/application.rb`
28
+
29
+ module MyRailsApplication
30
+ class Application < Rails::Application
31
+ config.middleware.use RackSessionAccess::Middleware if Rails.env.test?
32
+ end
33
+ end
34
+
35
+ *Note* Ensure you include rack_session_access middleware only for test environment
36
+ otherwise you will have security issue.
37
+
38
+ If you use rspec you may prefer to inject middleware only for rspec tests:
39
+ Put into `spec/spec_helper`:
40
+
41
+ Rails.application.configure do
42
+ config.middleware.use RackSessionAccess::Middleware
43
+ end
44
+
45
+ ## Using with Sinatra
46
+
47
+ Add to your sinatra application:
48
+
49
+ class MySinatraApplication < Sinatra::Base
50
+ enable :sessions
51
+ use RackSessionAccess if environment == :test
52
+ ...
53
+ end
54
+
55
+ If you use rspec you may prefer to inject middleware only for rspec tests:
56
+ Put into `spec/spec_helper`:
57
+
58
+ MySinatraApplication.configure do
59
+ use RackSessionAccess::Middleware
60
+ end
61
+
62
+ ## Using with Rack builder
63
+
64
+ Rack::Builder.new do
65
+ ...
66
+ use Rack::Session::Cookie
67
+ use RackSessionAccess::Middleware
68
+ use MyRackApplication
69
+ end.to_app
70
+
71
+ ## Testing with Capybara
72
+
73
+ Add to `spec/spec_helper.rb`
74
+
75
+ require "rack_session_access/capybara"
76
+
77
+ And use `page.set_rack_session` to set your desired session data!
78
+
79
+ Example:
80
+
81
+ require 'spec_helper'
82
+
83
+ feature "My feature" do
84
+ background do
85
+ @user = Factory(:user, :email => 'jack@daniels.com')
86
+ end
87
+
88
+ scenario "logged in user access profile page" do
89
+ page.set_rack_session(:user_id => user.id)
90
+ page.visit "/profile"
91
+ page.should have_content("Hi, jack@daniels.com")
92
+ end
93
+ end
94
+
95
+
96
+ ## Notes
97
+
98
+ Thus we use marshalized data it's possible to set any ruby object into application session hash.
99
+
100
+ Enjoy!
101
+
102
+
103
+ ## References
104
+
105
+ * [capybara](https://github.com/jnicklas/capybara)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
1
+ require 'rack'
2
+
3
+ TestRackApp = Rack::Builder.new do
4
+ use Rack::Session::Cookie
5
+ use RackSessionAccess::Middleware
6
+ run lambda { |env| [200, { 'Content-Type' => 'text/plain'}, [ "DUMMY"] ] }
7
+ end.to_app
@@ -0,0 +1,35 @@
1
+ require 'rails'
2
+ require 'action_controller/railtie'
3
+
4
+ module TestRailsApp
5
+ class Application < Rails::Application
6
+ config.secret_token = '572c86f5ede338bd8aba8dae0fd3a326aabababc98d1e6ce34b9f5'
7
+
8
+ routes.draw do
9
+ get '/login' => 'test_rails_app/sessions#new'
10
+ post '/login' => 'test_rails_app/sessions#create'
11
+ get '/profile' => 'test_rails_app/profiles#show'
12
+ end
13
+ end
14
+
15
+ class SessionsController < ActionController::Base
16
+ def new
17
+ render :text => "Please log in"
18
+ end
19
+
20
+ def create
21
+ session[:user_email] = params[:user_email]
22
+ redirect_to '/profile'
23
+ end
24
+ end
25
+
26
+ class ProfilesController < ActionController::Base
27
+ def show
28
+ if user_email = session[:user_email]
29
+ render :text => "Welcome, #{user_email}!"
30
+ else
31
+ redirect_to '/login'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ require 'sinatra'
2
+
3
+ class TestSinatraApp < Sinatra::Base
4
+ enable :sessions
5
+
6
+ get '/login' do
7
+ body "Please log in"
8
+ end
9
+
10
+ post '/login' do
11
+ session[:user_email] = params[:user_email]
12
+ redirect to('/profile')
13
+ end
14
+
15
+ get '/profile' do
16
+ if user_email = session[:user_email]
17
+ body "Welcome, #{user_email}!"
18
+ else
19
+ redirect to('/login')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module RackSessionAccess
2
+ autoload :Middleware, 'rack_session_access/middleware'
3
+
4
+ class << self
5
+ # session resource path
6
+ attr_accessor :path
7
+
8
+ # session resource edit path
9
+ attr_accessor :edit_path
10
+
11
+ # encode session hash to string
12
+ def encode(hash)
13
+ Array(Marshal.dump(hash)).pack('m')
14
+ end
15
+
16
+ # decode string to session hash
17
+ def decode(string)
18
+ Marshal.load(string.unpack('m').first)
19
+ end
20
+
21
+ def configure
22
+ yield self
23
+ end
24
+ end
25
+ end
26
+
27
+ RackSessionAccess.configure do |config|
28
+ config.path = '/rack_session'
29
+ config.edit_path = '/rack_session/edit'
30
+ end
@@ -0,0 +1,16 @@
1
+ module RackSessionAccess
2
+ module Capybara
3
+ def set_rack_session(hash)
4
+ data = ::RackSessionAccess.encode(hash)
5
+
6
+ visit ::RackSessionAccess.edit_path
7
+ has_content?("Update rack session")
8
+ fill_in "data", :with => data
9
+ click_button "Update"
10
+ has_content?("Rack session data")
11
+ end
12
+ end
13
+ end
14
+
15
+ require 'capybara/session'
16
+ Capybara::Session.send :include, RackSessionAccess::Capybara
@@ -0,0 +1,136 @@
1
+ require 'rack/request'
2
+ require 'builder'
3
+
4
+ module RackSessionAccess
5
+ class Middleware
6
+ # Initialize RackSessionAccess middleware
7
+ #
8
+ # @param app a rack application
9
+ # @param options
10
+ #
11
+ # Options:
12
+ # * :key - rack session key
13
+ def initialize(app, options = {})
14
+ @app = app
15
+ @key = options[:key] || 'rack.session'
16
+ @routing = [
17
+ [ 'GET', RackSessionAccess.path, :show ],
18
+ [ 'GET', RackSessionAccess.edit_path, :edit ],
19
+ [ 'PUT', RackSessionAccess.path, :update ]
20
+ ]
21
+ end
22
+
23
+ def call(env)
24
+ return render(500) do |xml|
25
+ xml.h2("#{@key} env is not initialized")
26
+ end unless env[@key]
27
+
28
+ request = ::Rack::Request.new(env)
29
+
30
+ if action = dispatch_action(request)
31
+ send(action, request)
32
+ else
33
+ @app.call(env)
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ # List session data
40
+ def show(request)
41
+ # call inspect because session can be lazy loaded
42
+ request.env[@key].inspect
43
+
44
+ render do |xml|
45
+ xml.h2 "Rack session data"
46
+ xml.ul do |xml|
47
+ request.env[@key].each do |k,v|
48
+ xml.li("#{k.inspect} : #{v.inspect}")
49
+ end
50
+ end
51
+ xml.p do |xml|
52
+ xml.a("Edit", :href => action_path(:edit))
53
+ end
54
+ end
55
+ end
56
+
57
+ # Render form for submit new session data
58
+ def edit(request)
59
+ render do |xml|
60
+ xml.h2 "Update rack session"
61
+ xml.p "Put marshalized and encoded with base64 ruby hash into the form"
62
+ xml.form({
63
+ :action => action_path(:update),
64
+ :method => 'post',
65
+ :enctype => 'application/x-www-form-urlencoded'
66
+ }) do |xml|
67
+ xml.input(:type => 'hidden', :name =>'_method', :value => 'put')
68
+ xml.textarea("", :cols => 40, :rows => 10, :name => 'data')
69
+ xml.p do |xml|
70
+ xml.input(:type => 'submit', :value => "Update")
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # Update session data
77
+ def update(request)
78
+ begin
79
+ data = request.params['data']
80
+ hash = RackSessionAccess.decode(data)
81
+ hash.each { |k, v| request.env[@key][k] = v }
82
+ rescue => e
83
+ return render(400) do |xml|
84
+ xml.h2("Bad data #{data.inspect}: #{e.message} ")
85
+ end
86
+ end
87
+
88
+ redirect_to action_path(:show)
89
+ end
90
+
91
+ private
92
+
93
+ # Dispatch action from request
94
+ def dispatch_action(request)
95
+ method = request_method(request)
96
+ path = request.path
97
+ route = @routing.detect { |r| r[0] == method && r[1] == path }
98
+ route[2] if route
99
+ end
100
+
101
+ # Return HTTP method, detect emulated method with _method param
102
+ def request_method(request)
103
+ return request.request_method if request.request_method != 'POST'
104
+ return request.params['_method'].upcase if request.params['_method']
105
+ request.request_method
106
+ end
107
+
108
+ # @return path for given action name
109
+ def action_path(action)
110
+ @routing.detect { |r| r[2] == action }[1]
111
+ end
112
+
113
+ # @return redirect response to specified url
114
+ def redirect_to(url)
115
+ render(302, {"Location" => url}) do |xml|
116
+ xml.a "You are being redirected", :href => url
117
+ end
118
+ end
119
+
120
+ # @return html response
121
+ def render(code = 200, headers = {})
122
+ headers["Content-Type"] ||= "text/html"
123
+
124
+ builder = Builder::XmlMarkup.new(:indent => 2)
125
+
126
+ builder.html do |xml|
127
+ xml.body do |xml|
128
+ yield xml
129
+ end
130
+ end
131
+
132
+ [ code, headers, [builder.target!] ]
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module RackSessionAccess
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rack_session_access/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack_session_access"
7
+ s.version = RackSessionAccess::VERSION
8
+ s.authors = ["Andriy Yanko"]
9
+ s.email = ["andriy.yanko@gmail.com"]
10
+ s.homepage = "https://github.com/railsware/rack_session_access"
11
+ s.summary = %q{Rack middleware that provides access to rack.session environment}
12
+
13
+ s.rubyforge_project = "rack_session_access"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency "rack", ">=1.0.0"
21
+ s.add_runtime_dependency "builder", ">=2.0.0"
22
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples "common scenarios" do
4
+ scenario "changing session data" do
5
+ page.visit RackSessionAccess.edit_path
6
+ page.should have_content("Update rack session")
7
+
8
+ page.fill_in "data", :with => RackSessionAccess.encode({'user_id' => 1})
9
+ page.click_button "Update"
10
+ page.should have_content("Rack session data")
11
+ page.should have_content('"user_id" : 1')
12
+ page.current_path.should == RackSessionAccess.path
13
+ end
14
+
15
+ scenario "providing no session data" do
16
+ page.visit RackSessionAccess.edit_path
17
+ page.should have_content("Update rack session")
18
+
19
+ page.click_button "Update"
20
+ page.should have_content("Bad data")
21
+ page.current_path.should == RackSessionAccess.path
22
+ end
23
+
24
+ scenario "providing bad data" do
25
+ page.visit RackSessionAccess.edit_path
26
+ page.should have_content("Update rack session")
27
+
28
+ page.fill_in "data", :with => "qwertyuiop"
29
+ page.click_button "Update"
30
+ page.should have_content("Bad data")
31
+ page.current_path.should == RackSessionAccess.path
32
+ end
33
+
34
+ scenario "modify session data with set_rack_session helper" do
35
+ page.set_rack_session({
36
+ 'user_email' => 'jack@daniels.com',
37
+ 'user_profile' => { :age => 12 },
38
+ 'role_ids' => [1, 20, 30]
39
+ })
40
+ page.visit(RackSessionAccess.path)
41
+ page.should have_content('"user_email" : "jack@daniels.com"')
42
+ page.should have_content('"user_profile" : {:age=>12}')
43
+ page.should have_content('"role_ids" : [1, 20, 30]')
44
+ end
45
+ end
46
+
47
+ shared_examples "rack scenarios" do
48
+ scenario "accessing application" do
49
+ page.visit "/welcome"
50
+ page.text.should == "DUMMY"
51
+ end
52
+ end
53
+
54
+ shared_examples "sinatra scenarios" do
55
+ scenario "test application itself" do
56
+ page.visit "/login"
57
+ page.should have_content("Please log in")
58
+
59
+ page.visit "/profile"
60
+ page.should have_content("Please log in")
61
+ end
62
+
63
+ scenario "accessing application" do
64
+ page.visit "/profile"
65
+ page.text.should == "Please log in"
66
+
67
+ page.set_rack_session({:user_email => "jack@daniels.com"})
68
+
69
+ page.visit "/profile"
70
+ page.text.should == "Welcome, jack@daniels.com!"
71
+ end
72
+ end
73
+
74
+ shared_examples "rails scenarios" do
75
+ scenario "test application itself" do
76
+ page.visit "/login"
77
+ page.should have_content("Please log in")
78
+
79
+ page.visit "/profile"
80
+ page.should have_content("Please log in")
81
+ end
82
+
83
+ scenario "accessing application" do
84
+ page.visit "/profile"
85
+ page.text.should == "Please log in"
86
+
87
+ page.set_rack_session({:user_email => "jack@daniels.com"})
88
+
89
+ page.visit "/profile"
90
+ page.text.should == "Welcome, jack@daniels.com!"
91
+ end
92
+ end
93
+
94
+ feature "manage rack session", %q(
95
+ As developer
96
+ I want to have ability to modify rack session
97
+ So I can write faster tests
98
+ ) do
99
+
100
+ context "with rack application and :rack_test driver", :driver => :rack_test do
101
+ background do
102
+ Capybara.app = TestRackApp
103
+ Capybara.current_driver.should == :rack_test
104
+ end
105
+ include_examples "common scenarios"
106
+ include_examples "rack scenarios"
107
+ end
108
+
109
+ context "with rack application and :selenium driver", :driver => :selenium do
110
+ background do
111
+ Capybara.app = TestRackApp
112
+ Capybara.current_driver.should == :selenium
113
+ end
114
+ include_examples "common scenarios"
115
+ include_examples "rack scenarios"
116
+ end
117
+
118
+ context "with sinatra application and :rack_test driver", :driver => :rack_test do
119
+ background do
120
+ Capybara.app = TestSinatraApp
121
+ Capybara.current_driver.should == :rack_test
122
+ end
123
+ include_examples "common scenarios"
124
+ include_examples "sinatra scenarios"
125
+ end
126
+
127
+ context "with sinatra application and :selenium driver", :driver => :selenium do
128
+ background do
129
+ Capybara.app = TestSinatraApp
130
+ Capybara.current_driver.should == :selenium
131
+ end
132
+ include_examples "common scenarios"
133
+ include_examples "sinatra scenarios"
134
+ end
135
+
136
+ context "with rails application and :rack_test driver", :driver => :rack_test do
137
+ background do
138
+ Capybara.app = TestRailsApp::Application
139
+ Capybara.current_driver.should == :rack_test
140
+ end
141
+ include_examples "common scenarios"
142
+ include_examples "rails scenarios"
143
+ end
144
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe RackSessionAccess do
4
+ subject { described_class }
5
+
6
+ its(:path) { should == '/rack_session' }
7
+
8
+ its(:edit_path) { should == '/rack_session/edit' }
9
+
10
+ describe ".encode" do
11
+ it "should encode ruby hash to string" do
12
+ result = subject.encode( { 'a' => 'b' })
13
+ result.should be_kind_of(String)
14
+ end
15
+ end
16
+
17
+ describe ".decode" do
18
+ it "should decode marshalized and base64 encoded string" do
19
+ # Array(Marshal.dump({ :user_id => 100 })).pack("m")
20
+ # => "BAh7BjoMdXNlcl9pZGlp\n"
21
+ subject.decode("BAh7BjoMdXNlcl9pZGlp\n").should == { :user_id => 100 }
22
+ end
23
+ end
24
+
25
+ it "should encode and decode value" do
26
+ source = { 'klass' => Class, :id => 100, '1' => 2 }
27
+ data = subject.encode(source)
28
+ result = subject.decode(data)
29
+ result.should == source
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'capybara/rspec'
2
+
3
+ require 'rack_session_access'
4
+ require 'rack_session_access/capybara'
5
+
6
+ Dir[File.expand_path('../../apps/*.rb', __FILE__)].each do |f|
7
+ require f
8
+ end
9
+
10
+ TestSinatraApp.configure do |app|
11
+ app.environment = :test
12
+ app.use RackSessionAccess::Middleware
13
+ end
14
+
15
+ TestRailsApp::Application.configure do |app|
16
+ app.middleware.use RackSessionAccess::Middleware
17
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack_session_access
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andriy Yanko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: &21097300 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *21097300
25
+ - !ruby/object:Gem::Dependency
26
+ name: builder
27
+ requirement: &21096800 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *21096800
36
+ description:
37
+ email:
38
+ - andriy.yanko@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - README.md
46
+ - Rakefile
47
+ - apps/test_rack_app.rb
48
+ - apps/test_rails_app.rb
49
+ - apps/test_sinatra_app.rb
50
+ - lib/rack_session_access.rb
51
+ - lib/rack_session_access/capybara.rb
52
+ - lib/rack_session_access/middleware.rb
53
+ - lib/rack_session_access/version.rb
54
+ - rack_session_access.gemspec
55
+ - spec/middleware_spec.rb
56
+ - spec/rack_session_access_spec.rb
57
+ - spec/spec_helper.rb
58
+ homepage: https://github.com/railsware/rack_session_access
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: rack_session_access
78
+ rubygems_version: 1.8.10
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Rack middleware that provides access to rack.session environment
82
+ test_files:
83
+ - spec/middleware_spec.rb
84
+ - spec/rack_session_access_spec.rb
85
+ - spec/spec_helper.rb
86
+ has_rdoc: