rack-state 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +53 -19
- data/lib/rack/state.rb +13 -7
- data/spec/spec_state.rb +64 -13
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -90,30 +90,32 @@ code sets up Rack::State to function similarly to Rack::Session.
|
|
90
90
|
|
91
91
|
=== Multiple state management with different storage adapters
|
92
92
|
|
93
|
-
Below, the
|
94
|
-
state in files under "tmp/
|
95
|
-
"/store" requires encryption it is wise to secure the token.
|
93
|
+
Below, the _flash_ state will use the default Memory store and _store_ will
|
94
|
+
save state in files under "tmp/state-store" in the project's root.
|
95
|
+
Additionally, if "/store" requires encryption it is wise to secure the token.
|
96
96
|
|
97
|
-
use Rack::State, key: '
|
98
|
-
use Rack::State, key: '
|
99
|
-
store: Rack::State::Store::File.new('tmp/
|
97
|
+
use Rack::State, key: 'flash', path: '/', max_age: 60*60
|
98
|
+
use Rack::State, key: 'store', path: '/store', secure: true,
|
99
|
+
store: Rack::State::Store::File.new('tmp/state-store')
|
100
100
|
|
101
101
|
Then on the application side you may have some helper methods like this:
|
102
102
|
|
103
|
+
def flash
|
104
|
+
env['rack.state.flash'].get or env['rack.state.flash'].set Flash.new
|
105
|
+
end
|
106
|
+
|
103
107
|
def store
|
104
108
|
env['rack.state.store']
|
105
109
|
end
|
106
110
|
|
107
111
|
store.set SecureStore.new # start shopping session
|
108
|
-
store.get.cart.add :item7 # buy some stuff
|
109
|
-
store.delete # remove from client and server after checkout
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
end
|
113
|
+
store.get.cart.add :item7
|
114
|
+
flash.notice 'Added Item 7'
|
114
115
|
|
115
|
-
|
116
|
-
|
116
|
+
store.get.transaction.process
|
117
|
+
flash.notice 'Transaction successful'
|
118
|
+
store.delete # remove from client/server after checkout
|
117
119
|
|
118
120
|
|
119
121
|
=== Choose a specific storage adapter depending on the environment
|
@@ -129,15 +131,37 @@ store for development and testing.
|
|
129
131
|
end
|
130
132
|
|
131
133
|
|
134
|
+
=== Keep state of an arbitrary object using helper methods
|
135
|
+
|
136
|
+
First setup the middleware key and helpers.
|
137
|
+
|
138
|
+
use Rack::State, key: 'myobj'
|
139
|
+
|
140
|
+
def my_object
|
141
|
+
env['rack.state.myobj'].get
|
142
|
+
end
|
143
|
+
|
144
|
+
def set_my_object(obj)
|
145
|
+
obj ? env['rack.state.myobj'].set(obj) : env['rack.state.myobj'].delete
|
146
|
+
end
|
147
|
+
|
148
|
+
Then get state tracking in your app.
|
149
|
+
|
150
|
+
set_my_object ObjectA.new # instance of ObjectA persistently stored
|
151
|
+
my_object.do_something # interact with original instance
|
152
|
+
set_my_object nil # remove instance from state store
|
153
|
+
set_my_object ObjectB.new # do it all again with another object
|
154
|
+
|
155
|
+
|
132
156
|
== Install, Test & Contribute
|
133
157
|
|
134
158
|
Install the gem:
|
135
159
|
|
136
160
|
$ sudo gem install rack-state
|
137
161
|
|
138
|
-
Or clone the
|
162
|
+
Or clone the Mercurial repository:
|
139
163
|
|
140
|
-
|
164
|
+
$ hg clone http://code.pachl.us/rack-state
|
141
165
|
|
142
166
|
State is tested with Christian Neukirchen's awesome test framework,
|
143
167
|
{Bacon}[https://github.com/chneukirchen/bacon].
|
@@ -157,6 +181,13 @@ After you fix a bug or develop a storage adapter, submit your code and tests.
|
|
157
181
|
|
158
182
|
TODO
|
159
183
|
|
184
|
+
== Links
|
185
|
+
|
186
|
+
Homepage :: http://ecentryx.com/gems/rack-state
|
187
|
+
Ruby Gem :: https://rubygems.org/gems/rack-state
|
188
|
+
Source Code :: https://bitbucket.org/pachl/rack-state/src
|
189
|
+
Bug Tracker :: https://bitbucket.org/pachl/rack-state/issues
|
190
|
+
|
160
191
|
== Compatibility
|
161
192
|
|
162
193
|
Rack::State was developed and tested on
|
@@ -166,13 +197,16 @@ and Rack[https://github.com/rack/rack] 1.5.
|
|
166
197
|
|
167
198
|
== History
|
168
199
|
|
169
|
-
|
200
|
+
1. 2013-09-05, v0.0.0: Initial design, development and testing
|
201
|
+
2. 2013-09-22, v0.0.1: First public release
|
202
|
+
3. 2013-09-22, v0.0.2: Update Gem info and documentation
|
203
|
+
4. 2014-04-20, v0.0.3: Add homepage and bug tracker URLs
|
170
204
|
|
171
|
-
|
205
|
+
== License
|
172
206
|
|
173
|
-
|
207
|
+
({ISC License}[http://opensource.org/licenses/ISC])
|
174
208
|
|
175
|
-
Copyright (c) 2013, Clint Pachl <
|
209
|
+
Copyright (c) 2013, Clint Pachl <pachl@ecentryx.com>
|
176
210
|
|
177
211
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
178
212
|
with or without fee is hereby granted, provided that the above copyright notice
|
data/lib/rack/state.rb
CHANGED
@@ -16,7 +16,7 @@ module Rack
|
|
16
16
|
#
|
17
17
|
class State
|
18
18
|
|
19
|
-
VERSION = '0.0.
|
19
|
+
VERSION = '0.0.3'
|
20
20
|
|
21
21
|
#
|
22
22
|
# == Middleware Options
|
@@ -44,12 +44,18 @@ module Rack
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def call(env)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
request = Request.new(env)
|
48
|
+
|
49
|
+
if request.path =~ /^#{@options[:path]}/
|
50
|
+
token = request.cookies.fetch(@key, nil)
|
51
|
+
state = env[@skey] = Manager.new(@store, token, @key, @options)
|
52
|
+
status, headers, body = @app.call(env)
|
53
|
+
resp = Response.new(body, status, headers)
|
54
|
+
state.finish(resp)
|
55
|
+
resp.finish
|
56
|
+
else
|
57
|
+
@app.call(env)
|
58
|
+
end
|
53
59
|
end
|
54
60
|
|
55
61
|
#
|
data/spec/spec_state.rb
CHANGED
@@ -16,6 +16,10 @@ module StateMiddlewareHelpers #:nodoc:
|
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
|
+
def key
|
20
|
+
'token' # default key name
|
21
|
+
end
|
22
|
+
|
19
23
|
def get_state(key, token)
|
20
24
|
@req.get("/get_state?key=#{key}", {'HTTP_COOKIE' => token})['Set-Cookie']
|
21
25
|
end
|
@@ -32,7 +36,7 @@ module StateMiddlewareHelpers #:nodoc:
|
|
32
36
|
def mock_app
|
33
37
|
Proc.new do |env|
|
34
38
|
req = Rack::Request.new(env)
|
35
|
-
key = req.params['key'] ||
|
39
|
+
key = req.params['key'] || self.key
|
36
40
|
skey = 'rack.state.' + key
|
37
41
|
data = nil
|
38
42
|
|
@@ -50,13 +54,32 @@ module StateMiddlewareHelpers #:nodoc:
|
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
57
|
+
module StateMiddlewarePathHelpers #:nodoc:
|
58
|
+
def allow(path)
|
59
|
+
should.not.raise(SecurityError) { @req.get(path) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def disallow(path)
|
63
|
+
should.raise(SecurityError) { @req.get(path) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def mock_app
|
67
|
+
Proc.new do |env|
|
68
|
+
if env['rack.state.token'].nil?
|
69
|
+
raise SecurityError, 'state unavailable at path'
|
70
|
+
else
|
71
|
+
Rack::Response.new.finish
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
53
77
|
describe Rack::State do
|
54
78
|
|
55
79
|
describe 'Default Options' do
|
56
80
|
extend StateMiddlewareHelpers
|
57
81
|
|
58
82
|
before do
|
59
|
-
@key = 'token' # default key name
|
60
83
|
@req = Rack::MockRequest.new(Rack::State.new(mock_app))
|
61
84
|
end
|
62
85
|
|
@@ -65,37 +88,37 @@ describe Rack::State do
|
|
65
88
|
end
|
66
89
|
|
67
90
|
should 'return token when setting state' do
|
68
|
-
set_state(
|
91
|
+
set_state(key).should.be.a valid_token
|
69
92
|
end
|
70
93
|
|
71
94
|
should 'return previous token' do
|
72
|
-
token1 = set_state(
|
73
|
-
token2 = get_state(
|
95
|
+
token1 = set_state(key)
|
96
|
+
token2 = get_state(key, token1)
|
74
97
|
token1.should.equal token2
|
75
98
|
end
|
76
99
|
|
77
100
|
should 'not return token after deleting state' do
|
78
|
-
token1 = set_state(
|
79
|
-
token2 = del_state(
|
80
|
-
token3 = get_state(
|
101
|
+
token1 = set_state(key)
|
102
|
+
token2 = del_state(key, token1)
|
103
|
+
token3 = get_state(key, token1)
|
81
104
|
token1.should.be.a valid_token
|
82
105
|
token2.should.be.an expired_token
|
83
106
|
token3.should.be.nil
|
84
107
|
end
|
85
108
|
|
86
109
|
should 'return no token when getting state with nonexistent token' do
|
87
|
-
get_state(
|
110
|
+
get_state(key, "#{key}=nonexistent").should.be.nil
|
88
111
|
end
|
89
112
|
|
90
113
|
should 'return new token when setting state with nonexistent token' do
|
91
|
-
token = set_state(
|
92
|
-
token.should.not.equal "#{
|
114
|
+
token = set_state(key, "#{key}=nonexistent")
|
115
|
+
token.should.not.equal "#{key}=nonexistent"
|
93
116
|
token.should.be.a valid_token
|
94
117
|
end
|
95
118
|
|
96
119
|
should 'expire token when deleting state with nonexistent token' do
|
97
|
-
token = del_state(
|
98
|
-
token.should.not.equal "#{
|
120
|
+
token = del_state(key, "#{key}=nonexistent")
|
121
|
+
token.should.not.equal "#{key}=nonexistent"
|
99
122
|
token.should.be.an expired_token
|
100
123
|
end
|
101
124
|
end
|
@@ -171,4 +194,32 @@ describe Rack::State do
|
|
171
194
|
mt.should.not.equal ft
|
172
195
|
end
|
173
196
|
end
|
197
|
+
|
198
|
+
describe 'Path Restrictions' do
|
199
|
+
extend StateMiddlewarePathHelpers
|
200
|
+
|
201
|
+
should 'allow state access for any path by default' do
|
202
|
+
@req = Rack::MockRequest.new(Rack::State.new(mock_app))
|
203
|
+
allow '/'
|
204
|
+
allow '/a'
|
205
|
+
allow '/a/b'
|
206
|
+
end
|
207
|
+
|
208
|
+
should 'allow state access for specified path only' do
|
209
|
+
@req = Rack::MockRequest.new(Rack::State.new(mock_app, path:'/a'))
|
210
|
+
allow '/a'
|
211
|
+
allow '/a/b'
|
212
|
+
disallow '/'
|
213
|
+
disallow '/b'
|
214
|
+
end
|
215
|
+
|
216
|
+
should 'allow state access for specified multi-level path only' do
|
217
|
+
@req = Rack::MockRequest.new(Rack::State.new(mock_app, path:'/a/b'))
|
218
|
+
allow '/a/b'
|
219
|
+
allow '/a/b/c'
|
220
|
+
disallow '/'
|
221
|
+
disallow '/a'
|
222
|
+
disallow '/b'
|
223
|
+
end
|
224
|
+
end
|
174
225
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-state
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-04-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -71,7 +71,7 @@ files:
|
|
71
71
|
- spec/spec_state_store_file.rb
|
72
72
|
- spec/spec_state_store_memory.rb
|
73
73
|
- spec/spec_state_store_postgres.rb
|
74
|
-
homepage: http://
|
74
|
+
homepage: http://ecentryx.com/gems/rack-state
|
75
75
|
licenses:
|
76
76
|
- ISC
|
77
77
|
post_install_message:
|