rack-cache 0.3.0 → 0.4

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.

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