Exercise 3: Why a Rack Middleware Is a Real Standalone App — Possible Solution ==================================================================== RACK'S OWN QUOTED INTERFACE REQUIREMENT ------------------------------------------------------------ Per Rack's own official specification: "A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment... and returns a non-frozen Array of exactly three elements: the status, the headers, and the body." Nothing in that real requirement mentions "middleware" as a separate category at all -- it is the complete, entire definition of what counts as a real Rack app. WHY MyCustomMiddleware.new(some_app) QUALIFIES DIRECTLY ------------------------------------------------------------------ class MyCustomMiddleware def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) [status, headers, body] end end MyCustomMiddleware.new(some_app) is a real Ruby object. It responds to call. That call method takes exactly one argument (env) and returns a real [status, headers, body] array. Every single requirement in Rack's own quoted spec is satisfied -- nothing about the object's own internal implementation (the fact that it happens to delegate to @app.call(env) internally) is part of the actual interface contract at all. A Rack server handed MyCustomMiddleware.new(some_app) directly, with no further chain around it, would work completely correctly and could never tell the difference between that and a "real" standalone application. WHY CHAPTER 8'S OWN PYTHON MIDDLEWARE FUNCTIONS DON'T QUALIFY ------------------------------------------------------------------------ Chapter 8's own middleware functions all share the signature middleware(request, next) -- TWO parameters. Chapter 8's own real handler functions, by contrast, share the signature handler(request) -- ONE parameter. These are genuinely, structurally different call signatures. Calling one of Chapter 8's own middleware functions directly, on its own, the same way a handler would be called -- auth_middleware(some_request) with nothing supplied for next -- would raise a real Python TypeError (a missing required positional argument), not silently work. There is no way to hand Chapter 8's own auth_middleware to build_chain([], auth_middleware) and have it work as a standalone handler, because build_chain() only ever calls its own handler argument with one argument, and auth_middleware genuinely requires two. THE REAL, GENERAL PRINCIPLE ------------------------------------------------------------------ Rack's own single, uniform interface (one method, one argument, one return shape, shared identically by every app and every middleware) is precisely what makes "a middleware is also a valid app" a real, structurally guaranteed fact rather than a coincidence. Chapter 8's own two-shape design (middlewares take next, handlers don't) is a genuinely different, equally valid real choice -- but it deliberately keeps middleware and handlers structurally distinct, which is exactly why the identical claim can never be true for it. WHY THIS WORKS AS AN ANSWER ---------------------------- It checks MyCustomMiddleware.new(some_app) against every individual requirement in Rack's own quoted spec rather than asserting the conclusion, and identifies the precise structural reason (a genuine signature mismatch, verified by what would actually happen if called directly) that the identical claim fails for Chapter 8's own Python functions.