After a brief interlude into silly Ruby arcana, I am back to Rack tonight. There are some gaps in my understanding of Rack, so I will prototype to learn.
The other night, I was able to get my Sinatra app running under Rack by creating a
config.ru
rackup file with the following contents:require 'eee'So let's see if we can add a handler in front of the Sinatra app. For example I will try to force the user to look at an interstitial every fifth access of the site.
run Sinatra::Application
As with any Rack middleware, I need to initialize with a single argument (the Rack application) and expose a call method that accepts the web environment:
module RackTo get access to that session, I need to use
class Interstitial
def initialize app
@app = app
end
def call env
session = env["rack.session"]
session[:count] = session[:count].to_i + 1
if session[:count] % 5 == 0
return [200, { }, session[:count].to_s]
end
@app.call env
end
end
end
Rack::Session::Cookie
(and my newly created Rack::Interstitial
:use Rack::Session::CookieWith each access, Rack will first hand off to
use Rack::Interstitial
Rack::Session::Cookie
, which adds the cookie values to the environment under the value "rack.session" by default. Rack::Session::Cookie
will then call Rack::Interstitial
.Each access to
Rack::Interstitial
pulls a running count from the session and increments the count by 1. Normally it will then call the next Rack module, but every time the session count is divisible by 5 with no remainder, the Rack::Interstitial
middleware will return HTTP response code 200 (OK), with no special headers ({ }
) and content consisting entirely of the current session count.To verify that I have implemented this correctly, I load up one of our meals in the browser:
So far, so good. What happens if I reload 4 more times?
Nice! The interstitial kicks in as expected.
Before I call it a day, I am curious about manipulating output. Since I am already a third of the way to fizz buzz glory, I think I'll try to modify the output such that every instance of the word "grits" is replaced by "fizz" every third access.
If I was doing this legit, I would make this another Rack handler. Since I am just learning, I'll do this right in my interstitial handler. Every Rack handler's
call
method has to respond with three things: the HTTP response code, the HTTP headers and the response body. So I ought to be able to take that response body and do a global substitiute:code, headers, body = @app.call envThat does not quite work:
[code, headers, body.gsub(/grits/i, 'fizz')]
I get an error calling gsub on a
Rack::CommonLogger
instance. Ah, that is not the response body, just an object that responds to each with the response body. Guess I'll have to do it the hard way:code, headers, body = @app.call envWith that, I get my desired "Sausage Vegetable fizz":
if session[:count] % 3 == 0
ret = []
body.each do |line|
ret << line.gsub(/grits/i, '<span style="color:red">fizz</span>')
end
else
ret = body
end
[code, headers, body]
(commit)
There is still some other areas of Rack that I would like to explore, so I will probably pick back up prototyping tomorrow.
No comments:
Post a Comment