middlegem 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +16 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +199 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/middlegem.rb +31 -0
- data/lib/middlegem/array_definition.rb +132 -0
- data/lib/middlegem/definition.rb +55 -0
- data/lib/middlegem/invalid_definition_error.rb +10 -0
- data/lib/middlegem/invalid_middleware_error.rb +9 -0
- data/lib/middlegem/invalid_middleware_output_error.rb +10 -0
- data/lib/middlegem/middleware.rb +60 -0
- data/lib/middlegem/stack.rb +151 -0
- data/lib/middlegem/unpermitted_middleware_error.rb +9 -0
- data/lib/middlegem/version.rb +5 -0
- data/middlegem.gemspec +42 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d8e1dd451fcba2d15305534945c4d6fe88c7f6c9fb29c8e8059241503291432b
|
4
|
+
data.tar.gz: 61fcd25df93bb9902e6a85815ab6b63696c6c2f6d753cceeac4afa521143ca12
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 95319c1cc7be36e04290c0a30e3494834dea8b9aef142ca1c44582a80ba9037a9325efb7a764dff32c1483e591c62ca14794d07c4c810d29dcf171c6e40eb171
|
7
|
+
data.tar.gz: 5790037f20f4c2a7b88f894c74377bd1ca75031ebc87c4c3fc601e3d4b9de21436f11a5aeb7f28b908da0462ef8962d9cfadbfbfdf491781faee122141a74ec8
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Default
|
2
|
+
|
3
|
+
/.bundle/
|
4
|
+
/.yardoc
|
5
|
+
/_yardoc/
|
6
|
+
/coverage/
|
7
|
+
/doc/
|
8
|
+
/pkg/
|
9
|
+
/spec/reports/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
# rspec failure tracking
|
13
|
+
.rspec_status
|
14
|
+
|
15
|
+
# Ignore Gemfile.lock, since this is a gem.
|
16
|
+
|
17
|
+
Gemfile.lock
|
18
|
+
|
19
|
+
# Ignore byebug history
|
20
|
+
|
21
|
+
.byebug_history
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID="c5db72d9e061605b89ff87aa3914c44362c4a45efdcb5cd75d26d90b10ec004c"
|
4
|
+
language: ruby
|
5
|
+
rvm:
|
6
|
+
- 2.5
|
7
|
+
before_script:
|
8
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
9
|
+
- chmod +x ./cc-test-reporter
|
10
|
+
- ./cc-test-reporter before-build
|
11
|
+
script:
|
12
|
+
- bundle exec rubocop -DESP
|
13
|
+
- bundle exec rake spec
|
14
|
+
after_script:
|
15
|
+
- ./cc-test-reporter format-coverage -t simplecov
|
16
|
+
- ./cc-test-reporter upload-coverage
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--private --protected
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## [0.1.0] - 2021-07-08
|
10
|
+
### Added
|
11
|
+
- The initial `middlegem` gem.
|
12
|
+
|
13
|
+
[Unreleased]: https://github.com/jacoblockard99/middlegem/compare/v0.1.0...HEAD
|
14
|
+
[0.1.0]: https://github.com/jacoblockard99/middlegem/releases/tag/v0.1.0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Jacob
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
# Middlegem
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.com/jacoblockard99/middlegem.svg?branch=master)](https://travis-ci.com/jacoblockard99/middlegem)
|
4
|
+
[![Inline docs](http://inch-ci.org/github/jacoblockard99/middlegem.svg?branch=master)](http://inch-ci.org/github/jacoblockard99/middlegem)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/b43ed85211cb562678bb/maintainability)](https://codeclimate.com/github/jacoblockard99/middlegem/maintainability)
|
6
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/b43ed85211cb562678bb/test_coverage)](https://codeclimate.com/github/jacoblockard99/middlegem/test_coverage)
|
7
|
+
|
8
|
+
`middlegem` is a Ruby gem that provides one-way middleware chain functionality. It aims to be simple and reliable. It might be a good fit for you if:
|
9
|
+
- **You want simplicity and reliability.**
|
10
|
+
- **You don't need two-way middleware.** `middlegem` does not allow processing both the "request" and the "response", for example. For that kind of functionality, I would recommend checking out [ruby-middleware](https://github.com/Ibsciss/ruby-middleware).
|
11
|
+
- **You want to explicitly define the order of your middlwares.** `middlegem` encourages you to explicitly define your middlewares and the order they should run.
|
12
|
+
|
13
|
+
## Links
|
14
|
+
|
15
|
+
- [API Docs](https://rdoc.info/github/jacoblockard99/middlegem)
|
16
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
17
|
+
- [Releases](https://github.com/jacoblockard99/middlegem/releases)
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
`middlegem` is a Ruby gem. If you use Bundler, you may install it by adding it to your `Gemfile`, like so:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'middlegem'
|
25
|
+
```
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
|
29
|
+
$ bundle install
|
30
|
+
|
31
|
+
Or you may install it manually with:
|
32
|
+
|
33
|
+
$ gem install middlegem
|
34
|
+
|
35
|
+
`middlegem` has zero dependencies and requires very little setup to get started!
|
36
|
+
|
37
|
+
## Key Concepts
|
38
|
+
|
39
|
+
`middlegem` is broken into three key parts: middlewares, middleware definitions, and middleware stacks.
|
40
|
+
|
41
|
+
**Middlewares** are the heart and soul of the gem. In essence, a middleware is a single transforming function that accepts input and produces output. In `middlegem`, any object that responds to the `call` method can be a middleware. By convention, however, middleware classes derive from `Middlegem::Middleware`.
|
42
|
+
|
43
|
+
Middlewares in `middlegem` are designed to operate on *argument lists*. This has two consequences:
|
44
|
+
1. A middleware's `call` method should simply accept the arguments it expects to transform—no need to accept and "arguments array" and try to parse it!
|
45
|
+
2. The `call` method **must** return an array. Because it is transforming an argument list, it must also return an argument list, i.e. an array.
|
46
|
+
|
47
|
+
While you can certainly use `middlegem` with a single input, always remember to return an array in your middleware `call` methods.
|
48
|
+
|
49
|
+
**Midleware definitions** are a key difference in `middlegem` from other middleware gems. They strive to solve a common problem with middlewares. Imagine, for example, that you have two middlewares: one that converts an input string to an integer, and another that multiplies that number by 10. Obviously, the conversion middleware must run first, or an error will occur. With a simple example like this, it is trivial to simply insert the middlewares in the right place at the right time. But as you begin adding more middlewares and—worse—begin allowing *custom* middlewares to be defined, things quickly become unmanageable! It becomes impossible to know exactly where a given middleware should be inserted in a middleware stack.
|
50
|
+
|
51
|
+
This is where middleware definitions come in. A middleware definition is essentially an object that determines 1) what middlewares are permitted in a middleware stack, and 2) in what order those middlewares should be run. Any object that implements a `defined?` method and a `sort` method can be avalid middleware definition, though by convention middleware definitions derive from `Middlegem::Definition`. The only middleware definition that currently ships with `middlegem` is `Middlegem::ArrayDefinition`, which allows you to define an ordered list of permitted middleware classes.
|
52
|
+
|
53
|
+
Finally, **middleware stacks**, represented by `Middlegem::Stack`, are chains of middlewares. Every `Middlegem::Stack` has a single middleware definition that determines how to run its middlewares. Note that `Middlegem::Stack` has no fancy methods for inserting middlewares at specific locations—it relies on Ruby's built-in methods. Instead, it allows ordering to be determined by the middleware definition.
|
54
|
+
|
55
|
+
## Usage
|
56
|
+
|
57
|
+
### Basic Usage
|
58
|
+
|
59
|
+
The easiest way to define middlewares is to create a class with a `call` method that optionally extends `Middlegem::Middleware`. In this example and the following ones, assume that these middlewares are defined:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
class ParenthesesMiddleware << Middlegem::Middleware
|
63
|
+
def call(input)
|
64
|
+
["(#{input})"]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class BracketsMiddleware << Middlegem::Middleware
|
69
|
+
def call(input)
|
70
|
+
["[#{input}]"]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class BracesMiddleware << Middlegem::Middleware
|
75
|
+
def call(input)
|
76
|
+
["{#{input}}"]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class MultiplierMiddleware << Middlegem::Middleware
|
81
|
+
attr_accessor :multiplier
|
82
|
+
|
83
|
+
def initialize(multiplier)
|
84
|
+
@multiplier = multiplier
|
85
|
+
end
|
86
|
+
|
87
|
+
def call(num)
|
88
|
+
[num * multiplier]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
Now you'll need to _define_ your middleware. If you're using Rails, initializers are usually a good place to do this. The easiest way to create a middleware definition is using `Middlegem::ArrayDefinition`, which allows you to specify an array of middleware classes. For example:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
DEFINITION = Middlegem::ArrayDefinition.new([
|
97
|
+
MultiplierMiddleware,
|
98
|
+
ParenthesesMiddleware,
|
99
|
+
BracketsMiddleware,
|
100
|
+
BracesMiddleware
|
101
|
+
])
|
102
|
+
```
|
103
|
+
|
104
|
+
Notice that the `MultiplierMiddleware` is at the top, because it must be given a number, and the others are arranged in "mathematical" order. Now, we can create a middleware stack with our definition.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
stack = Middlegem::Stack.new(DEFINITION)
|
108
|
+
```
|
109
|
+
|
110
|
+
And add some middlewares, however you like:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
stack.middlewares = [BracketsMiddleware]
|
114
|
+
stack.middlewares += [MultiplierMiddleware, BracesMiddleware]
|
115
|
+
stack.middlewares << ParenthesesMiddleware
|
116
|
+
```
|
117
|
+
|
118
|
+
Finally, we can call the stack with a number:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
stack.call(10) # => ["{[(100)]}"]
|
122
|
+
```
|
123
|
+
|
124
|
+
Notice how the number is first multiplied, then given parentheses, then given brackets, then given braces, exactly as specified in the middleware definition.
|
125
|
+
|
126
|
+
### Tie Resolvers
|
127
|
+
|
128
|
+
You may have noticed a problem here. What if multiple middleware instances of the same type are added to a stack. How will it know which to call? Take this code, for example, where procs are used as middleware:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
DEFINITION = Middlegem::ArrayDefinition.new([Proc])
|
132
|
+
|
133
|
+
to_int = proc { |s| Integer(s) }
|
134
|
+
square = proc { |n| n*n }
|
135
|
+
|
136
|
+
stack = Middlegem::Stack.new(DEFINITION, middlewares: [
|
137
|
+
square,
|
138
|
+
to_int
|
139
|
+
])
|
140
|
+
```
|
141
|
+
|
142
|
+
If `stack.call('5')` were run right now, the program would try to square `'5'`, *then* convert it to an integer. Moreover, there is no way to specify which should come first—they are both procs, after all. For this reason, it is recommended that you keep all your middlewares in separate classes, so they can be defined easily. There are two potential solutions, however.
|
143
|
+
|
144
|
+
First, `ArrayDefinition.new` accepts an optional "tie resolver" that will be called in such cases. For example, let's say we have this middleware:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
class AppendMiddleware
|
148
|
+
attr_accessor :appended
|
149
|
+
|
150
|
+
def initialize(appended)
|
151
|
+
@appended = appended
|
152
|
+
end
|
153
|
+
|
154
|
+
def call(input)
|
155
|
+
[input + appended]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
Obviously, the order of even individual `AppendMiddleware`s matters. "TAB" is a very differnt word from "BAT"! Imagining that we want the letters to be alphabetized, here is one potential solution:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
DEFINITION = Middlegem::ArrayDefinition.new([AppendMiddleware], resolver: ->(ties) {
|
164
|
+
if ties.count > 1 && ties.first.is_a? AppendMiddleware
|
165
|
+
return ties.sort_by(&:appended)
|
166
|
+
end
|
167
|
+
ties
|
168
|
+
})
|
169
|
+
|
170
|
+
stack = Middlegem::Stack.new(DEFINITION, middlewares: [
|
171
|
+
AppendMiddleware.new('B'),
|
172
|
+
AppendMiddleware.new('A'),
|
173
|
+
AppendMiddleware.new('C'),
|
174
|
+
AppendMiddleware.new('E'),
|
175
|
+
AppendMiddleware.new('D')
|
176
|
+
end
|
177
|
+
|
178
|
+
stack.call('') # => ['ABCDE']
|
179
|
+
```
|
180
|
+
|
181
|
+
As you can see, the resolver passed to `ArrayDefinition.new` will be called with an array of middleware whenever multiple middleware with the same class are encountered. The resolver is then expected to sort and return the array appropriately. In this case, we simply check whether the tied middlewares are `AppendMiddleware`s and sort them by their `appended` attribute if so.
|
182
|
+
|
183
|
+
While this works, the limitations quickly become obvious. Mainly, it requires a bunch of branching `else/if` or `case/when` structures in the resolver since that one resolver is called for all ties. While it may work for very simple use cases (such as preventing multiple instances of the same middleware at all), it is not feasible for anything more complicated.
|
184
|
+
|
185
|
+
For more complicated scenarios, it is instead recommended that you create your own implementation of `Middlegem::Definition` that allows ordering the middlewares in some other way. Perhaps you could set "priorities" on the middlewares, or organize them into "groups"—the possibilities with this method are limitless!
|
186
|
+
|
187
|
+
## Development
|
188
|
+
|
189
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
190
|
+
|
191
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
192
|
+
|
193
|
+
## Contributing
|
194
|
+
|
195
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jacoblockard99/middlegem.
|
196
|
+
|
197
|
+
## License
|
198
|
+
|
199
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'middlegem'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require 'pry'
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/middlegem.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'middlegem/version'
|
4
|
+
require_relative 'middlegem/middleware'
|
5
|
+
require_relative 'middlegem/definition'
|
6
|
+
require_relative 'middlegem/stack'
|
7
|
+
require_relative 'middlegem/array_definition'
|
8
|
+
|
9
|
+
# {Middlegem} is a namespace that contains all modules in the +middlegem+ gem.
|
10
|
+
#
|
11
|
+
# @author Jacob Lockard
|
12
|
+
# @since 0.1.0
|
13
|
+
module Middlegem
|
14
|
+
# {Error} is a subclass of {https://ruby-doc.org/core-2.5.0/StandardError.html StandardError}
|
15
|
+
# from which all custom errors in +middlegem+ are derived. One potential use for this class is
|
16
|
+
# to rescue all custom errors produced by +middlegem+. For example:
|
17
|
+
#
|
18
|
+
# begin
|
19
|
+
# # Do something risky with middlegem here...
|
20
|
+
# rescue Middlegem::Error
|
21
|
+
# # Catch any middlegem-specific error here...
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# @see https://ruby-doc.org/core-2.0.0/Exception.html
|
25
|
+
class Error < StandardError; end
|
26
|
+
end
|
27
|
+
|
28
|
+
require_relative 'middlegem/invalid_middleware_error'
|
29
|
+
require_relative 'middlegem/unpermitted_middleware_error'
|
30
|
+
require_relative 'middlegem/invalid_definition_error'
|
31
|
+
require_relative 'middlegem/invalid_middleware_output_error'
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Middlegem
|
4
|
+
# {ArrayDefinition} is an implementation of {Definition} that allows middlewares to be
|
5
|
+
# explicitly defined and ordered by class in an array. A basic example of usage is:
|
6
|
+
#
|
7
|
+
# definition = Middlegem::ArrayDefinition.new([
|
8
|
+
# MiddlewareOne, # appends '1'
|
9
|
+
# MiddlewareTwo, # appends '2'
|
10
|
+
# MiddlewareThree, # appends '3'
|
11
|
+
# MiddlewareFinal # appends '.'
|
12
|
+
# ])
|
13
|
+
#
|
14
|
+
# stack = Middlegem::Stack.new(definition, middlewares: [
|
15
|
+
# MiddlewareThree.new,
|
16
|
+
# MiddlewareFinal.new,
|
17
|
+
# MiddlewareOne.new,
|
18
|
+
# MiddlewareTwo.new
|
19
|
+
# ])
|
20
|
+
#
|
21
|
+
# stack.call('hello') # => ['hello123.']
|
22
|
+
#
|
23
|
+
# Notice that the middlewares are called in the order they are specified in the definition
|
24
|
+
# array.
|
25
|
+
#
|
26
|
+
# If two or more middlewares are encountered that have the same class, they will be left in the
|
27
|
+
# order they were added. This behavior can be overriden by setting a tie resolver. The
|
28
|
+
# following code, for example, raises an error when multiple +MiddlewareFinal+ middlewares
|
29
|
+
# are encountered:
|
30
|
+
#
|
31
|
+
# middlewares = [
|
32
|
+
# MiddlewareOne,
|
33
|
+
# MiddlewareTwo,
|
34
|
+
# MiddlewareThree,
|
35
|
+
# MiddlewareFinal
|
36
|
+
# ]
|
37
|
+
#
|
38
|
+
# tie_resolver = proc do |ties|
|
39
|
+
# raise "Can't run multiple MiddlewareFinals!" if ties.first.is_a? MiddlewareFinal
|
40
|
+
# ties
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# definition = Middlegem::ArrayDefinition.new(middlewares, resolver: tie_resolver)
|
44
|
+
#
|
45
|
+
# stack = Middlegem::Stack.new(definition, middlewares: [
|
46
|
+
# MiddlewareTwo.new,
|
47
|
+
# MiddlewareOne.new,
|
48
|
+
# MiddlewareFinal.new,
|
49
|
+
# MiddlewareThree.new,
|
50
|
+
# MiddlewareFinal.new
|
51
|
+
# ])
|
52
|
+
#
|
53
|
+
# stack.call('hello') # => RuntimeError (Can't run multiple MiddlewareFinals!)
|
54
|
+
#
|
55
|
+
# When the two +MiddlewareFinal+ instances are encountered, the tie resolver is run, which
|
56
|
+
# raises the error.
|
57
|
+
#
|
58
|
+
# Of course, this is only scratching the surface of what is possible with
|
59
|
+
# a custom tie resolver. You might, for example, simply skip other instances of
|
60
|
+
# +MiddlewareFinal+, rather than raising an error. A word of caution is in order, however!
|
61
|
+
# It is not recommended to try anything too complicated with the tie resolver because it is run
|
62
|
+
# for <em>all ties whatsoever</em>. That means that, while you could technically try to sort
|
63
|
+
# middlewares with the same class based on some other factor---there is even an example
|
64
|
+
# in +spec/middlegem/array_definition_spec.rb+---it potentially results in long +if/else+ or
|
65
|
+
# +case/when+ constructions because each type must be dealt with separately. Use at your own risk!
|
66
|
+
#
|
67
|
+
# In general, if you need to use a tie resolver for anything but the most basic of tasks, you
|
68
|
+
# should probably just create your own {Definition} implementation with the required
|
69
|
+
# functionality. {ArrayDefinition} is intended primarily for defining middlewares according to
|
70
|
+
# their classes and nothing more.
|
71
|
+
#
|
72
|
+
# @author Jacob Lockard
|
73
|
+
# @since 0.1.0
|
74
|
+
# @see Definition
|
75
|
+
class ArrayDefinition < Definition
|
76
|
+
# An array of the middleware classes defined by this {ArrayDefinition}. Middlewares will only
|
77
|
+
# be permitted if their class is in this array will be run in the order specified here.
|
78
|
+
# @return [Array<Class>] the array of defined classes.
|
79
|
+
attr_accessor :defined_classes
|
80
|
+
|
81
|
+
# The callable object to use to break ties when sorting middlewares. When multiple
|
82
|
+
# middlewares of the same type are encountered, this object will be called with an
|
83
|
+
# array of all tied middlewares. The resolver should sort and return the array as
|
84
|
+
# appropriate.
|
85
|
+
# @return [#call] the middleware tie resolver.
|
86
|
+
attr_reader :resolver
|
87
|
+
|
88
|
+
# Creates a new instance of {ArrayDefinition} with the given array of defined classes and,
|
89
|
+
# optionally, a custom tie resolver.
|
90
|
+
# @param defined_classes [Object<Class>] an ordered array of classes to be defined by this
|
91
|
+
# {ArrayDefinition} (see {#defined_classes}).
|
92
|
+
# @param resolver [#call, nil] a callable object to use when middlewares of the same class
|
93
|
+
# are encountered (see {#resolver}). If a +nil+ resolver is passed (the default), the
|
94
|
+
# default resolver will be used, which keeps tied middlewares in the order they are passed
|
95
|
+
# to {#sort}.
|
96
|
+
def initialize(defined_classes, resolver: nil)
|
97
|
+
resolver = ->(*ties) { ties } if resolver.nil?
|
98
|
+
|
99
|
+
@defined_classes = defined_classes
|
100
|
+
@resolver = resolver
|
101
|
+
|
102
|
+
super()
|
103
|
+
end
|
104
|
+
|
105
|
+
# Determines whether the given middleware is defined according to this {ArrayDefinition} by
|
106
|
+
# checking whether its class is contained in the list of defined classes
|
107
|
+
# (i.e. {#defined_classes}).
|
108
|
+
# @param middleware [Object] the middleware to check.
|
109
|
+
# @return [bool] whether the middleware is defined.
|
110
|
+
def defined?(middleware)
|
111
|
+
defined_classes.include?(middleware.class)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Sorts the given array of middlewares according to this {ArrayDefinition}. Middlewares are
|
115
|
+
# sorted according to the order in which their classes are specified in {#defined_classes}.
|
116
|
+
# If multiple middlewares of the same type are encountered, they will be resolved with the
|
117
|
+
# {#resolver}.
|
118
|
+
def sort(middlewares)
|
119
|
+
defined_classes.map { |c| resolver.call(matches(middlewares, c)) }.flatten
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Gets all the middlewares in the given array whose class is the given class.
|
125
|
+
# @param middlewares [Array<Object>] the array of middlewares to search.
|
126
|
+
# @param klass [Class] the class to search for.
|
127
|
+
# @return [Array<Object>] the matched middlewares.
|
128
|
+
def matches(middlewares, klass)
|
129
|
+
middlewares.select { |m| m.instance_of?(klass) }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Middlegem
|
4
|
+
# {Definition} is an abstract class whose implementations are capable of defining what
|
5
|
+
# middlewares are permitted in a given context and in what order they should be executed. Note
|
6
|
+
# that this concept of "middleware definitions" is a major difference from other middleware
|
7
|
+
# solutions, where middlewares are simply inserted into a stack. In +middlegem+, a
|
8
|
+
# {Definition} determines what order the middleware in a {Stack} should be called. This greatly
|
9
|
+
# decreases the likelihood of "middleware conflicts", where one middleware expects another to
|
10
|
+
# have already run. The downside, of course, is the verbosity---all middleware must be defined.
|
11
|
+
#
|
12
|
+
# It should be noted, however, that the concept of a "middleware definition" is completely
|
13
|
+
# flexible. If you would prefer to simply insert middlewares into a stack without defining
|
14
|
+
# them, you can create a {Definition} implentation whose {#sort} method just returns the
|
15
|
+
# unsorted middlewares. And if you want to allow any middlewares to be inserted, simply return
|
16
|
+
# +true+ from the {#defined?} method. On the other hand, there is a lot of flexibility in how
|
17
|
+
# you determine the middleware order: define a DSL, let middleware have dependencies, define
|
18
|
+
# middleware "groups", allow middleware "priorities"---the possibilities are endless!
|
19
|
+
#
|
20
|
+
# Currently, the only default implementation is {ArrayDefinition}, which executes
|
21
|
+
# middleware based on an ordered list of middleware classes.
|
22
|
+
#
|
23
|
+
# Finally, you might notice that {Definition} contains no actual instance method
|
24
|
+
# implementations. In other words, for all intents and pruposes, it is empty! This is
|
25
|
+
# intentional. A middleware definition is *any* object that implements both a {#defined?} and a
|
26
|
+
# {#sort} method (see {.valid?}). You may extend this class, however, to explicitly mark your
|
27
|
+
# middleware definition classes as middleware definitions.
|
28
|
+
#
|
29
|
+
# @author Jacob Lockard
|
30
|
+
# @since 0.1.0
|
31
|
+
# @abstract
|
32
|
+
class Definition
|
33
|
+
# Determines whether the given object is a valid middleware definition. Currently, any object
|
34
|
+
# that implements a +defined?+ method and a +sort+ method is valid.
|
35
|
+
#
|
36
|
+
# @param definition [Object] the middleware definition to check.
|
37
|
+
# @return [bool] whether the given object is a valid middleware definition.
|
38
|
+
def self.valid?(definition)
|
39
|
+
definition.respond_to?(:defined?) && definition.respond_to?(:sort)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!method defined?(middleware)
|
43
|
+
# Should determine whether the given middleware is defined according to this
|
44
|
+
# {Definition}. Feel free to determine this however you like! This method will
|
45
|
+
# be used to validate middlewares added to a {Stack}.
|
46
|
+
# @param middleware [Object] the middleware object to check.
|
47
|
+
# @return [bool] whether the given middleware object is defined.
|
48
|
+
|
49
|
+
# @!method sort(middlewares)
|
50
|
+
# Should sort the given array of middlewares according to this {Definition}. In a {Stack},
|
51
|
+
# middlewares will be called in the order returned.
|
52
|
+
# @param middlewares [Array<Object>] the middlewares to sort.
|
53
|
+
# @return [Array<Object>] the sorted middlewares.
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Middlegem
|
4
|
+
# An error that is raised when a middleware object returns an invalid output. This error is
|
5
|
+
# most commonly raised when a middleware does not return an array.
|
6
|
+
#
|
7
|
+
# @author Jacob Lockard
|
8
|
+
# @since 0.1.0
|
9
|
+
class InvalidMiddlewareOutputError < Error; end
|
10
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Middlegem
|
4
|
+
# {Middleware} is an abstract representation of a single "middleware". A middleware is a
|
5
|
+
# transforming function that accepts arbitrary input and produces arbitrary output.
|
6
|
+
# Middlewares can be chained with {Stack} to produce powerful, flexible data-transforming
|
7
|
+
# layers.
|
8
|
+
#
|
9
|
+
# One important concept to note is that middlewares in +middlegem+ are "one-way". In other
|
10
|
+
# words, they cannot transform both a "request" and a "response". For this functionality,
|
11
|
+
# please see other gems such as {https://github.com/Ibsciss/ruby-middleware ruby-middleware} or
|
12
|
+
# (for web requests) {https://github.com/rack/rack rack}.
|
13
|
+
#
|
14
|
+
# Middlewares in +middlegem+ are also slightly different in exactly what they operate upon.
|
15
|
+
# Whereas the middlewares in +ruby-middleware+ simply transform a single +env+
|
16
|
+
# variable, +middlegem+ middlewares transform an entire argument list. This may or may not be
|
17
|
+
# desirable, as it requires all middlewares to return an array, but it highlights the primary
|
18
|
+
# use case for +middlegem+. While it can be used for a variety of purposes, +middlegem+ was
|
19
|
+
# specifically designed for filtering and changing arguments passed to a method.
|
20
|
+
#
|
21
|
+
# Finally, you might notice that {Middleware} contains no actual instance method
|
22
|
+
# implementations. In other words, for all intents and pruposes, it is empty! This is
|
23
|
+
# intentional. A middleware is *any* object that implements a {#call} method (you read that
|
24
|
+
# right---a proc is a valid middleware!). You may extend this class, however, to explicitly
|
25
|
+
# mark your middleware classes as middlewares.
|
26
|
+
#
|
27
|
+
# @author Jacob Lockard
|
28
|
+
# @since 0.1.0
|
29
|
+
# @abstract
|
30
|
+
# @see Middlegem::Stack
|
31
|
+
class Middleware
|
32
|
+
# Determines whether the given object is a valid middleware. Currently, any object that
|
33
|
+
# responds to the +call+ method is valid.
|
34
|
+
#
|
35
|
+
# @param middleware [Object] the middleware to check.
|
36
|
+
# @return [bool] whether the given object is a valid middleware.
|
37
|
+
def self.valid?(middleware)
|
38
|
+
middleware.respond_to?(:call)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!method call(*args)
|
42
|
+
# The method called to actually execute the middleware. It is passed the output of the
|
43
|
+
# previous middleware in the chain and should return the appropriately transformed output
|
44
|
+
# to be passed to the next middleware. Note that the splat operator is intentional! This
|
45
|
+
# method will be called with actual arguments, not an array. This means that you can
|
46
|
+
# simply define the arguments you expect directly in the method signature, rather than
|
47
|
+
# accepting a splatted array and accessing its elements. For example, the following is
|
48
|
+
# possible:
|
49
|
+
# class MyMiddleware
|
50
|
+
# def call(first, last, email)
|
51
|
+
# return "#{email} <#{first} #{last}>"
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
# @param args [Array<Object>] the input to be transformed, usually the output of the
|
55
|
+
# previous middleware in a middleware chain.
|
56
|
+
# @return [Object, Array<Object>] the transformed output. Note that if an array is
|
57
|
+
# returned, it will be splatted before being passed to the next middleware.
|
58
|
+
# @note This method must be implemented by all middleware!
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Middlegem
|
4
|
+
# {Stack} is a class that represents a chain of middlewares, which, when called, can
|
5
|
+
# arbitrarily transform a given input. Most of the functionality provided by +middlegem+ lies
|
6
|
+
# in this class. Using {Stack} is simple: create a new instance with the desired definition,
|
7
|
+
# add whatever middlewares you want to use, and {#call} it.
|
8
|
+
#
|
9
|
+
# A very basic example of usage is:
|
10
|
+
#
|
11
|
+
# class LastNameMiddleware < Middlegem::Middleware
|
12
|
+
# def call(name)
|
13
|
+
# "The Honorable #{name}"
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class EmailStringMiddleware < Middlegem::Middleware
|
18
|
+
# def initialize(email)
|
19
|
+
# @email = email
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def call(name)
|
23
|
+
# "#{@email} <#{name}>"
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# definitions = [
|
28
|
+
# LastNameMiddleware,
|
29
|
+
# EmailStringMiddleware
|
30
|
+
# ]
|
31
|
+
#
|
32
|
+
# stack = Middlegem::Stack.new(Middlegem::ArrayDefinition.new(definitions))
|
33
|
+
# stack.middlewares += [EmailStringMiddleware.new('mail@test.com'), LastNameMiddleware.new]
|
34
|
+
#
|
35
|
+
# stack.call('Jacob') # => "mail@test.com <The Honorable Jacob>"
|
36
|
+
#
|
37
|
+
# Notice that, even though the +EmailStringMiddleware+ was added before the
|
38
|
+
# +LastNameMiddleware+, the +LastNameMiddleware+ was still run first since it was defined
|
39
|
+
# first. That is a core principle of +middlegem+---rather than providing extensive methods to
|
40
|
+
# insert middleware in a specific place along the chain, +middlegem+ allows you to define
|
41
|
+
# the order explicitly. Also note that there are a variety of ways that you could specify the
|
42
|
+
# middleware order by extending {Definition}.
|
43
|
+
#
|
44
|
+
# @author Jacob Lockard
|
45
|
+
# @since 0.1.0
|
46
|
+
# @see Middleware
|
47
|
+
# @see Definition
|
48
|
+
# @see ArrayDefinition
|
49
|
+
class Stack
|
50
|
+
# An array containing the middlewares represented by this {Stack}. You can insert middlewares
|
51
|
+
# in any way you like by accessing this attribute directly and using ruby's built-in array
|
52
|
+
# methods. If desired, you can even assign a new array to it. All middlewares will be
|
53
|
+
# validated before being run. To be run, a middleware must be *valid* as defined by
|
54
|
+
# {Middleware.valid?} and it must be *defined* according to the {Definition} instance
|
55
|
+
# in {#definition}.
|
56
|
+
# @return [Array<Object>] the middlewares contained in this stack.
|
57
|
+
attr_accessor :middlewares
|
58
|
+
|
59
|
+
# The {Definition} used to determine what middlewares are permitted in this stack
|
60
|
+
# and in what order they should be run. Note that this attribute may be any object that is a
|
61
|
+
# valid definition according to {Definition.valid?}.
|
62
|
+
# @return [Definition] the middleware definition of this middleware stack.
|
63
|
+
attr_reader :definition
|
64
|
+
|
65
|
+
# Creates a new instance of {Stack} with the given middleware definition and,
|
66
|
+
# optionally, an array of middlewares. Note that middlewares will be validated, not
|
67
|
+
# immediately, but before being run.
|
68
|
+
# @param definition [Definition] the middleware definition to use to determine
|
69
|
+
# what middleware to permit in this stack and in what order to run them. May be any object
|
70
|
+
# that is a valid definition according to {Definition.valid?}.
|
71
|
+
# @param middlwares [Array<Object>] an optional array of initial middlewares in this stack.
|
72
|
+
def initialize(definition, middlewares: [])
|
73
|
+
unless Definition.valid?(definition)
|
74
|
+
raise InvalidDefinitionError, "The middleware definition #{definition} is invalid!"
|
75
|
+
end
|
76
|
+
|
77
|
+
@definition = definition
|
78
|
+
@middlewares = middlewares
|
79
|
+
end
|
80
|
+
|
81
|
+
# Transforms the given input by calling all the middlewares in this stack, as defined by the
|
82
|
+
# middleware {#definition}. Note that, as mentioned in {Middleware}, middlewares in
|
83
|
+
# +middlegem+ transform argument lists. Thus, the arguments are already splatted---there is
|
84
|
+
# no need to pass a single array of arguments as the only parameter, unless you actually want
|
85
|
+
# to transform just a single array. Also, middleware *must* return an array of arguments,
|
86
|
+
# which will be splatted when passed to {Middleware#call}.
|
87
|
+
#
|
88
|
+
# Midlewares are validated before being run or sorted. If a middleware is encountered that
|
89
|
+
# is either invalid or unpermitted, an appropriate error will be raised.
|
90
|
+
#
|
91
|
+
# @param args [Array<Object>] the array of input arguments.
|
92
|
+
# @return [Array<Object>] the output of the last middleware in the chain.
|
93
|
+
# @raise [InvalidMiddlewareError] when one of the middlewares in {#middlewares} is not valid,
|
94
|
+
# as defined by {Middleware.valid?}.
|
95
|
+
# @raise [UnpermittedMiddlewareError] when one of the middlewares in {#middlewares} has not
|
96
|
+
# been defined, and is thus not permitted, according to the {Definition} instance in
|
97
|
+
# {#definition}.
|
98
|
+
def call(*args)
|
99
|
+
# Validate the middlewares.
|
100
|
+
middlewares.each { |m| ensure_valid!(m) }
|
101
|
+
|
102
|
+
# Sort the middlewares.
|
103
|
+
sorted = definition.sort(middlewares)
|
104
|
+
|
105
|
+
# Run each middleware with the output of the previous one, ensuring that each output is
|
106
|
+
# valid. For the first middleware, use `args` as the input.
|
107
|
+
last_output = args
|
108
|
+
sorted.each do |middleware|
|
109
|
+
last_output = middleware.call(*last_output)
|
110
|
+
ensure_valid_output!(middleware, last_output)
|
111
|
+
end
|
112
|
+
|
113
|
+
last_output
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
# Ensures that the given middleware is a valid middleware for this middleware stack, raising
|
119
|
+
# an appropriate error if not. A middleware is valid if:
|
120
|
+
# 1. it is valid according to {Middleware.valid?}, and
|
121
|
+
# 2. it is defined according to {#definition}.
|
122
|
+
# @param middleware [Object] the middleware to validate.
|
123
|
+
# @return [void]
|
124
|
+
def ensure_valid!(middleware)
|
125
|
+
unless Middleware.valid?(middleware)
|
126
|
+
raise InvalidMiddlewareError, "The middleware #{middleware} is not a valid middleware!"
|
127
|
+
end
|
128
|
+
|
129
|
+
unless definition.defined?(middleware)
|
130
|
+
raise UnpermittedMiddlewareError, "The middleware #{middleware} has not been defined!"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Ensures that the given output of the given middleware is valid for all the intents and
|
135
|
+
# purposes of this stack. Essentially, the output is valid if it can be passed, splatted, to
|
136
|
+
# the next middleware, or returned at the end of the stack. Currently, this method only
|
137
|
+
# checks whether the output is an "array" (as defined by the splat operator). If the output
|
138
|
+
# is invalid, an appropriate error will be raised.
|
139
|
+
# @param middleware [Object] the middleware whose output is being validated. This object is
|
140
|
+
# only used to generate appropriate error messages.
|
141
|
+
# @param output [Object] the middleware output to validate.
|
142
|
+
# @return [void]
|
143
|
+
def ensure_valid_output!(middleware, output)
|
144
|
+
unless output == [*output]
|
145
|
+
raise InvalidMiddlewareOutputError, <<~ERR
|
146
|
+
The middleware #{middleware} outputted #{output}, which is not an array!
|
147
|
+
ERR
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
data/middlegem.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/middlegem/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'middlegem'
|
7
|
+
spec.version = Middlegem::VERSION
|
8
|
+
spec.authors = ['Jacob']
|
9
|
+
spec.email = ['jacoblockard99@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Simple one-way middleware.'
|
12
|
+
spec.description = <<~DESC
|
13
|
+
Middlegem is a ruby gem that provides simple middleware chains with goals of simplicity and
|
14
|
+
reliability.
|
15
|
+
DESC
|
16
|
+
spec.homepage = 'https://github.com/jacoblockard99/middlegem'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
19
|
+
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
21
|
+
spec.metadata['source_code_uri'] = 'https://github.com/jacoblockard99/middlegem'
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/jacoblockard99/middlegem/CHANGELOG.md'
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
27
|
+
end
|
28
|
+
spec.bindir = 'exe'
|
29
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ['lib']
|
31
|
+
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
33
|
+
spec.add_development_dependency 'rubocop', '~> 1.18'
|
34
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.4'
|
35
|
+
spec.add_development_dependency 'simplecov', '0.17'
|
36
|
+
|
37
|
+
# Uncomment to register a new dependency of your gem
|
38
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
39
|
+
|
40
|
+
# For more information and examples about making a new gem, checkout our
|
41
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: middlegem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-07-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.18'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.18'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop-rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.17'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.17'
|
69
|
+
description: |
|
70
|
+
Middlegem is a ruby gem that provides simple middleware chains with goals of simplicity and
|
71
|
+
reliability.
|
72
|
+
email:
|
73
|
+
- jacoblockard99@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".rspec"
|
80
|
+
- ".rubocop.yml"
|
81
|
+
- ".travis.yml"
|
82
|
+
- ".yardopts"
|
83
|
+
- CHANGELOG.md
|
84
|
+
- Gemfile
|
85
|
+
- LICENSE.txt
|
86
|
+
- README.md
|
87
|
+
- Rakefile
|
88
|
+
- bin/console
|
89
|
+
- bin/setup
|
90
|
+
- lib/middlegem.rb
|
91
|
+
- lib/middlegem/array_definition.rb
|
92
|
+
- lib/middlegem/definition.rb
|
93
|
+
- lib/middlegem/invalid_definition_error.rb
|
94
|
+
- lib/middlegem/invalid_middleware_error.rb
|
95
|
+
- lib/middlegem/invalid_middleware_output_error.rb
|
96
|
+
- lib/middlegem/middleware.rb
|
97
|
+
- lib/middlegem/stack.rb
|
98
|
+
- lib/middlegem/unpermitted_middleware_error.rb
|
99
|
+
- lib/middlegem/version.rb
|
100
|
+
- middlegem.gemspec
|
101
|
+
homepage: https://github.com/jacoblockard99/middlegem
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata:
|
105
|
+
homepage_uri: https://github.com/jacoblockard99/middlegem
|
106
|
+
source_code_uri: https://github.com/jacoblockard99/middlegem
|
107
|
+
changelog_uri: https://github.com/jacoblockard99/middlegem/CHANGELOG.md
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 2.5.0
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubygems_version: 3.2.3
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Simple one-way middleware.
|
127
|
+
test_files: []
|