{"id":18728678,"url":"https://github.com/rubyonworld/ruby-middleware","last_synced_at":"2025-10-12T20:36:48.911Z","repository":{"id":174008092,"uuid":"542163274","full_name":"RubyOnWorld/ruby-middleware","owner":"RubyOnWorld","description":"Middleware is a library which provides a generalized implementation of the chain of responsibility pattern for Ruby.","archived":false,"fork":false,"pushed_at":"2022-09-28T01:13:53.000Z","size":65,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-19T20:32:28.585Z","etag":null,"topics":["chain","middleware","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RubyOnWorld.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-27T15:40:47.000Z","updated_at":"2022-09-28T04:12:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"76a2041b-bd91-483b-bf39-97fa05b69272","html_url":"https://github.com/RubyOnWorld/ruby-middleware","commit_stats":null,"previous_names":["rubyonworld/ruby-middleware"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/RubyOnWorld/ruby-middleware","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubyOnWorld%2Fruby-middleware","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubyOnWorld%2Fruby-middleware/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubyOnWorld%2Fruby-middleware/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubyOnWorld%2Fruby-middleware/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RubyOnWorld","download_url":"https://codeload.github.com/RubyOnWorld/ruby-middleware/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubyOnWorld%2Fruby-middleware/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279012800,"owners_count":26085187,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chain","middleware","ruby"],"created_at":"2024-11-07T14:23:14.149Z","updated_at":"2025-10-12T20:36:48.906Z","avatar_url":"https://github.com/RubyOnWorld.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Code Climate](https://codeclimate.com/github/Ibsciss/ruby-middleware/badges/gpa.svg)](https://codeclimate.com/github/Ibsciss/ruby-middleware) \n[![Test Coverage](https://codeclimate.com/github/Ibsciss/ruby-middleware/badges/coverage.svg)](https://codeclimate.com/github/Ibsciss/ruby-middleware)\n[![Build Status](https://semaphoreci.com/api/v1/projects/c5797935-6c93-4596-a8a8-bd45c8c584e9/393201/shields_badge.svg)](https://semaphoreci.com/lilobase/ruby-middleware)\n\n# Middleware\n\n[![Join the chat at https://gitter.im/Ibsciss/ruby-middleware](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Ibsciss/ruby-middleware?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n`Middleware` is a library which provides a generalized implementation\nof the [chain of responsibility pattern](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) for Ruby.\n\nThis pattern is used in `Rack::Builder` or `ActionDispatch::MiddlewareStack` to manage a stack of middlewares. This gem is a generic implementation for any Ruby project.\n \n The middleware pattern is a useful\nabstraction tool in various cases, but is specifically useful for splitting\nlarge sequential chunks of logic into small pieces.\n\nThis is an updated version of the original Mitchell Hashimoto's library: https://github.com/mitchellh/middleware\n\n## Installing\n\nMiddleware is distributed as a RubyGem, so simply gem install:\n\n```console\n$ gem install ibsciss-middleware\n```\n\nOr, in your Gemfile:\n\n```\ngem 'ibsciss-middleware', '~\u003e 0.4.2'\n```\n\nThen, you can add it to your project:\n\n```ruby\nrequire 'middleware'\n```\n\n## A Basic Example\n\nBelow is a basic example of the library in use. If you don't understand\nwhat middleware is, please read below. This example is simply meant to give\nyou a quick idea of what the library looks like.\n\n```ruby\n# Basic middleware that just prints the inbound and\n# outbound steps.\nclass Trace\n  def initialize(app, value)\n    @app   = app\n    @value = value\n  end\n\n  def call(env)\n    puts \"--\u003e #{@value}\"\n    @app.call(env)\n    puts \"\u003c-- #{@value}\"\n  end\nend\n\n# Build the actual middleware stack which runs a sequence\n# of slightly different versions of our middleware.\nstack = Middleware::Builder.new do |b|\n  b.use Trace, \"A\"\n  b.use Trace, \"B\"\n  b.use Trace, \"C\"\nend\n\n# Run it!\nstack.call(nil)\n```\n\nAnd the output:\n\n```\n--\u003e A\n--\u003e B\n--\u003e C\n\u003c-- C\n\u003c-- B\n\u003c-- A\n```\n\n\n## Middleware\n\n### What is it?\n\nMiddleware is a reusable chunk of logic that is called to perform some\naction. The middleware itself is responsible for calling up the next item\nin the middleware chain using a recursive-like call. This allows middleware\nto perform logic both _before_ and _after_ something is done.\n\nThe canonical middleware example is in web request processing, and middleware\nis used heavily by both [Rack](#) and [Rails](#).\nIn web processing, the first middleware is called with some information about\nthe web request, such as HTTP headers, request URL, etc. The middleware is\nresponsible for calling the next middleware, and may modify the request along\nthe way. When the middlewares begin returning, the state now has the HTTP\nresponse, so that the middlewares can then modify the response.\n\nCool? Yeah! And this pattern is generally usable in a wide variety of\nproblems.\n\n### Middleware Classes\n\nOne method of creating middleware, and by far the most common, is to define\na class that duck types to the following interface:\n\n```ruby\nclass MiddlewareExample\n  def initialize(app); end\n  def call(env); end\nend\n```\n\nTherefore, a basic middleware example follows:\n\n```ruby\nclass Trace\n  def initialize(app)\n    @app = app\n  end\n\n  def call(env)\n    puts \"Before next middleware execution\"\n    @app.call(env)\n    puts \"After next middleware execution\"\n  end\nend\n```\n\nA basic description of the two methods that a middleware must implement:\n\n  * **initialize(app)** - The first argument sent will always be the next middleware to call, called\n    `app` for historical reasons. This should be stored away for later.\n\n  * **call(env)** - This is what is actually invoked to do work. `env` is just some\n    state sent in (defined by the caller, but usually a Hash). This call should also\n    call `app.call(env)` at some point to move on.\n\nThis architecture offers the biggest advantage of letting you enhance the `env` variable before passing it to the next middleware, and giving you the ability to change the returned data, as follows:\n\n```ruby\nclass Greeting\n  def initialize(app, datas = nil)\n    @app = app\n    @datas = datas\n  end\n  \n  def call(env)\n    env = \"#{@datas} #{env}\"\n    result = @app(env)\n    \"#{result} !\"\n  end\nend\n\nMiddleware::Builder.new { |b|\n  b.use Greeting, 'Hello'\n}.call('John') #return \"Hello John !\"\n```\n\n### Middleware Lambdas\n\nA middleware can also be a simple lambda. The downside of using a lambda is that\nit only has access to the state on the initial call, there is no \"post\" step for\nlambdas:\n\n```ruby\nMiddleware::Builder.new { |b|\n  b.use -\u003e (env) { env + 3 }\n  b.use -\u003e (env) { env * 2 }\n}.call(1) #return 8\n```\n\n## Middleware Stacks\n\nMiddlewares on their own are useful as small chunks of logic, but their real\npower comes from building them up into a _stack_. A stack of middlewares are\nexecuted in the order given.\n\n### Basic Building and Running\n\nThe middleware library comes with a `Builder` class which provides a nice DSL\nfor building a stack of middlewares:\n\n```ruby\nstack = Middleware::Builder.new do |d|\n  d.use Trace\n  d.use -\u003e(env) { puts \"LAMBDA!\" }\nend\n```\n\nThis `stack` variable itself is now a valid middleware and has the same interface,\nso to execute the stack, just call `call` on it, so can compose middleware stack between them:\n\n```ruby\nMiddleware::Builder.new do |d|\n  d.use stack\nend.call()\n```\n\nThe call method takes an optional parameter which is the state to pass into the\ninitial middleware.\n\nYou can optionally set a name, that will be displayed in inspect and for logging purpose:\n\n```ruby\nMiddleware::Builder.new(name: 'MyPersonalMiddleware')\n```\n\n### Manipulating a Stack\n\nStacks also provide a set of methods for manipulating the middleware stack. This\nlets you insert, replace, and delete middleware after a stack has already been\ncreated. Given the `stack` variable created above, we can manipulate it as\nfollows. Please imagine that each example runs with the original `stack` variable,\nso that the order of the examples doesn't actually matter:\n\n#### Insert before\n\n```ruby\n# Insert a new item before the Trace middleware\nstack.insert_before Trace, SomeOtherMiddleware\n\n# Insert a new item at the top of the middleware stack\nstack.insert_before 0, SomeOtherMiddleware\n```\n\n#### Insert after\n\n```ruby\n# Insert a new item after the Trace middleware\nstack.insert_after(Trace, SomeOtherMiddleware)\n\n# Insert a new item after the first middleware\nstack.insert_after(0, SomeOtherMiddleware)\n```\n\n#### Insert after each\n\n```ruby\nlogger = -\u003e (env) { p env }\n\n# Insert the middleware (can be also a middleware object) after each existing middleware\nstack.insert_after_each logger\n```\n\n#### Insert before each\n\n```ruby\nlogger = -\u003e (env) { p env }\n\n# Insert the middleware (can be also a middleware object) before each existing middleware\nstack.insert_before_each logger\n```\n\n#### Replace\n\n```ruby\n# Replace the second middleware\nstack.replace(1, SomeOtherMiddleware)\n\n# Replace the Trace middleware\nstack.replace(Trace, SomeOtherMiddleware)\n```\n\n#### Delete\n\n```ruby\n# Delete the second middleware\nstack.delete(1)\n\n# Delete the Trace middleware\nstack.delete(Trace)\n```\n\n### Passing Additional Constructor Arguments\n\nWhen using middleware in a stack, you can also pass in additional constructor\narguments. Given the following middleware:\n\n```ruby\nclass Echo\n  def initialize(app, message)\n    @app = app\n    @message = message\n  end\n\n  def call(env)\n    puts @message\n    @app.call(env)\n  end\nend\n```\n\nWe can initialize `Echo` with a proper message as follows:\n\n```ruby\nMiddleware::Builder.new do\n  use Echo, \"Hello, World!\"\nend\n```\n\nThen when the stack is called, it will output \"Hello, World!\"\n\nNote that you can also pass blocks in using the `use` method.\n\n#### Lambda\n\nLambda work the same, with additional arguments:\n\n```ruby\nMiddleware::Builder.new { |b|\n  # arrow syntax for lambda construction\n  b.use -\u003e(env, msg) { puts msg }, 'some message'\n}.call(1) #will print \"some message\"\n```\n\n### Debug\n\nYou can see the content of a given stack using the `inspect` method\n\n```ruby\nMiddleware::Builder.new { |b|\n  b.use Trace\n  b.use Echo, \"Hello, World!\"\n}.inspect\n```\n\nIt will output:\n\n```ruby\nMiddleware[Trace(), Echo(\"Hello, World!\")]\n```\n\n_If you have set a name, it will be displayed instead of `Middleware`_.\n\n#### Logging\n\nA built-in logging mechanism is provided, it will output for each provider of the stack:\n\n- The provided arguments\n- The returned values (the first 255 chars) and the time (in milliseconds) elapsed in the call method\n\nTo initialize the logging you must provide a valid logger instance to `#inject_logger`.\n\nIt is also recommended to give a name to your middleware stack.\n\n```ruby\nrequire 'logger'\n\nclass UpperCaseMiddleware\n  def initialize app\n    @app = app\n  end\n\n  def call env\n    sleep(1)\n    env.upcase\n  end\nend\n\n# Build the middleware:\nMiddleware::Builder.new(name: 'MyMiddleware') { |b|\n    b.use UpperCaseMiddleware\n}.inject_logger(Logger.new(STDOUT)).call('a message')\n```\n\nIt will output something like:\n\n```\nINFO -- MyMiddleware: UpperCaseMiddleware has been called with: \"a message\"\nINFO -- MyMiddleware: UpperCaseMiddleware finished in 1001 ms and returned: \"A MESSAGE\"\n```\n\n_Note: the provided logger instance must respond to `#call(level severity, message, app name)`_","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubyonworld%2Fruby-middleware","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frubyonworld%2Fruby-middleware","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubyonworld%2Fruby-middleware/lists"}