rack-state 0.0.2 → 0.0.3
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/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:
|