ramontayag-middleware 0.2.0.dev

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,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode
7
+ - rbx-18mode
8
+ - rbx-19mode
9
+ - ruby-head
10
+ - jruby-head
11
+ - ree
@@ -0,0 +1,3 @@
1
+ -m markdown
2
+ --files
3
+ user_guide.md
@@ -0,0 +1,8 @@
1
+ ## 0.2.0 (unreleased)
2
+
3
+
4
+
5
+ ## 0.1.0 (March 16, 2012)
6
+
7
+ - Initial release, almost directly extracted from [Vagrant](http://vagrantup.com)
8
+
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,69 @@
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
+ ```console
15
+ $ gem install middleware
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Once you create a basic middleware, you can use the builder to
21
+ have a nice DSL to build middleware stacks. Calling the middleware
22
+ is simple, as well.
23
+
24
+ ```ruby
25
+ # Basic middleware that just prints the inbound and
26
+ # outbound steps.
27
+ class Trace
28
+ def initialize(app, value)
29
+ @app = app
30
+ @value = value
31
+ end
32
+
33
+ def call(env)
34
+ puts "--> #{@value}"
35
+ @app.call(env)
36
+ puts "<-- #{@value}"
37
+ end
38
+ end
39
+
40
+ # Build the actual middleware stack which runs a sequence
41
+ # of slightly different versions of our middleware.
42
+ stack = Middleware::Builder.new do
43
+ use Trace, "A"
44
+ use Trace, "B"
45
+ use Trace, "C"
46
+ end
47
+
48
+ # Run it!
49
+ stack.call(nil)
50
+ ```
51
+
52
+ And the output:
53
+
54
+ ```
55
+ --> A
56
+ --> B
57
+ --> C
58
+ <-- C
59
+ <-- B
60
+ <-- A
61
+ ```
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 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,135 @@
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
+ raise "no such middleware to insert before: #{index.inspect}" unless index
71
+ stack.insert(index, [middleware, args, block])
72
+ end
73
+
74
+ alias_method :insert_before, :insert
75
+
76
+ # Inserts a middleware after the given index or middleware object.
77
+ def insert_after(index, middleware, *args, &block)
78
+ index = self.index(index) unless index.is_a?(Integer)
79
+ raise "no such middleware to insert after: #{index.inspect}" unless index
80
+ insert(index + 1, middleware, *args, &block)
81
+ end
82
+
83
+ # Replaces the given middleware object or index with the new
84
+ # middleware.
85
+ def replace(index, middleware, *args, &block)
86
+ if index.is_a?(Integer)
87
+ delete(index)
88
+ insert(index, middleware, *args, &block)
89
+ else
90
+ insert_before(index, middleware, *args, &block)
91
+ delete(index)
92
+ end
93
+ end
94
+
95
+ # Deletes the given middleware object or index
96
+ def delete(index)
97
+ index = self.index(index) unless index.is_a?(Integer)
98
+ stack.delete_at(index)
99
+ end
100
+
101
+ # Runs the builder stack with the given environment.
102
+ def call(env=nil)
103
+ to_app.call(env)
104
+ end
105
+
106
+ protected
107
+
108
+ # Returns the numeric index for the given middleware object.
109
+ #
110
+ # @param [Object] object The item to find the index for
111
+ # @return [Integer]
112
+ def index(object)
113
+ stack.each_with_index do |item, i|
114
+ return i if item[0] == object
115
+ end
116
+
117
+ nil
118
+ end
119
+
120
+ # Returns the current stack of middlewares. You probably won't
121
+ # need to use this directly, and it's recommended that you don't.
122
+ #
123
+ # @return [Array]
124
+ def stack
125
+ @stack ||= []
126
+ end
127
+
128
+ # Converts the builder stack to a runnable action sequence.
129
+ #
130
+ # @return [Object] A callable object
131
+ def to_app
132
+ @runner_class.new(stack.dup)
133
+ end
134
+ end
135
+ 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| 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.2.0.dev"
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/ramontayag/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 = "ramontayag-middleware"
22
+ gem.require_paths = ["lib"]
23
+ gem.version = Middleware::VERSION
24
+ end
@@ -0,0 +1,160 @@
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 "raises an exception if attempting to insert before an invalid object" do
94
+ expect { instance.insert "object", appender_proc(1) }.
95
+ to raise_error(RuntimeError)
96
+ end
97
+
98
+ it "can insert after" do
99
+ instance.use appender_proc(1)
100
+ instance.use appender_proc(3)
101
+ instance.insert_after 0, appender_proc(2)
102
+ instance.call(data)
103
+
104
+ data[:data].should == [1, 2, 3]
105
+ end
106
+
107
+ it "raises an exception if attempting to insert after an invalid object" do
108
+ expect { instance.insert_after "object", appender_proc(1) }.
109
+ to raise_error(RuntimeError)
110
+ end
111
+ end
112
+
113
+ context "replace" do
114
+ it "can replace an object" do
115
+ proc1 = appender_proc(1)
116
+ proc2 = appender_proc(2)
117
+
118
+ instance.use proc1
119
+ instance.replace proc1, proc2
120
+ instance.call(data)
121
+
122
+ data[:data].should == [2]
123
+ end
124
+
125
+ it "can replace by index" do
126
+ proc1 = appender_proc(1)
127
+ proc2 = appender_proc(2)
128
+
129
+ instance.use proc1
130
+ instance.replace 0, proc2
131
+ instance.call(data)
132
+
133
+ data[:data].should == [2]
134
+ end
135
+ end
136
+
137
+ context "deleting" do
138
+ it "can delete by object" do
139
+ proc1 = appender_proc(1)
140
+
141
+ instance.use proc1
142
+ instance.use appender_proc(2)
143
+ instance.delete proc1
144
+ instance.call(data)
145
+
146
+ data[:data].should == [2]
147
+ end
148
+
149
+ it "can delete by index" do
150
+ proc1 = appender_proc(1)
151
+
152
+ instance.use proc1
153
+ instance.use appender_proc(2)
154
+ instance.delete 0
155
+ instance.call(data)
156
+
157
+ data[:data].should == [2]
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,193 @@
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 raise_error
92
+ end
93
+
94
+ it "should not call middlewares which aren't called" do
95
+ # A does not call B, so B should never execute
96
+ data = []
97
+ a = Class.new do
98
+ def initialize(app); @app = app; end
99
+
100
+ define_method :call do |env|
101
+ data << "a"
102
+ end
103
+ end
104
+
105
+ b = lambda { |env| data << "b" }
106
+
107
+ env = {}
108
+ instance = described_class.new([a, b])
109
+ instance.call(env)
110
+
111
+ data.should == ["a"]
112
+ end
113
+
114
+ describe "exceptions" do
115
+ it "should propagate the exception up the middleware chain" do
116
+ # This tests a few important properties:
117
+ # * Exceptions propagate multiple middlewares
118
+ # - C raises an exception, which raises through B to A.
119
+ # * Rescuing exceptions works
120
+ data = []
121
+ a = Class.new do
122
+ def initialize(app)
123
+ @app = app
124
+ end
125
+
126
+ define_method :call do |env|
127
+ data << "a"
128
+ begin
129
+ @app.call(env)
130
+ data << "never"
131
+ rescue Exception => e
132
+ data << "e"
133
+ raise
134
+ end
135
+ end
136
+ end
137
+
138
+ b = Class.new do
139
+ def initialize(app); @app = app; end
140
+
141
+ define_method :call do |env|
142
+ data << "b"
143
+ @app.call(env)
144
+ end
145
+ end
146
+
147
+ c = lambda { |env| raise "ERROR" }
148
+
149
+ env = {}
150
+ instance = described_class.new([a, b, c])
151
+ expect { instance.call(env) }.to raise_error
152
+
153
+ data.should == ["a", "b", "e"]
154
+ end
155
+
156
+ it "should stop propagation if rescued" do
157
+ # This test mainly tests that if there is a sequence A, B, C, and
158
+ # an exception is raised in C, that if B rescues this, then the chain
159
+ # continues fine backwards.
160
+ data = []
161
+ a = Class.new do
162
+ def initialize(app); @app = app; end
163
+
164
+ define_method :call do |env|
165
+ data << "in_a"
166
+ @app.call(env)
167
+ data << "out_a"
168
+ end
169
+ end
170
+
171
+ b = Class.new do
172
+ def initialize(app); @app = app; end
173
+
174
+ define_method :call do |env|
175
+ data << "in_b"
176
+ @app.call(env) rescue nil
177
+ data << "out_b"
178
+ end
179
+ end
180
+
181
+ c = lambda do |env|
182
+ data << "in_c"
183
+ raise "BAD"
184
+ end
185
+
186
+ env = {}
187
+ instance = described_class.new([a, b, c])
188
+ instance.call(env)
189
+
190
+ data.should == ["in_a", "in_b", "in_c", "out_b", "out_a"]
191
+ end
192
+ end
193
+ 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,207 @@
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
+ ```console
13
+ $ gem install middleware
14
+ ```
15
+
16
+ ## A Basic Example
17
+
18
+ Below is a basic example of the library in use. If you don't understand
19
+ what middleware is, please read below. This example is simply meant to give
20
+ you a quick idea of what the library looks like.
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
+
62
+
63
+ ## Middleware
64
+
65
+ ### What is it?
66
+
67
+ Middleware is a reusable chunk of logic that is called to perform some
68
+ action. The middleware itself is responsible for calling up the next item
69
+ in the middleware chain using a recursive-like call. This allows middleware
70
+ to perform logic both _before_ and _after_ something is done.
71
+
72
+ The canonical middleware example is in web request processing, and middleware
73
+ is used heavily by both [Rack](#) and [Rails](#).
74
+ In web processing, the first middleware is called with some information about
75
+ the web request, such as HTTP headers, request URL, etc. The middleware is
76
+ responsible for calling the next middleware, and may modify the request along
77
+ the way. When the middlewares begin returning, the state now has the HTTP
78
+ response, so that the middlewares can then modify the response.
79
+
80
+ Cool? Yeah! And this pattern is generally usable in a wide variety of
81
+ problems.
82
+
83
+ ### Middleware Classes
84
+
85
+ One method of creating middleware, and by far the most common, is to define
86
+ a class that duck types to the following interface:
87
+
88
+ ```ruby
89
+ class MiddlewareExample
90
+ def initialize(app); end
91
+ def call(env); end
92
+ end
93
+ ```
94
+
95
+ Therefore, a basic middleware example follows:
96
+
97
+ ```ruby
98
+ class Trace
99
+ def initialize(app)
100
+ @app = app
101
+ end
102
+
103
+ def call(env)
104
+ puts "Trace up"
105
+ @app.call(env)
106
+ puts "Trace down"
107
+ end
108
+ end
109
+ ```
110
+
111
+ A basic description of the two methods that a middleware must implement:
112
+
113
+ * **initialize(app)** - This is a constructor. It can take additional arguments
114
+ but the first argument sent will always be the next middleware to call, called
115
+ `app` for historical reasons. This should be stored away for later.
116
+
117
+ * **call(env)** - This is what is actually invoked to do work. `env` is just some
118
+ state sent in (defined by the caller, but usually a Hash). This call should also
119
+ call `app.call(env)` at some point to move on.
120
+
121
+ ### Middleware Lambdas
122
+
123
+ A middleware can also be a simple lambda. The downside of using a lambda is that
124
+ it only has access to the state on the initial call, there is no "post" step for
125
+ lambdas. A basic example, in the context of a web request:
126
+
127
+ ```ruby
128
+ lambda { |env| puts "You requested: #{env["http.request_url"]}" }
129
+ ```
130
+
131
+ ## Middleware Stacks
132
+
133
+ Middlewares on their own are useful as small chunks of logic, but their real
134
+ power comes from building them up into a _stack_. A stack of middlewares are
135
+ executed in the order given.
136
+
137
+ ### Basic Building and Running
138
+
139
+ The middleware library comes with a `Builder` class which provides a nice DSL
140
+ for building a stack of middlewares:
141
+
142
+ ```ruby
143
+ stack = Middleware::Builder.new do
144
+ use Trace
145
+ use lambda { |env| puts "LAMBDA!" }
146
+ end
147
+ ```
148
+
149
+ This `stack` variable itself is now a valid middleware and has the same interface,
150
+ so to execute the stack, just call `call` on it:
151
+
152
+ ```ruby
153
+ stack.call
154
+ ```
155
+
156
+ The call method takes an optional parameter which is the state to pass into the
157
+ initial middleware.
158
+
159
+ ### Manipulating a Stack
160
+
161
+ Stacks also provide a set of methods for manipulating the middleware stack. This
162
+ lets you insert, replace, and delete middleware after a stack has already been
163
+ created. Given the `stack` variable created above, we can manipulate it as
164
+ follows. Please imagine that each example runs with the original `stack` variable,
165
+ so that the order of the examples doesn't actually matter:
166
+
167
+ ```ruby
168
+ # Insert a new item after the Trace middleware
169
+ stack.insert_after(Trace, SomeOtherMiddleware)
170
+
171
+ # Replace the lambda
172
+ stack.replace(1, SomeOtherMiddleware)
173
+
174
+ # Delete the lambda
175
+ stack.delete(1)
176
+ ```
177
+
178
+ ### Passing Additional Constructor Arguments
179
+
180
+ When using middleware in a stack, you can also pass in additional constructor
181
+ arguments. Given the following middleware:
182
+
183
+ ```ruby
184
+ class Echo
185
+ def initialize(app, message)
186
+ @app = app
187
+ @message = message
188
+ end
189
+
190
+ def call(env)
191
+ puts @message
192
+ @app.call(env)
193
+ end
194
+ end
195
+ ```
196
+
197
+ We can initialize `Echo` with a proper message as follows:
198
+
199
+ ```ruby
200
+ Middleware::Builder.new do
201
+ use Echo, "Hello, World!"
202
+ end
203
+ ```
204
+
205
+ Then when the stack is called, it will output "Hello, World!"
206
+
207
+ Note that you can also pass blocks in using the `use` method.
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ramontayag-middleware
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0.dev
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Mitchell Hashimoto
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: redcarpet
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.1.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.1.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec-core
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.8.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.8.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec-expectations
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.8.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.8.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec-mocks
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 2.8.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 2.8.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: yard
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 0.7.5
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 0.7.5
110
+ description: Generalized implementation of the middleware abstraction for Ruby.
111
+ email:
112
+ - mitchell.hashimoto@gmail.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .travis.yml
119
+ - .yardopts
120
+ - CHANGELOG.md
121
+ - Gemfile
122
+ - LICENSE
123
+ - README.md
124
+ - Rakefile
125
+ - lib/middleware.rb
126
+ - lib/middleware/builder.rb
127
+ - lib/middleware/runner.rb
128
+ - lib/middleware/version.rb
129
+ - middleware.gemspec
130
+ - spec/middleware/builder_spec.rb
131
+ - spec/middleware/runner_spec.rb
132
+ - spec/setup.rb
133
+ - user_guide.md
134
+ homepage: https://github.com/ramontayag/middleware
135
+ licenses: []
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>'
150
+ - !ruby/object:Gem::Version
151
+ version: 1.3.1
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 1.8.24
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Generalized implementation of the middleware abstraction for Ruby.
158
+ test_files:
159
+ - spec/middleware/builder_spec.rb
160
+ - spec/middleware/runner_spec.rb
161
+ - spec/setup.rb
162
+ has_rdoc: