rack-cache 0.3.0 → 0.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack-cache might be problematic. Click here for more details.

Files changed (44) hide show
  1. data/CHANGES +43 -0
  2. data/README +18 -9
  3. data/Rakefile +1 -14
  4. data/TODO +13 -14
  5. data/doc/configuration.markdown +7 -153
  6. data/doc/faq.markdown +8 -0
  7. data/doc/index.markdown +7 -9
  8. data/example/sinatra/app.rb +25 -0
  9. data/example/sinatra/views/index.erb +44 -0
  10. data/lib/rack/cache.rb +5 -11
  11. data/lib/rack/cache/cachecontrol.rb +193 -0
  12. data/lib/rack/cache/context.rb +190 -52
  13. data/lib/rack/cache/entitystore.rb +10 -4
  14. data/lib/rack/cache/key.rb +52 -0
  15. data/lib/rack/cache/metastore.rb +52 -16
  16. data/lib/rack/cache/options.rb +60 -39
  17. data/lib/rack/cache/request.rb +11 -15
  18. data/lib/rack/cache/response.rb +221 -30
  19. data/lib/rack/cache/storage.rb +1 -2
  20. data/rack-cache.gemspec +9 -15
  21. data/test/cache_test.rb +9 -6
  22. data/test/cachecontrol_test.rb +139 -0
  23. data/test/context_test.rb +251 -169
  24. data/test/entitystore_test.rb +12 -11
  25. data/test/key_test.rb +50 -0
  26. data/test/metastore_test.rb +57 -14
  27. data/test/options_test.rb +11 -0
  28. data/test/request_test.rb +19 -0
  29. data/test/response_test.rb +164 -23
  30. data/test/spec_setup.rb +7 -0
  31. metadata +12 -20
  32. data/doc/events.dot +0 -27
  33. data/lib/rack/cache/config.rb +0 -65
  34. data/lib/rack/cache/config/busters.rb +0 -16
  35. data/lib/rack/cache/config/default.rb +0 -133
  36. data/lib/rack/cache/config/no-cache.rb +0 -13
  37. data/lib/rack/cache/core.rb +0 -299
  38. data/lib/rack/cache/headers.rb +0 -325
  39. data/lib/rack/utils/environment_headers.rb +0 -78
  40. data/test/config_test.rb +0 -66
  41. data/test/core_test.rb +0 -84
  42. data/test/environment_headers_test.rb +0 -69
  43. data/test/headers_test.rb +0 -298
  44. data/test/logging_test.rb +0 -45
data/test/spec_setup.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'pp'
2
2
  require 'tmpdir'
3
+ require 'stringio'
3
4
 
4
5
  [ STDOUT, STDERR ].each { |io| io.sync = true }
5
6
 
@@ -27,6 +28,7 @@ rescue LoadError => boom
27
28
  $memcached = false
28
29
  false
29
30
  rescue => boom
31
+ STDERR.puts "memcached not working. related tests will be skipped."
30
32
  $memcached = false
31
33
  false
32
34
  end
@@ -192,4 +194,9 @@ class Object
192
194
  def class_def name, &blk
193
195
  class_eval { define_method name, &blk }
194
196
  end
197
+
198
+ # True when the Object is neither false or nil.
199
+ def truthy?
200
+ !!self
201
+ end
195
202
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: "0.4"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Tomayko
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-28 00:00:00 -08:00
12
+ date: 2009-03-16 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,6 @@ files:
40
40
  - Rakefile
41
41
  - TODO
42
42
  - doc/configuration.markdown
43
- - doc/events.dot
44
43
  - doc/faq.markdown
45
44
  - doc/index.markdown
46
45
  - doc/layout.html.erb
@@ -48,33 +47,28 @@ files:
48
47
  - doc/rack-cache.css
49
48
  - doc/server.ru
50
49
  - doc/storage.markdown
50
+ - example/sinatra/app.rb
51
+ - example/sinatra/views/index.erb
51
52
  - lib/rack/cache.rb
52
- - lib/rack/cache/config.rb
53
- - lib/rack/cache/config/busters.rb
54
- - lib/rack/cache/config/default.rb
55
- - lib/rack/cache/config/no-cache.rb
53
+ - lib/rack/cache/cachecontrol.rb
56
54
  - lib/rack/cache/context.rb
57
- - lib/rack/cache/core.rb
58
55
  - lib/rack/cache/entitystore.rb
59
- - lib/rack/cache/headers.rb
56
+ - lib/rack/cache/key.rb
60
57
  - lib/rack/cache/metastore.rb
61
58
  - lib/rack/cache/options.rb
62
59
  - lib/rack/cache/request.rb
63
60
  - lib/rack/cache/response.rb
64
61
  - lib/rack/cache/storage.rb
65
- - lib/rack/utils/environment_headers.rb
66
62
  - rack-cache.gemspec
67
63
  - test/cache_test.rb
68
- - test/config_test.rb
64
+ - test/cachecontrol_test.rb
69
65
  - test/context_test.rb
70
- - test/core_test.rb
71
66
  - test/entitystore_test.rb
72
- - test/environment_headers_test.rb
73
- - test/headers_test.rb
74
- - test/logging_test.rb
67
+ - test/key_test.rb
75
68
  - test/metastore_test.rb
76
69
  - test/options_test.rb
77
70
  - test/pony.jpg
71
+ - test/request_test.rb
78
72
  - test/response_test.rb
79
73
  - test/spec_setup.rb
80
74
  - test/storage_test.rb
@@ -111,14 +105,12 @@ specification_version: 2
111
105
  summary: HTTP Caching for Rack
112
106
  test_files:
113
107
  - test/cache_test.rb
114
- - test/config_test.rb
108
+ - test/cachecontrol_test.rb
115
109
  - test/context_test.rb
116
- - test/core_test.rb
117
110
  - test/entitystore_test.rb
118
- - test/environment_headers_test.rb
119
- - test/headers_test.rb
120
- - test/logging_test.rb
111
+ - test/key_test.rb
121
112
  - test/metastore_test.rb
122
113
  - test/options_test.rb
114
+ - test/request_test.rb
123
115
  - test/response_test.rb
124
116
  - test/storage_test.rb
data/doc/events.dot DELETED
@@ -1,27 +0,0 @@
1
- digraph cache_logic {
2
- nodesep=1.25;
3
- center=true;
4
-
5
- node[fontname="Lucida Sans Unicode",labelloc=c,margin=0.10,0.03]
6
- edge[fontname="Lucida Sans Unicode",fontcolor="#444444",labeldistance=20];
7
-
8
- receive -> pass[label="uncacheable request",color=grey];
9
- receive -> lookup[label="cacheable request"];
10
-
11
- pass -> deliver[label="",color=grey];
12
-
13
- lookup -> hit[label="fresh"];
14
- lookup -> fetch[label="stale (needs validation)"];
15
- lookup -> miss[label="uncached"];
16
-
17
- hit -> deliver[label="sizzling"];
18
- hit -> pass[label="bailing...",color=grey];
19
-
20
- miss -> fetch[label=""];
21
- miss -> pass[color=grey];
22
-
23
- fetch -> store[label="cacheable"];
24
- fetch -> deliver[label="not cacheable",color=grey];
25
-
26
- store -> deliver[label="KTHX"];
27
- }
@@ -1,65 +0,0 @@
1
- require 'set'
2
-
3
- module Rack::Cache
4
- # Provides cache configuration methods. This module is included in the cache
5
- # context object.
6
-
7
- module Config
8
- # Evaluate a block of configuration code within the scope of receiver.
9
- def configure(&block)
10
- instance_eval(&block) if block_given?
11
- end
12
-
13
- # Import the configuration file specified. This has the same basic semantics
14
- # as Ruby's built-in +require+ statement but always evaluates the source
15
- # file within the scope of the receiver. The file may exist anywhere on the
16
- # $LOAD_PATH.
17
- def import(file)
18
- return false if imported_features.include?(file)
19
- path = add_file_extension(file, 'rb')
20
- if path = locate_file_on_load_path(path)
21
- source = File.read(path)
22
- imported_features.add(file)
23
- instance_eval source, path, 1
24
- true
25
- else
26
- raise LoadError, 'no such file to load -- %s' % [file]
27
- end
28
- end
29
-
30
- private
31
- # Load the default configuration and evaluate the block provided within
32
- # the scope of the receiver.
33
- def initialize_config(&block)
34
- import 'rack/cache/config/default'
35
- configure(&block)
36
- end
37
-
38
- # Set of files that have been imported.
39
- def imported_features
40
- @imported_features ||= Set.new
41
- end
42
-
43
- # Attempt to expand +file+ to a full path by possibly adding an .rb
44
- # extension and traversing the $LOAD_PATH looking for matches.
45
- def locate_file_on_load_path(file)
46
- if file[0,1] == '/'
47
- file if File.exist?(file)
48
- else
49
- $LOAD_PATH.
50
- map { |base| File.join(base, file) }.
51
- detect { |p| File.exist?(p) }
52
- end
53
- end
54
-
55
- # Add an extension to the filename provided if the file doesn't
56
- # already have extension.
57
- def add_file_extension(file, extension='rb')
58
- if file =~ /\.\w+$/
59
- file
60
- else
61
- "#{file}.#{extension}"
62
- end
63
- end
64
- end
65
- end
@@ -1,16 +0,0 @@
1
- # Adds a very long max-age response header when the requested url
2
- # looks like it includes a cache busting timestamp. Cache busting
3
- # URLs look like this:
4
- # http://HOST/PATH?DIGITS
5
- #
6
- # DIGITS is typically the number of seconds since some epoch but
7
- # this can theoretically be any set of digits. Example:
8
- # http://example.com/css/foo.css?7894387283
9
- #
10
- on :fetch do
11
- next if response.freshness_information?
12
- if request.url =~ /\?\d+$/
13
- trace 'adding huge max-age to response for cache busting URL'
14
- response.ttl = 100000000000000
15
- end
16
- end
@@ -1,133 +0,0 @@
1
- # Called at the beginning of request processing, after the complete
2
- # request has been fully received. Its purpose is to decide whether or
3
- # not to serve the request and how to do it.
4
- #
5
- # The request should not be modified.
6
- #
7
- # Possible transitions from receive:
8
- #
9
- # * pass! - pass the request to the backend the response upstream,
10
- # bypassing all caching features.
11
- #
12
- # * lookup! - attempt to locate the entry in the cache. Control will
13
- # pass to the +hit+, +miss+, or +fetch+ event based on the result of
14
- # the cache lookup.
15
- #
16
- # * error! - return the error code specified, abandoning the request.
17
- #
18
- on :receive do
19
- pass! unless request.method? 'GET', 'HEAD'
20
- pass! if request.header? 'Expect'
21
- lookup!
22
- end
23
-
24
- # Called upon entering pass mode. The request is sent to the backend,
25
- # and the backend's response is sent to the client, but is not entered
26
- # into the cache. The event is triggered immediately after the response
27
- # is received from the backend but before the it has been sent upstream.
28
- #
29
- # Possible transitions from pass:
30
- #
31
- # * finish! - deliver the response upstream.
32
- #
33
- # * error! - return the error code specified, abandoning the request.
34
- #
35
- on :pass do
36
- finish!
37
- end
38
-
39
- # Called after a cache lookup when no matching entry is found in the
40
- # cache. Its purpose is to decide whether or not to attempt to retrieve
41
- # the response from the backend and in what manner.
42
- #
43
- # Possible transitions from miss:
44
- #
45
- # * fetch! - retrieve the requested document from the backend with
46
- # caching features enabled.
47
- #
48
- # * pass! - pass the request to the backend and the response upstream,
49
- # bypassing all caching features.
50
- #
51
- # * error! - return the error code specified and abandon request.
52
- #
53
- # The default configuration transfers control to the fetch event.
54
- on :miss do
55
- fetch!
56
- end
57
-
58
- # Called after a cache lookup when the requested document is found in
59
- # the cache and is fresh.
60
- #
61
- # Possible transitions from hit:
62
- #
63
- # * deliver! - transfer control to the deliver event, sending the cached
64
- # response upstream.
65
- #
66
- # * pass! - abandon the cache entry and transfer to pass mode. The
67
- # original request is sent to the backend and the response sent
68
- # upstream, bypassing all caching features.
69
- #
70
- # * error! - return the error code specified and abandon request.
71
- #
72
- on :hit do
73
- deliver!
74
- end
75
-
76
- # Called after a document is successfully retrieved from the backend
77
- # application or after a cache entry is validated with the backend.
78
- # During validation, the original request is used as a template for a
79
- # conditional GET request with the backend. The +original_response+
80
- # object contains the response as received from the backend and +entry+
81
- # is set to the cached response that triggered validation.
82
- #
83
- # Possible transitions from fetch:
84
- #
85
- # * store! - store the fetched response in the cache or, when
86
- # validating, update the cached response with validated results.
87
- #
88
- # * deliver! - deliver the response upstream without entering it
89
- # into the cache.
90
- #
91
- # * error! return the error code specified and abandon request.
92
- #
93
- on :fetch do
94
- store! if response.cacheable?
95
- deliver!
96
- end
97
-
98
- # Called immediately before an entry is written to the underlying
99
- # cache. The +entry+ object may be modified.
100
- #
101
- # Possible transitions from store:
102
- #
103
- # * persist! - commit the object to cache and transfer control to
104
- # the deliver event.
105
- #
106
- # * deliver! - transfer control to the deliver event without committing
107
- # the object to cache.
108
- #
109
- # * error! - return the error code specified and abandon request.
110
- #
111
- on :store do
112
- trace 'store backend response in cache (ttl: %ds)', entry.ttl
113
- persist!
114
- end
115
-
116
- # Called immediately before +response+ is delivered upstream. +response+
117
- # may be modified at this point but the changes will not effect the
118
- # cache since the entry has already been persisted.
119
- #
120
- # * finish! - complete processing and send the response upstream
121
- #
122
- # * error! - return the error code specified and abandon request.
123
- #
124
- on :deliver do
125
- finish!
126
- end
127
-
128
- # Called when an error! transition is triggered. The +response+ has the
129
- # error code, headers, and body that will be delivered to upstream and
130
- # may be modified if needed.
131
- on :error do
132
- finish!
133
- end
@@ -1,13 +0,0 @@
1
- # The default configuration ignores the `Cache-Control: no-cache` directive on
2
- # requests. Per RFC 2616, the presence of the no-cache directive should cause
3
- # intermediaries to process requests as if no cached version were available.
4
- # However, this directive is most often targetted at shared proxy caches, not
5
- # gateway caches, and so we've chosen to break with the spec in our default
6
- # configuration.
7
- #
8
- # Import 'rack/cache/config/no-cache' to enable standards-based
9
- # processing.
10
-
11
- on :receive do
12
- pass! if request.header['Cache-Control'] =~ /no-cache/
13
- end
@@ -1,299 +0,0 @@
1
- require 'rack/cache/request'
2
- require 'rack/cache/response'
3
-
4
- module Rack::Cache
5
- # Raised when an attempt is made to transition to an event that can
6
- # not be transitioned from the current event.
7
- class IllegalTransition < Exception
8
- end
9
-
10
- # The core logic engine and state machine. When a request is received,
11
- # the engine begins transitioning from state to state based on the
12
- # advice given by events. Each transition performs some piece of core
13
- # logic, calls out to an event handler, and then kicks off the next
14
- # transition.
15
- #
16
- # Five objects of interest are made available during execution:
17
- #
18
- # * +original_request+ - The request as originally received. This object
19
- # is never modified.
20
- # * +request+ - The request that may eventually be sent downstream in
21
- # case of pass or miss. This object defaults to the +original_request+
22
- # but may be modified or replaced entirely.
23
- # * +original_response+ - The response exactly as specified by the
24
- # downstream application; +nil+ on cache hit.
25
- # * +entry+ - The response loaded from cache or stored to cache. This
26
- # object becomes +response+ if the cached response is valid.
27
- # * +response+ - The response that will be delivered upstream after
28
- # processing is complete. This object may be modified as necessary.
29
- #
30
- # These objects can be accessed and modified from within event handlers
31
- # to perform various types of request/response manipulation.
32
- module Core
33
-
34
- # The request exactly as received. The object is an instance of the
35
- # Rack::Cache::Request class, which includes many utility methods for
36
- # inspecting the state of the request.
37
- #
38
- # This object cannot be modified. If the request requires modification
39
- # before being delivered to the downstream application, use the
40
- # #request object.
41
- attr_reader :original_request
42
-
43
- # The response exactly as received from the downstream application. The
44
- # object is an instance of the Rack::Cache::Response class, which includes
45
- # utility methods for inspecting the state of the response.
46
- #
47
- # The original response should not be modified. Use the #response object to
48
- # access the response to be sent back upstream.
49
- attr_reader :original_response
50
-
51
- # A response object retrieved from cache, or the response that is to be
52
- # saved to cache, or nil if no cached response was found. The object is
53
- # an instance of the Rack::Cache::Response class.
54
- attr_reader :entry
55
-
56
- # The request that will be made downstream on the application. This
57
- # defaults to the request exactly as received (#original_request). The
58
- # object is an instance of the Rack::Cache::Request class, which includes
59
- # utility methods for inspecting and modifying various aspects of the
60
- # HTTP request.
61
- attr_reader :request
62
-
63
- # The response that will be sent upstream. Defaults to the response
64
- # received from the downstream application (#original_response) but
65
- # is set to the cached #entry when valid. In any case, the object
66
- # is an instance of the Rack::Cache::Response class, which includes a
67
- # variety of utility methods for inspecting and modifying the HTTP
68
- # response.
69
- attr_reader :response
70
-
71
- # Has the given event been performed at any time during the
72
- # request life-cycle? Useful for testing.
73
- def performed?(event)
74
- @triggered.include?(event)
75
- end
76
-
77
- # Event handlers.
78
- attr_reader :events
79
- private :events
80
-
81
- public
82
- # Attach custom logic to one or more events.
83
- def on(*events, &block)
84
- events.each { |event| @events[event].unshift(block) }
85
- nil
86
- end
87
-
88
- private
89
- # Transitioning statements
90
-
91
- def pass! ; throw(:transition, [:pass]) ; end
92
- def lookup! ; throw(:transition, [:lookup]) ; end
93
- def store! ; throw(:transition, [:store]) ; end
94
- def fetch! ; throw(:transition, [:fetch]) ; end
95
- def persist! ; throw(:transition, [:persist]) ; end
96
- def deliver! ; throw(:transition, [:deliver]) ; end
97
- def finish! ; throw(:transition, [:finish]) ; end
98
-
99
- def error!(code=500, headers={}, body=nil)
100
- throw(:transition, [:error, code, headers, body])
101
- end
102
-
103
- private
104
- # Does the request include authorization or other sensitive information
105
- # that should cause the response to be considered private by default?
106
- # Private responses are not stored in the cache.
107
- def private_request?
108
- request.header?(*private_headers)
109
- end
110
-
111
- # Determine if the #response validators (ETag, Last-Modified) matches
112
- # a conditional value specified in #original_request.
113
- def not_modified?
114
- response.etag_matches?(original_request.if_none_match) ||
115
- response.last_modified_at?(original_request.if_modified_since)
116
- end
117
-
118
- # Delegate the request to the backend and create the response.
119
- def fetch_from_backend
120
- status, headers, body = backend.call(request.env)
121
- response = Response.new(status, headers, body)
122
- @response = response.dup
123
- @original_response = response.freeze
124
- end
125
-
126
- private
127
- def perform_receive
128
- @original_request = Request.new(@env.dup.freeze)
129
- @env['REQUEST_METHOD'] = 'GET' if @original_request.head?
130
- @request = Request.new(@env)
131
- info "%s %s", @original_request.request_method, @original_request.fullpath
132
- transition(from=:receive, to=[:pass, :lookup, :error])
133
- end
134
-
135
- def perform_pass
136
- trace 'passing'
137
- request.env['REQUEST_METHOD'] = @original_request.request_method
138
- fetch_from_backend
139
- transition(from=:pass, to=[:pass, :finish, :error]) do |event|
140
- if event == :pass
141
- :finish
142
- else
143
- event
144
- end
145
- end
146
- end
147
-
148
- def perform_error(code=500, headers={}, body=nil)
149
- body, headers = headers, {} unless headers.is_a?(Hash)
150
- headers = {} if headers.nil?
151
- body = [] if body.nil? || body == ''
152
- @response = Rack::Cache::Response.new(code, headers, body)
153
- transition(from=:error, to=[:finish])
154
- end
155
-
156
- def perform_lookup
157
- if @entry = metastore.lookup(original_request, entitystore)
158
- if @entry.fresh?
159
- trace 'cache hit (ttl: %ds)', @entry.ttl
160
- transition(from=:hit, to=[:deliver, :pass, :error]) do |event|
161
- @response = @entry if event == :deliver
162
- event
163
- end
164
- else
165
- trace 'cache stale (ttl: %ds), validating...', @entry.ttl
166
- perform_validate
167
- end
168
- else
169
- trace 'cache miss'
170
- transition(from=:miss, to=[:fetch, :pass, :error])
171
- end
172
- end
173
-
174
- def perform_validate
175
- # add our cached validators to the backend request
176
- request.headers['If-Modified-Since'] = entry.last_modified
177
- request.headers['If-None-Match'] = entry.etag
178
- fetch_from_backend
179
-
180
- if original_response.status == 304
181
- trace "cache entry valid"
182
- @response = entry.dup
183
- @response.headers.delete('Age')
184
- @response.headers.delete('Date')
185
- @response.headers['X-Origin-Status'] = '304'
186
- %w[Date Expires Cache-Control Etag Last-Modified].each do |name|
187
- next unless value = original_response.headers[name]
188
- @response[name] = value
189
- end
190
- @response.activate!
191
- else
192
- trace "cache entry invalid"
193
- @entry = nil
194
- end
195
- transition(from=:fetch, to=[:store, :deliver, :error])
196
- end
197
-
198
- def perform_fetch
199
- trace "fetching response from backend"
200
- request.env.delete('HTTP_IF_MODIFIED_SINCE')
201
- request.env.delete('HTTP_IF_NONE_MATCH')
202
- fetch_from_backend
203
-
204
- # mark the response as explicitly private if any of the private
205
- # request headers are present and the response was not explicitly
206
- # declared public.
207
- if private_request? && !@response.public?
208
- @response.private = true
209
- else
210
- # assign a default TTL for the cache entry if none was specified in
211
- # the response; the must-revalidate cache control directive disables
212
- # default ttl assigment.
213
- if default_ttl > 0 && @response.ttl.nil? && !@response.must_revalidate?
214
- @response.ttl = default_ttl
215
- end
216
- end
217
- transition(from=:fetch, to=[:store, :deliver, :error])
218
- end
219
-
220
- def perform_store
221
- @entry = @response
222
- transition(from=:store, to=[:persist, :deliver, :error]) do |event|
223
- if event == :persist
224
- if @response.private?
225
- warn 'forced to store response marked as private.'
226
- else
227
- trace "storing response in cache"
228
- end
229
- metastore.store(original_request, @entry, entitystore)
230
- @response = @entry
231
- :deliver
232
- else
233
- event
234
- end
235
- end
236
- end
237
-
238
- def perform_deliver
239
- trace "delivering response ..."
240
- response.not_modified! if not_modified?
241
- response.body = [] if @original_request.head?
242
- transition(from=:deliver, to=[:finish, :error])
243
- end
244
-
245
- def perform_finish
246
- response.headers.delete 'X-Status'
247
- response.to_a
248
- end
249
-
250
- private
251
- # Transition from the currently processing event to another event
252
- # after triggering event handlers.
253
- def transition(from, to)
254
- ev, *args = trigger(from)
255
- raise IllegalTransition, "No transition to :#{ev}" unless to.include?(ev)
256
- ev = yield ev if block_given?
257
- send "perform_#{ev}", *args
258
- end
259
-
260
- # Trigger processing of the event specified and return an array containing
261
- # the name of the next transition and any arguments provided to the
262
- # transitioning statement.
263
- def trigger(event)
264
- if @events.include? event
265
- @triggered << event
266
- catch(:transition) do
267
- @events[event].each { |block| instance_eval(&block) }
268
- nil
269
- end
270
- else
271
- raise NameError, "No such event: #{event}"
272
- end
273
- end
274
-
275
- private
276
- # Setup the core prototype. The object's state after execution
277
- # of this method will be duped and used for individual request.
278
- def initialize_core
279
- @triggered = []
280
- @events = Hash.new { |h,k| h[k.to_sym] = [] }
281
-
282
- # initialize some instance variables; we won't use them until we dup to
283
- # process a request.
284
- @request = nil
285
- @response = nil
286
- @original_request = nil
287
- @original_response = nil
288
- @entry = nil
289
- end
290
-
291
- # Process a request. This method is compatible with Rack's #call
292
- # interface.
293
- def process_request(env)
294
- @triggered = []
295
- @env = @default_options.merge(env)
296
- perform_receive
297
- end
298
- end
299
- end