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.
Files changed (4) hide show
  1. data/README.rdoc +53 -19
  2. data/lib/rack/state.rb +13 -7
  3. data/spec/spec_state.rb +64 -13
  4. metadata +3 -3
@@ -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 _store_ state will use the default Memory store and _blog_ will store
94
- state in files under "tmp/sessions" in the project's root. Additionally, if
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: 'store', path: '/store', secure: true
98
- use Rack::State, key: 'blog', path: '/blog', max_age: 60*60*24*30,
99
- store: Rack::State::Store::File.new('tmp/sessions')
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
- def blog
112
- env['rack.state.blog'].get or env['rack.state.blog'].set BlogTracker.new
113
- end
113
+ store.get.cart.add :item7
114
+ flash.notice 'Added Item 7'
114
115
 
115
- blog.viewed << :article9
116
- blog.shared << :article5
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 project:
162
+ Or clone the Mercurial repository:
139
163
 
140
- TODO
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
- * September 5, 2013: Initial design, development and testing.
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
- * September 22, 2013: First public release 0.0.1.
205
+ == License
172
206
 
173
- == Copyright
207
+ ({ISC License}[http://opensource.org/licenses/ISC])
174
208
 
175
- Copyright (c) 2013, Clint Pachl <http://clint.pachl.us>
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
@@ -16,7 +16,7 @@ module Rack
16
16
  #
17
17
  class State
18
18
 
19
- VERSION = '0.0.2'
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
- token = Request.new(env).cookies.fetch(@key, nil)
48
- state = env[@skey] = Manager.new(@store, token, @key, @options)
49
- status, headers, body = @app.call(env)
50
- resp = Response.new(body, status, headers)
51
- state.finish(resp)
52
- resp.finish
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
  #
@@ -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'] || 'token'
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(@key).should.be.a valid_token
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(@key)
73
- token2 = get_state(@key, token1)
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(@key)
79
- token2 = del_state(@key, token1)
80
- token3 = get_state(@key, token1)
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(@key, "#{@key}=nonexistent").should.be.nil
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(@key, "#{@key}=nonexistent")
92
- token.should.not.equal "#{@key}=nonexistent"
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(@key, "#{@key}=nonexistent")
98
- token.should.not.equal "#{@key}=nonexistent"
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.2
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: 2013-09-23 00:00:00.000000000 Z
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://pachl.us/code/rack-state
74
+ homepage: http://ecentryx.com/gems/rack-state
75
75
  licenses:
76
76
  - ISC
77
77
  post_install_message: