rack_session_access 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: