middleware-0.1.0/0000755000004100000410000000000012576036676013677 5ustar www-datawww-datamiddleware-0.1.0/Rakefile0000644000004100000410000000027512576036676015350 0ustar www-datawww-data#!/usr/bin/env rake require "bundler/gem_tasks" require "rspec/core/rake_task" # RSpec test task RSpec::Core::RakeTask.new # Make sure the default is to run RSpec task :default => "spec" middleware-0.1.0/Gemfile0000644000004100000410000000013712576036676015173 0ustar www-datawww-datasource 'https://rubygems.org' # Specify your gem's dependencies in middleware.gemspec gemspec middleware-0.1.0/spec/0000755000004100000410000000000012576036676014631 5ustar www-datawww-datamiddleware-0.1.0/spec/middleware/0000755000004100000410000000000012576036676016746 5ustar www-datawww-datamiddleware-0.1.0/spec/middleware/runner_spec.rb0000644000004100000410000000356112576036676021623 0ustar www-datawww-datarequire File.expand_path("../../setup", __FILE__) require "middleware" describe Middleware::Runner do it "should work with an empty stack" do instance = described_class.new([]) expect { instance.call({}) }.to_not raise_error end it "should call classes in the proper order" do a = Class.new do def initialize(app) @app = app end def call(env) env[:result] << "A" @app.call(env) env[:result] << "A" end end b = Class.new do def initialize(app) @app = app end def call(env) env[:result] << "B" @app.call(env) env[:result] << "B" end end env = { :result => [] } instance = described_class.new([a, b]) instance.call(env) env[:result].should == ["A", "B", "B", "A"] end it "should call lambdas in the proper order" do data = [] a = lambda { |env| data << "A" } b = lambda { |env| data << "B" } instance = described_class.new([a, b]) instance.call({}) data.should == ["A", "B"] end it "passes in arguments if given" do a = Class.new do def initialize(app, value) @app = app @value = value end def call(env) env[:result] = @value end end env = {} instance = described_class.new([[a, 42]]) instance.call(env) env[:result].should == 42 end it "passes in a block if given" do a = Class.new do def initialize(app, &block) @block = block end def call(env) env[:result] = @block.call end end block = Proc.new { 42 } env = {} instance = described_class.new([[a, nil, block]]) instance.call(env) env[:result].should == 42 end it "should raise an error if an invalid middleware is given" do expect { described_class.new([27]) }.to raise_error end end middleware-0.1.0/spec/middleware/builder_spec.rb0000644000004100000410000000673012576036676021741 0ustar www-datawww-datarequire File.expand_path("../../setup", __FILE__) require "middleware" describe Middleware::Builder do let(:data) { { :data => [] } } let(:instance) { described_class.new } # This returns a proc that can be used with the builder # that simply appends data to an array in the env. def appender_proc(data) Proc.new { |env| env[:data] << data } end context "basic `use`" do it "should add items to the stack and make them callable" do data = {} proc = Proc.new { |env| env[:data] = true } instance.use proc instance.call(data) data[:data].should == true end it "should be able to add multiple items" do data = {} proc1 = Proc.new { |env| env[:one] = true } proc2 = Proc.new { |env| env[:two] = true } instance.use proc1 instance.use proc2 instance.call(data) data[:one].should == true data[:two].should == true end it "should be able to add another builder" do data = {} proc1 = Proc.new { |env| env[:one] = true } # Build the first builder one = described_class.new one.use proc1 # Add it to this builder two = described_class.new two.use one # Call the 2nd and verify results two.call(data) data[:one].should == true end it "should default the env to `nil` if not given" do result = false proc = Proc.new { |env| result = env.nil? } instance.use proc instance.call result.should be end end context "inserting" do it "can insert at an index" do instance.use appender_proc(1) instance.insert(0, appender_proc(2)) instance.call(data) data[:data].should == [2, 1] end it "can insert next to a previous object" do proc2 = appender_proc(2) instance.use appender_proc(1) instance.use proc2 instance.insert(proc2, appender_proc(3)) instance.call(data) data[:data].should == [1, 3, 2] end it "can insert before" do instance.use appender_proc(1) instance.insert_before 0, appender_proc(2) instance.call(data) data[:data].should == [2, 1] end it "can insert after" do instance.use appender_proc(1) instance.use appender_proc(3) instance.insert_after 0, appender_proc(2) instance.call(data) data[:data].should == [1, 2, 3] end it "raises an exception if an invalid object given" do expect { instance.insert_after "object", appender_proc(1) }. to raise_error(RuntimeError) end end context "replace" do it "can replace an object" do proc1 = appender_proc(1) proc2 = appender_proc(2) instance.use proc1 instance.replace proc1, proc2 instance.call(data) data[:data].should == [2] end it "can replace by index" do proc1 = appender_proc(1) proc2 = appender_proc(2) instance.use proc1 instance.replace 0, proc2 instance.call(data) data[:data].should == [2] end end context "deleting" do it "can delete by object" do proc1 = appender_proc(1) instance.use proc1 instance.use appender_proc(2) instance.delete proc1 instance.call(data) data[:data].should == [2] end it "can delete by index" do proc1 = appender_proc(1) instance.use proc1 instance.use appender_proc(2) instance.delete 0 instance.call(data) data[:data].should == [2] end end end middleware-0.1.0/spec/setup.rb0000644000004100000410000000023112576036676016312 0ustar www-datawww-datarequire "rubygems" require "rspec/autorun" # Do not buffer output $stdout.sync = true $stderr.sync = true # Configure RSpec RSpec.configure do |c| end middleware-0.1.0/.travis.yml0000644000004100000410000000022712576036676016011 0ustar www-datawww-datalanguage: ruby rvm: - 1.8.7 - 1.9.2 - 1.9.3 - jruby-18mode - jruby-19mode - rbx-18mode - rbx-19mode - ruby-head - jruby-head - ree middleware-0.1.0/lib/0000755000004100000410000000000012576036676014445 5ustar www-datawww-datamiddleware-0.1.0/lib/middleware/0000755000004100000410000000000012576036676016562 5ustar www-datawww-datamiddleware-0.1.0/lib/middleware/version.rb0000644000004100000410000000005212576036676020571 0ustar www-datawww-datamodule Middleware VERSION = "0.1.0" end middleware-0.1.0/lib/middleware/runner.rb0000644000004100000410000000502512576036676020422 0ustar www-datawww-datamodule Middleware # This is a basic runner for middleware stacks. This runner does # the default expected behavior of running the middleware stacks # in order, then reversing the order. class Runner # A middleware which does nothing EMPTY_MIDDLEWARE = lambda { |env| } # Build a new middleware runner with the given middleware # stack. # # Note: This class usually doesn't need to be used directly. # Instead, take a look at using the {Builder} class, which is # a much friendlier way to build up a middleware stack. # # @param [Array] stack An array of the middleware to run. def initialize(stack) # We need to take the stack of middleware and initialize them # all so they call the proper next middleware. @kickoff = build_call_chain(stack) end # Run the middleware stack with the given state bag. # # @param [Object] env The state to pass into as the initial # environment data. This is usual a hash of some sort. def call(env) # We just call the kickoff middleware, which is responsible # for properly calling the next middleware, and so on and so # forth. @kickoff.call(env) end protected # This takes a stack of middlewares and initializes them in a way # that each middleware properly calls the next middleware. def build_call_chain(stack) # We need to instantiate the middleware stack in reverse # order so that each middleware can have a reference to # the next middleware it has to call. The final middleware # is always the empty middleware, which does nothing but return. stack.reverse.inject(EMPTY_MIDDLEWARE) do |next_middleware, current_middleware| # Unpack the actual item klass, args, block = current_middleware # Default the arguments to an empty array. Otherwise in Ruby 1.8 # a `nil` args will actually pass `nil` into the class. Not what # we want! args ||= [] if klass.is_a?(Class) # If the klass actually is a class, then instantiate it with # the app and any other arguments given. klass.new(next_middleware, *args, &block) elsif klass.respond_to?(:call) # Make it a lambda which calls the item then forwards up # the chain. lambda do |env| klass.call(env) next_middleware.call(env) end else raise "Invalid middleware, doesn't respond to `call`: #{action.inspect}" end end end end end middleware-0.1.0/lib/middleware/builder.rb0000644000004100000410000000736712576036676020552 0ustar www-datawww-datamodule Middleware # This provides a DSL for building up a stack of middlewares. # # This code is based heavily off of `Rack::Builder` and # `ActionDispatch::MiddlewareStack` in Rack and Rails, respectively. # # # Usage # # Building a middleware stack is very easy: # # app = Middleware::Builder.new do # use A # use B # end # # # Call the middleware # app.call(7) # class Builder # Initializes the builder. An optional block can be passed which # will be evaluated in the context of the instance. # # Example: # # Builder.new do # use A # use B # end # # @param [Hash] opts Options hash # @option opts [Class] :runner_class The class to wrap the middleware stack # in which knows how to run them. # @yield [] Evaluated in this instance which allows you to use methods # like {#use} and such. def initialize(opts=nil, &block) opts ||= {} @runner_class = opts[:runner_class] || Runner instance_eval(&block) if block_given? end # Returns a mergeable version of the builder. If `use` is called with # the return value of this method, then the stack will merge, instead # of being treated as a separate single middleware. def flatten lambda do |env| self.call(env) end end # Adds a middleware class to the middleware stack. Any additional # args and a block, if given, are saved and passed to the initializer # of the middleware. # # @param [Class] middleware The middleware class def use(middleware, *args, &block) if middleware.kind_of?(Builder) # Merge in the other builder's stack into our own self.stack.concat(middleware.stack) else self.stack << [middleware, args, block] end self end # Inserts a middleware at the given index or directly before the # given middleware object. def insert(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) stack.insert(index, [middleware, args, block]) end alias_method :insert_before, :insert # Inserts a middleware after the given index or middleware object. def insert_after(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert after: #{index.inspect}" unless index insert(index + 1, middleware, *args, &block) end # Replaces the given middlware object or index with the new # middleware. def replace(index, middleware, *args, &block) if index.is_a?(Integer) delete(index) insert(index, middleware, *args, &block) else insert_before(index, middleware, *args, &block) delete(index) end end # Deletes the given middleware object or index def delete(index) index = self.index(index) unless index.is_a?(Integer) stack.delete_at(index) end # Runs the builder stack with the given environment. def call(env=nil) to_app.call(env) end protected # Returns the numeric index for the given middleware object. # # @param [Object] object The item to find the index for # @return [Integer] def index(object) stack.each_with_index do |item, i| return i if item[0] == object end nil end # Returns the current stack of middlewares. You probably won't # need to use this directly, and it's recommended that you don't. # # @return [Array] def stack @stack ||= [] end # Converts the builder stack to a runnable action sequence. # # @return [Object] A callable object def to_app @runner_class.new(stack.dup) end end end middleware-0.1.0/lib/middleware.rb0000644000004100000410000000012612576036676017106 0ustar www-datawww-datarequire "middleware/version" require "middleware/builder" require "middleware/runner" middleware-0.1.0/metadata.yml0000644000004100000410000000713012576036676016203 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: middleware version: !ruby/object:Gem::Version hash: 27 prerelease: segments: - 0 - 1 - 0 version: 0.1.0 platform: ruby authors: - Mitchell Hashimoto autorequire: bindir: bin cert_chain: [] date: 2012-03-16 00:00:00 Z dependencies: - !ruby/object:Gem::Dependency name: rake prerelease: false type: :development requirement: &id001 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" version_requirements: *id001 - !ruby/object:Gem::Dependency name: redcarpet prerelease: false type: :development requirement: &id002 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 11 segments: - 2 - 1 - 0 version: 2.1.0 version_requirements: *id002 - !ruby/object:Gem::Dependency name: rspec-core prerelease: false type: :development requirement: &id003 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 47 segments: - 2 - 8 - 0 version: 2.8.0 version_requirements: *id003 - !ruby/object:Gem::Dependency name: rspec-expectations prerelease: false type: :development requirement: &id004 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 47 segments: - 2 - 8 - 0 version: 2.8.0 version_requirements: *id004 - !ruby/object:Gem::Dependency name: rspec-mocks prerelease: false type: :development requirement: &id005 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 47 segments: - 2 - 8 - 0 version: 2.8.0 version_requirements: *id005 - !ruby/object:Gem::Dependency name: yard prerelease: false type: :development requirement: &id006 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 9 segments: - 0 - 7 - 5 version: 0.7.5 version_requirements: *id006 description: Generalized implementation of the middleware abstraction for Ruby. email: - mitchell.hashimoto@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - .gitignore - .travis.yml - .yardopts - CHANGELOG.md - Gemfile - LICENSE - README.md - Rakefile - lib/middleware.rb - lib/middleware/builder.rb - lib/middleware/runner.rb - lib/middleware/version.rb - middleware.gemspec - spec/middleware/builder_spec.rb - spec/middleware/runner_spec.rb - spec/setup.rb - user_guide.md homepage: https://github.com/mitchellh/middleware licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: rubygems_version: 1.8.6 signing_key: specification_version: 3 summary: Generalized implementation of the middleware abstraction for Ruby. test_files: - spec/middleware/builder_spec.rb - spec/middleware/runner_spec.rb - spec/setup.rb has_rdoc: middleware-0.1.0/middleware.gemspec0000644000004100000410000000215012576036676017357 0ustar www-datawww-data# -*- encoding: utf-8 -*- require File.expand_path('../lib/middleware/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Mitchell Hashimoto"] gem.email = ["mitchell.hashimoto@gmail.com"] gem.description = %q{Generalized implementation of the middleware abstraction for Ruby.} gem.summary = %q{Generalized implementation of the middleware abstraction for Ruby.} gem.homepage = "https://github.com/mitchellh/middleware" gem.add_development_dependency "rake" gem.add_development_dependency "redcarpet", "~> 2.1.0" gem.add_development_dependency "rspec-core", "~> 2.8.0" gem.add_development_dependency "rspec-expectations", "~> 2.8.0" gem.add_development_dependency "rspec-mocks", "~> 2.8.0" gem.add_development_dependency "yard", "~> 0.7.5" gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") gem.name = "middleware" gem.require_paths = ["lib"] gem.version = Middleware::VERSION end middleware-0.1.0/.gitignore0000644000004100000410000000023212576036676015664 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp middleware-0.1.0/.yardopts0000644000004100000410000000004212576036676015541 0ustar www-datawww-data-m markdown --files user_guide.md middleware-0.1.0/LICENSE0000644000004100000410000000206212576036676014704 0ustar www-datawww-dataCopyright (c) 2012 Mitchell Hashimoto MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.middleware-0.1.0/CHANGELOG.md0000644000004100000410000000016012576036676015505 0ustar www-datawww-data## 0.1.0 (March 16, 2012) - Initial release, almost directly extracted from [Vagrant](http://vagrantup.com) middleware-0.1.0/README.md0000644000004100000410000000257412576036676015166 0ustar www-datawww-data# Middleware [![Build Status](https://secure.travis-ci.org/mitchellh/middleware.png?branch=master)](http://travis-ci.org/mitchellh/middleware) This is a generalized library for using middleware patterns within your Ruby projects. To get started, the best place to look is [the user guide](https://github.com/mitchellh/middleware/blob/master/user_guide.md). ## Installation This project is distributed as a RubyGem: $ gem install middleware ## Usage Once you create a basic middleware, you can use the builder to have a nice DSL to build middleware stacks. Calling the middleware is simple, as well. ```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 ``` ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request middleware-0.1.0/user_guide.md0000644000004100000410000001233712576036676016362 0ustar www-datawww-data# 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.