Thursday, October 8, 2009

Two More Examples to Drive Rack Middleware

‹prev | My Chain | next›

From yesterday, I have the skeleton of Rack middleware:
module Rack
class ThumbNailer
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
end
end
end
Right now, that doesn't do much—it simply passes thru the results of the target Rack application (my Sinatra application in this case). This is exactly what I described in RSpec examples—for most resources the middleware should just pass-thru the application results.

Even for most images in the application, the image should be passed thru with no modifications:
  context "Accessing an image" do
context "without thumbnail param" do
it "should return image directly from the target app" do
get "/foo.jpg"
last_response.body.should contain("Target app")
end
end
end
This new example still passes since the ThumbNailer middleware only passes-thru.

But, if a :thumbnail parameter is supplied, I want a thumbnail image. From my prototype, I know that ImageScience needs to save this to the file system. To return that image, I will have to create a new File instance and read from it:
  context "Accessing an image" do
context "with thumbnail param" do
before(:each) do
file = mock("File", :read => "Thumbnail")

File.
stub!(:new).
and_return(file)
end
end
In real life, the read will lift image data, for the mock, I read the string "Thumbnail" for testing. Specifically:
      it "should return a thumbnail" do
get "/foo.jpg", :thumbnail => 1
last_response.body.should contain("Thumbnail")
end
To make that pass, I need access to the parameters. The easiest way to accomplish that is via Rack::Request object. With that I can create a conditional based on the presence of the :thumbnail parameter:
    def call(env)
req = Rack::Request.new(env)
if !req.params['thumbnail'].blank?
filename = "foo"
thumbnail = File.new(filename).read
[200, { }, thumbnail]
else
@app.call(env)
end
end
I am using a dummy value for the filename here—a future example will drive the actual storage location. This example is only driving that a file is read and that is the simplest thing that can possibly work. Except:
1)
NoMethodError in 'ThumbNailer Accessing an image with thumbnail param should return a thumbnail'
undefined method `read' for #
./thumbnailer.rb:12:in `call'
/home/cstrom/.gem/ruby/1.8/gems/rack-test-0.5.0/lib/rack/mock_session.rb:30:in `request'
/home/cstrom/.gem/ruby/1.8/gems/rack-test-0.5.0/lib/rack/test.rb:207:in `process_request'
/home/cstrom/.gem/ruby/1.8/gems/rack-test-0.5.0/lib/rack/test.rb:57:in `get'
./thumbnailer_spec.rb:46:
Ack! I forgot that I am working inside a module. I really want to access the top-level File class, not something encapsulated in the Rack:: namespace. Leading double-colons to the rescue:
    def call(env)
req = Rack::Request.new(env)
if !req.params['thumbnail'].blank?
filename = "foo"
thumbnail = ::File.new(filename).read
[200, { }, thumbnail]
else
@app.call(env)
end
end
With that example passing, I call it a night. Tomorrow I will pick back up with BDDing this bit of Rack middleware into submission.

No comments:

Post a Comment