middleware 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ - ruby-head
11
+ - jruby-head
12
+ - ree
@@ -0,0 +1,3 @@
1
+ -m markdown
2
+ --files
3
+ user_guide.md
@@ -0,0 +1,4 @@
1
+ ## 0.1.0 (March 16, 2012)
2
+
3
+ - Initial release, almost directly extracted from [Vagrant](http://vagrantup.com)
4
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in middleware.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Mitchell Hashimoto
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # Middleware
2
+
3
+ [![Build Status](https://secure.travis-ci.org/mitchellh/middleware.png?branch=master)](http://travis-ci.org/mitchellh/middleware)
4
+
5
+ This is a generalized library for using middleware patterns within
6
+ your Ruby projects.
7
+
8
+ To get started, the best place to look is [the user guide](https://github.com/mitchellh/middleware/blob/master/user_guide.md).
9
+
10
+ ## Installation
11
+
12
+ This project is distributed as a RubyGem:
13
+
14
+ $ gem install middleware
15
+
16
+ ## Usage
17
+
18
+ Once you create a basic middleware, you can use the builder to
19
+ have a nice DSL to build middleware stacks. Calling the middleware
20
+ is simple, as well.
21
+
22
+ ```ruby
23
+ # Basic middleware that just prints the inbound and
24
+ # outbound steps.
25
+ class Trace
26
+ def initialize(app, value)
27
+ @app = app
28
+ @value = value
29
+ end
30
+
31
+ def call(env)
32
+ puts "--> #{@value}"
33
+ @app.call(env)
34
+ puts "<-- #{@value}"
35
+ end
36
+ end
37
+
38
+ # Build the actual middleware stack which runs a sequence
39
+ # of slightly different versions of our middleware.
40
+ stack = Middleware::Builder.new do
41
+ use Trace, "A"
42
+ use Trace, "B"
43
+ use Trace, "C"
44
+ end
45
+
46
+ # Run it!
47
+ stack.call(nil)
48
+ ```
49
+
50
+ And the output:
51
+
52
+ ```
53
+ --> A
54
+ --> B
55
+ --> C
56
+ <-- C
57
+ <-- B
58
+ <-- A
59
+ ```
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ # RSpec test task
6
+ RSpec::Core::RakeTask.new
7
+
8
+ # Make sure the default is to run RSpec
9
+ task :default => "spec"
@@ -0,0 +1,3 @@
1
+ require "middleware/version"
2
+ require "middleware/builder"
3
+ require "middleware/runner"
@@ -0,0 +1,134 @@
1
+ module Middleware
2
+ # This provides a DSL for building up a stack of middlewares.
3
+ #
4
+ # This code is based heavily off of `Rack::Builder` and
5
+ # `ActionDispatch::MiddlewareStack` in Rack and Rails, respectively.
6
+ #
7
+ # # Usage
8
+ #
9
+ # Building a middleware stack is very easy:
10
+ #
11
+ # app = Middleware::Builder.new do
12
+ # use A
13
+ # use B
14
+ # end
15
+ #
16
+ # # Call the middleware
17
+ # app.call(7)
18
+ #
19
+ class Builder
20
+ # Initializes the builder. An optional block can be passed which
21
+ # will be evaluated in the context of the instance.
22
+ #
23
+ # Example:
24
+ #
25
+ # Builder.new do
26
+ # use A
27
+ # use B
28
+ # end
29
+ #
30
+ # @param [Hash] opts Options hash
31
+ # @option opts [Class] :runner_class The class to wrap the middleware stack
32
+ # in which knows how to run them.
33
+ # @yield [] Evaluated in this instance which allows you to use methods
34
+ # like {#use} and such.
35
+ def initialize(opts=nil, &block)
36
+ opts ||= {}
37
+ @runner_class = opts[:runner_class] || Runner
38
+ instance_eval(&block) if block_given?
39
+ end
40
+
41
+ # Returns a mergeable version of the builder. If `use` is called with
42
+ # the return value of this method, then the stack will merge, instead
43
+ # of being treated as a separate single middleware.
44
+ def flatten
45
+ lambda do |env|
46
+ self.call(env)
47
+ end
48
+ end
49
+
50
+ # Adds a middleware class to the middleware stack. Any additional
51
+ # args and a block, if given, are saved and passed to the initializer
52
+ # of the middleware.
53
+ #
54
+ # @param [Class] middleware The middleware class
55
+ def use(middleware, *args, &block)
56
+ if middleware.kind_of?(Builder)
57
+ # Merge in the other builder's stack into our own
58
+ self.stack.concat(middleware.stack)
59
+ else
60
+ self.stack << [middleware, args, block]
61
+ end
62
+
63
+ self
64
+ end
65
+
66
+ # Inserts a middleware at the given index or directly before the
67
+ # given middleware object.
68
+ def insert(index, middleware, *args, &block)
69
+ index = self.index(index) unless index.is_a?(Integer)
70
+ stack.insert(index, [middleware, args, block])
71
+ end
72
+
73
+ alias_method :insert_before, :insert
74
+
75
+ # Inserts a middleware after the given index or middleware object.
76
+ def insert_after(index, middleware, *args, &block)
77
+ index = self.index(index) unless index.is_a?(Integer)
78
+ raise "no such middleware to insert after: #{index.inspect}" unless index
79
+ insert(index + 1, middleware, *args, &block)
80
+ end
81
+
82
+ # Replaces the given middlware object or index with the new
83
+ # middleware.
84
+ def replace(index, middleware, *args, &block)
85
+ if index.is_a?(Integer)
86
+ delete(index)
87
+ insert(index, middleware, *args, &block)
88
+ else
89
+ insert_before(index, middleware, *args, &block)
90
+ delete(index)
91
+ end
92
+ end
93
+
94
+ # Deletes the given middleware object or index
95
+ def delete(index)
96
+ index = self.index(index) unless index.is_a?(Integer)
97
+ stack.delete_at(index)
98
+ end
99
+
100
+ # Runs the builder stack with the given environment.
101
+ def call(env=nil)
102
+ to_app.call(env)
103
+ end
104
+
105
+ protected
106
+
107
+ # Returns the numeric index for the given middleware object.
108
+ #
109
+ # @param [Object] object The item to find the index for
110
+ # @return [Integer]
111
+ def index(object)
112
+ stack.each_with_index do |item, i|
113
+ return i if item[0] == object
114
+ end
115
+
116
+ nil
117
+ end
118
+
119
+ # Returns the current stack of middlewares. You probably won't
120
+ # need to use this directly, and it's recommended that you don't.
121
+ #
122
+ # @return [Array]
123
+ def stack
124
+ @stack ||= []
125
+ end
126
+
127
+ # Converts the builder stack to a runnable action sequence.
128
+ #
129
+ # @return [Object] A callable object
130
+ def to_app
131
+ @runner_class.new(stack.dup)
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,69 @@
1
+ module Middleware
2
+ # This is a basic runner for middleware stacks. This runner does
3
+ # the default expected behavior of running the middleware stacks
4
+ # in order, then reversing the order.
5
+ class Runner
6
+ # A middleware which does nothing
7
+ EMPTY_MIDDLEWARE = lambda { |env| }
8
+
9
+ # Build a new middleware runner with the given middleware
10
+ # stack.
11
+ #
12
+ # Note: This class usually doesn't need to be used directly.
13
+ # Instead, take a look at using the {Builder} class, which is
14
+ # a much friendlier way to build up a middleware stack.
15
+ #
16
+ # @param [Array] stack An array of the middleware to run.
17
+ def initialize(stack)
18
+ # We need to take the stack of middleware and initialize them
19
+ # all so they call the proper next middleware.
20
+ @kickoff = build_call_chain(stack)
21
+ end
22
+
23
+ # Run the middleware stack with the given state bag.
24
+ #
25
+ # @param [Object] env The state to pass into as the initial
26
+ # environment data. This is usual a hash of some sort.
27
+ def call(env)
28
+ # We just call the kickoff middleware, which is responsible
29
+ # for properly calling the next middleware, and so on and so
30
+ # forth.
31
+ @kickoff.call(env)
32
+ end
33
+
34
+ protected
35
+
36
+ # This takes a stack of middlewares and initializes them in a way
37
+ # that each middleware properly calls the next middleware.
38
+ def build_call_chain(stack)
39
+ # We need to instantiate the middleware stack in reverse
40
+ # order so that each middleware can have a reference to
41
+ # the next middleware it has to call. The final middleware
42
+ # is always the empty middleware, which does nothing but return.
43
+ stack.reverse.inject(EMPTY_MIDDLEWARE) do |next_middleware, current_middleware|
44
+ # Unpack the actual item
45
+ klass, args, block = current_middleware
46
+
47
+ # Default the arguments to an empty array. Otherwise in Ruby 1.8
48
+ # a `nil` args will actually pass `nil` into the class. Not what
49
+ # we want!
50
+ args ||= []
51
+
52
+ if klass.is_a?(Class)
53
+ # If the klass actually is a class, then instantiate it with
54
+ # the app and any other arguments given.
55
+ klass.new(next_middleware, *args, &block)
56
+ elsif klass.respond_to?(:call)
57
+ # Make it a lambda which calls the item then forwards up
58
+ # the chain.
59
+ lambda do |env|
60
+ klass.call(env)
61
+ next_middleware.call(env)
62
+ end
63
+ else
64
+ raise "Invalid middleware, doesn't respond to `call`: #{action.inspect}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Middleware
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/middleware/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Mitchell Hashimoto"]
6
+ gem.email = ["mitchell.hashimoto@gmail.com"]
7
+ gem.description = %q{Generalized implementation of the middleware abstraction for Ruby.}
8
+ gem.summary = %q{Generalized implementation of the middleware abstraction for Ruby.}
9
+ gem.homepage = "https://github.com/mitchellh/middleware"
10
+
11
+ gem.add_development_dependency "rake"
12
+ gem.add_development_dependency "redcarpet", "~> 2.1.0"
13
+ gem.add_development_dependency "rspec-core", "~> 2.8.0"
14
+ gem.add_development_dependency "rspec-expectations", "~> 2.8.0"
15
+ gem.add_development_dependency "rspec-mocks", "~> 2.8.0"
16
+ gem.add_development_dependency "yard", "~> 0.7.5"
17
+
18
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ gem.files = `git ls-files`.split("\n")
20
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ gem.name = "middleware"
22
+ gem.require_paths = ["lib"]
23
+ gem.version = Middleware::VERSION
24
+ end
@@ -0,0 +1,155 @@
1
+ require File.expand_path("../../setup", __FILE__)
2
+ require "middleware"
3
+
4
+ describe Middleware::Builder do
5
+ let(:data) { { :data => [] } }
6
+ let(:instance) { described_class.new }
7
+
8
+ # This returns a proc that can be used with the builder
9
+ # that simply appends data to an array in the env.
10
+ def appender_proc(data)
11
+ Proc.new { |env| env[:data] << data }
12
+ end
13
+
14
+ context "basic `use`" do
15
+ it "should add items to the stack and make them callable" do
16
+ data = {}
17
+ proc = Proc.new { |env| env[:data] = true }
18
+
19
+ instance.use proc
20
+ instance.call(data)
21
+
22
+ data[:data].should == true
23
+ end
24
+
25
+ it "should be able to add multiple items" do
26
+ data = {}
27
+ proc1 = Proc.new { |env| env[:one] = true }
28
+ proc2 = Proc.new { |env| env[:two] = true }
29
+
30
+ instance.use proc1
31
+ instance.use proc2
32
+ instance.call(data)
33
+
34
+ data[:one].should == true
35
+ data[:two].should == true
36
+ end
37
+
38
+ it "should be able to add another builder" do
39
+ data = {}
40
+ proc1 = Proc.new { |env| env[:one] = true }
41
+
42
+ # Build the first builder
43
+ one = described_class.new
44
+ one.use proc1
45
+
46
+ # Add it to this builder
47
+ two = described_class.new
48
+ two.use one
49
+
50
+ # Call the 2nd and verify results
51
+ two.call(data)
52
+ data[:one].should == true
53
+ end
54
+
55
+ it "should default the env to `nil` if not given" do
56
+ result = false
57
+ proc = Proc.new { |env| result = env.nil? }
58
+
59
+ instance.use proc
60
+ instance.call
61
+
62
+ result.should be
63
+ end
64
+ end
65
+
66
+ context "inserting" do
67
+ it "can insert at an index" do
68
+ instance.use appender_proc(1)
69
+ instance.insert(0, appender_proc(2))
70
+ instance.call(data)
71
+
72
+ data[:data].should == [2, 1]
73
+ end
74
+
75
+ it "can insert next to a previous object" do
76
+ proc2 = appender_proc(2)
77
+ instance.use appender_proc(1)
78
+ instance.use proc2
79
+ instance.insert(proc2, appender_proc(3))
80
+ instance.call(data)
81
+
82
+ data[:data].should == [1, 3, 2]
83
+ end
84
+
85
+ it "can insert before" do
86
+ instance.use appender_proc(1)
87
+ instance.insert_before 0, appender_proc(2)
88
+ instance.call(data)
89
+
90
+ data[:data].should == [2, 1]
91
+ end
92
+
93
+ it "can insert after" do
94
+ instance.use appender_proc(1)
95
+ instance.use appender_proc(3)
96
+ instance.insert_after 0, appender_proc(2)
97
+ instance.call(data)
98
+
99
+ data[:data].should == [1, 2, 3]
100
+ end
101
+
102
+ it "raises an exception if an invalid object given" do
103
+ expect { instance.insert_after "object", appender_proc(1) }.
104
+ to raise_error(RuntimeError)
105
+ end
106
+ end
107
+
108
+ context "replace" do
109
+ it "can replace an object" do
110
+ proc1 = appender_proc(1)
111
+ proc2 = appender_proc(2)
112
+
113
+ instance.use proc1
114
+ instance.replace proc1, proc2
115
+ instance.call(data)
116
+
117
+ data[:data].should == [2]
118
+ end
119
+
120
+ it "can replace by index" do
121
+ proc1 = appender_proc(1)
122
+ proc2 = appender_proc(2)
123
+
124
+ instance.use proc1
125
+ instance.replace 0, proc2
126
+ instance.call(data)
127
+
128
+ data[:data].should == [2]
129
+ end
130
+ end
131
+
132
+ context "deleting" do
133
+ it "can delete by object" do
134
+ proc1 = appender_proc(1)
135
+
136
+ instance.use proc1
137
+ instance.use appender_proc(2)
138
+ instance.delete proc1
139
+ instance.call(data)
140
+
141
+ data[:data].should == [2]
142
+ end
143
+
144
+ it "can delete by index" do
145
+ proc1 = appender_proc(1)
146
+
147
+ instance.use proc1
148
+ instance.use appender_proc(2)
149
+ instance.delete 0
150
+ instance.call(data)
151
+
152
+ data[:data].should == [2]
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path("../../setup", __FILE__)
2
+ require "middleware"
3
+
4
+ describe Middleware::Runner do
5
+ it "should work with an empty stack" do
6
+ instance = described_class.new([])
7
+ expect { instance.call({}) }.to_not raise_error
8
+ end
9
+
10
+ it "should call classes in the proper order" do
11
+ a = Class.new do
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ env[:result] << "A"
18
+ @app.call(env)
19
+ env[:result] << "A"
20
+ end
21
+ end
22
+
23
+ b = Class.new do
24
+ def initialize(app)
25
+ @app = app
26
+ end
27
+
28
+ def call(env)
29
+ env[:result] << "B"
30
+ @app.call(env)
31
+ env[:result] << "B"
32
+ end
33
+ end
34
+
35
+ env = { :result => [] }
36
+ instance = described_class.new([a, b])
37
+ instance.call(env)
38
+ env[:result].should == ["A", "B", "B", "A"]
39
+ end
40
+
41
+ it "should call lambdas in the proper order" do
42
+ data = []
43
+ a = lambda { |env| data << "A" }
44
+ b = lambda { |env| data << "B" }
45
+
46
+ instance = described_class.new([a, b])
47
+ instance.call({})
48
+
49
+ data.should == ["A", "B"]
50
+ end
51
+
52
+ it "passes in arguments if given" do
53
+ a = Class.new do
54
+ def initialize(app, value)
55
+ @app = app
56
+ @value = value
57
+ end
58
+
59
+ def call(env)
60
+ env[:result] = @value
61
+ end
62
+ end
63
+
64
+ env = {}
65
+ instance = described_class.new([[a, 42]])
66
+ instance.call(env)
67
+
68
+ env[:result].should == 42
69
+ end
70
+
71
+ it "passes in a block if given" do
72
+ a = Class.new do
73
+ def initialize(app, &block)
74
+ @block = block
75
+ end
76
+
77
+ def call(env)
78
+ env[:result] = @block.call
79
+ end
80
+ end
81
+
82
+ block = Proc.new { 42 }
83
+ env = {}
84
+ instance = described_class.new([[a, nil, block]])
85
+ instance.call(env)
86
+
87
+ env[:result].should == 42
88
+ end
89
+
90
+ it "should raise an error if an invalid middleware is given" do
91
+ expect { described_class.new([27]) }.to
92
+ raise_error
93
+ end
94
+ end
@@ -0,0 +1,10 @@
1
+ require "rubygems"
2
+ require "rspec/autorun"
3
+
4
+ # Do not buffer output
5
+ $stdout.sync = true
6
+ $stderr.sync = true
7
+
8
+ # Configure RSpec
9
+ RSpec.configure do |c|
10
+ end
@@ -0,0 +1,189 @@
1
+ # Middleware User Guide
2
+
3
+ `middleware` is a library which provides a generalized implementation
4
+ of the middleware pattern for Ruby. The middleware pattern is a useful
5
+ abstraction tool in various cases, but is specifically useful for splitting
6
+ large sequential chunks of logic into small pieces.
7
+
8
+ ## Installing
9
+
10
+ Middleware is distributed as a RubyGem, so simply gem install:
11
+
12
+ gem install middleware
13
+
14
+ ## A Basic Example
15
+
16
+ Below is a basic example of the library in use. If you don't understand
17
+ what middleware is, please read below. This example is simply meant to give
18
+ you a quick idea of what the library looks like.
19
+
20
+ ```ruby
21
+ # Basic middleware that just prints the inbound and
22
+ # outbound steps.
23
+ class Trace
24
+ def initialize(app, value)
25
+ @app = app
26
+ @value = value
27
+ end
28
+
29
+ def call(env)
30
+ puts "--> #{@value}"
31
+ @app.call(env)
32
+ puts "<-- #{@value}"
33
+ end
34
+ end
35
+
36
+ # Build the actual middleware stack which runs a sequence
37
+ # of slightly different versions of our middleware.
38
+ stack = Middleware::Builder.new do
39
+ use Trace, "A"
40
+ use Trace, "B"
41
+ use Trace, "C"
42
+ end
43
+
44
+ # Run it!
45
+ stack.call(nil)
46
+ ```
47
+
48
+ And the output:
49
+
50
+ ```
51
+ --> A
52
+ --> B
53
+ --> C
54
+ <-- C
55
+ <-- B
56
+ <-- A
57
+ ```
58
+
59
+
60
+
61
+ ## Middleware
62
+
63
+ ### What is it?
64
+
65
+ Middleware is a reusable chunk of logic that is called to perform some
66
+ action. The middleware itself is responsible for calling up the next item
67
+ in the middleware chain using a recursive-like call. This allows middleware
68
+ to perform logic both _before_ and _after_ something is done.
69
+
70
+ The canonical middleware example is in web request processing, and middleware
71
+ is used heavily by both [Rack](#) and [Rails](#).
72
+ In web processing, the first middleware is called with some information about
73
+ the web request, such as HTTP headers, request URL, etc. The middleware is
74
+ responsible for calling the next middleware, and may modify the request along
75
+ the way. When the middlewares begin returning, the state now has the HTTP
76
+ response, so that the middlewares can then modify the response.
77
+
78
+ Cool? Yeah! And this pattern is generally usable in a wide variety of
79
+ problems.
80
+
81
+ ### Middleware Classes
82
+
83
+ One method of creating middleware, and by far the most common, is to define
84
+ a class that duck types to the following interface:
85
+
86
+ class MiddlewareExample
87
+ def initialize(app); end
88
+ def call(env); end
89
+ end
90
+
91
+ Therefore, a basic middleware example follows:
92
+
93
+ class Trace
94
+ def initialize(app)
95
+ @app = app
96
+ end
97
+
98
+ def call(env)
99
+ puts "Trace up"
100
+ @app.call(env)
101
+ puts "Trace down"
102
+ end
103
+ end
104
+
105
+ A basic description of the two methods that a middleware must implement:
106
+
107
+ * **initialize(app)** - This is a constructor. It can take additional arguments
108
+ but the first argument sent will always be the next middleware to call, called
109
+ `app` for historical reasons. This should be stored away for later.
110
+
111
+ * **call(env)** - This is what is actually invoked to do work. `env` is just some
112
+ state sent in (defined by the caller, but usually a Hash). This call should also
113
+ call `app.call(env)` at some point to move on.
114
+
115
+ ### Middleware Lambdas
116
+
117
+ A middleware can also be a simple lambda. The downside of using a lambda is that
118
+ it only has access to the state on the initial call, there is no "post" step for
119
+ lambdas. A basic example, in the context of a web request:
120
+
121
+ lambda { |env| puts "You requested: #{env["http.request_url"]}" }
122
+
123
+ ## Middleware Stacks
124
+
125
+ Middlewares on their own are useful as small chunks of logic, but their real
126
+ power comes from building them up into a _stack_. A stack of middlewares are
127
+ executed in the order given.
128
+
129
+ ### Basic Building and Running
130
+
131
+ The middleware library comes with a `Builder` class which provides a nice DSL
132
+ for building a stack of middlewares:
133
+
134
+ stack = Middleware::Builder.new do
135
+ use Trace
136
+ use lambda { |env| puts "LAMBDA!" }
137
+ end
138
+
139
+ This `stack` variable itself is now a valid middleware and has the same interface,
140
+ so to execute the stack, just call `call` on it:
141
+
142
+ stack.call
143
+
144
+ The call method takes an optional parameter which is the state to pass into the
145
+ initial middleware.
146
+
147
+ ### Manipulating a Stack
148
+
149
+ Stacks also provide a set of methods for manipulating the middleware stack. This
150
+ lets you insert, replace, and delete middleware after a stack has already been
151
+ created. Given the `stack` variable created above, we can manipulate it as
152
+ follows. Please imagine that each example runs with the original `stack` variable,
153
+ so that the order of the examples doesn't actually matter:
154
+
155
+ # Insert a new item after the Trace middleware
156
+ stack.insert_after(Trace, SomeOtherMiddleware)
157
+
158
+ # Replace the lambda
159
+ stack.replace(1, SomeOtherMiddleware)
160
+
161
+ # Delete the lambda
162
+ stack.delete(1)
163
+
164
+ ### Passing Additional Constructor Arguments
165
+
166
+ When using middleware in a stack, you can also pass in additional constructor
167
+ arguments. Given the following middleware:
168
+
169
+ class Echo
170
+ def initialize(app, message)
171
+ @app = app
172
+ @message = message
173
+ end
174
+
175
+ def call(env)
176
+ puts @message
177
+ @app.call(env)
178
+ end
179
+ end
180
+
181
+ We can initialize `Echo` with a proper message as follows:
182
+
183
+ Middleware::Builder.new do
184
+ use Echo, "Hello, World!"
185
+ end
186
+
187
+ Then when the stack is called, it will output "Hello, World!"
188
+
189
+ Note that you can also pass blocks in using the `use` method.
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: middleware
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Mitchell Hashimoto
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-03-16 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rake
22
+ prerelease: false
23
+ type: :development
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: redcarpet
36
+ prerelease: false
37
+ type: :development
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 11
44
+ segments:
45
+ - 2
46
+ - 1
47
+ - 0
48
+ version: 2.1.0
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec-core
52
+ prerelease: false
53
+ type: :development
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 47
60
+ segments:
61
+ - 2
62
+ - 8
63
+ - 0
64
+ version: 2.8.0
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: rspec-expectations
68
+ prerelease: false
69
+ type: :development
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ hash: 47
76
+ segments:
77
+ - 2
78
+ - 8
79
+ - 0
80
+ version: 2.8.0
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec-mocks
84
+ prerelease: false
85
+ type: :development
86
+ requirement: &id005 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ hash: 47
92
+ segments:
93
+ - 2
94
+ - 8
95
+ - 0
96
+ version: 2.8.0
97
+ version_requirements: *id005
98
+ - !ruby/object:Gem::Dependency
99
+ name: yard
100
+ prerelease: false
101
+ type: :development
102
+ requirement: &id006 !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ~>
106
+ - !ruby/object:Gem::Version
107
+ hash: 9
108
+ segments:
109
+ - 0
110
+ - 7
111
+ - 5
112
+ version: 0.7.5
113
+ version_requirements: *id006
114
+ description: Generalized implementation of the middleware abstraction for Ruby.
115
+ email:
116
+ - mitchell.hashimoto@gmail.com
117
+ executables: []
118
+
119
+ extensions: []
120
+
121
+ extra_rdoc_files: []
122
+
123
+ files:
124
+ - .gitignore
125
+ - .travis.yml
126
+ - .yardopts
127
+ - CHANGELOG.md
128
+ - Gemfile
129
+ - LICENSE
130
+ - README.md
131
+ - Rakefile
132
+ - lib/middleware.rb
133
+ - lib/middleware/builder.rb
134
+ - lib/middleware/runner.rb
135
+ - lib/middleware/version.rb
136
+ - middleware.gemspec
137
+ - spec/middleware/builder_spec.rb
138
+ - spec/middleware/runner_spec.rb
139
+ - spec/setup.rb
140
+ - user_guide.md
141
+ homepage: https://github.com/mitchellh/middleware
142
+ licenses: []
143
+
144
+ post_install_message:
145
+ rdoc_options: []
146
+
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 3
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ requirements: []
168
+
169
+ rubyforge_project:
170
+ rubygems_version: 1.8.6
171
+ signing_key:
172
+ specification_version: 3
173
+ summary: Generalized implementation of the middleware abstraction for Ruby.
174
+ test_files:
175
+ - spec/middleware/builder_spec.rb
176
+ - spec/middleware/runner_spec.rb
177
+ - spec/setup.rb
178
+ has_rdoc: