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.
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: