1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
# Middleware User Guide
`middleware` is a library which provides a generalized implementation
of the middleware pattern for Ruby. The middleware pattern is a useful
abstraction tool in various cases, but is specifically useful for splitting
large sequential chunks of logic into small pieces.
## Installing
Middleware is distributed as a RubyGem, so simply gem install:
gem install middleware
## A Basic Example
Below is a basic example of the library in use. If you don't understand
what middleware is, please read below. This example is simply meant to give
you a quick idea of what the library looks like.
```ruby
# Basic middleware that just prints the inbound and
# outbound steps.
class Trace
def initialize(app, value)
@app = app
@value = value
end
def call(env)
puts "--> #{@value}"
@app.call(env)
puts "<-- #{@value}"
end
end
# Build the actual middleware stack which runs a sequence
# of slightly different versions of our middleware.
stack = Middleware::Builder.new do
use Trace, "A"
use Trace, "B"
use Trace, "C"
end
# Run it!
stack.call(nil)
```
And the output:
```
--> A
--> B
--> C
<-- C
<-- B
<-- A
```
## Middleware
### What is it?
Middleware is a reusable chunk of logic that is called to perform some
action. The middleware itself is responsible for calling up the next item
in the middleware chain using a recursive-like call. This allows middleware
to perform logic both _before_ and _after_ something is done.
The canonical middleware example is in web request processing, and middleware
is used heavily by both [Rack](#) and [Rails](#).
In web processing, the first middleware is called with some information about
the web request, such as HTTP headers, request URL, etc. The middleware is
responsible for calling the next middleware, and may modify the request along
the way. When the middlewares begin returning, the state now has the HTTP
response, so that the middlewares can then modify the response.
Cool? Yeah! And this pattern is generally usable in a wide variety of
problems.
### Middleware Classes
One method of creating middleware, and by far the most common, is to define
a class that duck types to the following interface:
class MiddlewareExample
def initialize(app); end
def call(env); end
end
Therefore, a basic middleware example follows:
class Trace
def initialize(app)
@app = app
end
def call(env)
puts "Trace up"
@app.call(env)
puts "Trace down"
end
end
A basic description of the two methods that a middleware must implement:
* **initialize(app)** - This is a constructor. It can take additional arguments
but the first argument sent will always be the next middleware to call, called
`app` for historical reasons. This should be stored away for later.
* **call(env)** - This is what is actually invoked to do work. `env` is just some
state sent in (defined by the caller, but usually a Hash). This call should also
call `app.call(env)` at some point to move on.
### Middleware Lambdas
A middleware can also be a simple lambda. The downside of using a lambda is that
it only has access to the state on the initial call, there is no "post" step for
lambdas. A basic example, in the context of a web request:
lambda { |env| puts "You requested: #{env["http.request_url"]}" }
## Middleware Stacks
Middlewares on their own are useful as small chunks of logic, but their real
power comes from building them up into a _stack_. A stack of middlewares are
executed in the order given.
### Basic Building and Running
The middleware library comes with a `Builder` class which provides a nice DSL
for building a stack of middlewares:
stack = Middleware::Builder.new do
use Trace
use lambda { |env| puts "LAMBDA!" }
end
This `stack` variable itself is now a valid middleware and has the same interface,
so to execute the stack, just call `call` on it:
stack.call
The call method takes an optional parameter which is the state to pass into the
initial middleware.
### Manipulating a Stack
Stacks also provide a set of methods for manipulating the middleware stack. This
lets you insert, replace, and delete middleware after a stack has already been
created. Given the `stack` variable created above, we can manipulate it as
follows. Please imagine that each example runs with the original `stack` variable,
so that the order of the examples doesn't actually matter:
# Insert a new item after the Trace middleware
stack.insert_after(Trace, SomeOtherMiddleware)
# Replace the lambda
stack.replace(1, SomeOtherMiddleware)
# Delete the lambda
stack.delete(1)
### Passing Additional Constructor Arguments
When using middleware in a stack, you can also pass in additional constructor
arguments. Given the following middleware:
class Echo
def initialize(app, message)
@app = app
@message = message
end
def call(env)
puts @message
@app.call(env)
end
end
We can initialize `Echo` with a proper message as follows:
Middleware::Builder.new do
use Echo, "Hello, World!"
end
Then when the stack is called, it will output "Hello, World!"
Note that you can also pass blocks in using the `use` method.
|