web_pipe 0.13.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +29 -0
- data/Gemfile +2 -2
- data/README.md +18 -11
- data/docs/building_a_rack_application.md +1 -1
- data/docs/composing_applications.md +4 -4
- data/docs/connection_struct/configuring_the_connection_struct.md +4 -4
- data/docs/connection_struct/halting_the_pipe.md +17 -19
- data/docs/connection_struct/sharing_data_downstream.md +9 -8
- data/docs/connection_struct.md +22 -19
- data/docs/design_model.md +10 -9
- data/docs/dsl_free_usage.md +85 -14
- data/docs/extensions/container.md +9 -10
- data/docs/extensions/cookies.md +4 -2
- data/docs/extensions/dry_schema.md +5 -4
- data/docs/extensions/flash.md +9 -11
- data/docs/extensions/{dry_view.md → hanami_view.md} +20 -22
- data/docs/extensions/not_found.md +40 -0
- data/docs/extensions/params.md +6 -4
- data/docs/extensions/rails.md +31 -38
- data/docs/extensions/redirect.md +5 -4
- data/docs/extensions/router_params.md +5 -5
- data/docs/extensions/session.md +4 -4
- data/docs/extensions/url.md +6 -6
- data/docs/extensions.md +5 -6
- data/docs/introduction.md +7 -7
- data/docs/plugging_operations/composing_operations.md +3 -3
- data/docs/plugging_operations/injecting_operations.md +4 -4
- data/docs/plugging_operations/inspecting_operations.md +24 -0
- data/docs/plugging_operations/resolving_operations.md +3 -3
- data/docs/plugging_operations.md +3 -3
- data/docs/plugs/config.md +1 -1
- data/docs/plugs/content_type.md +2 -1
- data/docs/plugs.md +6 -7
- data/docs/recipes/hanami_2_and_dry_rb_integration.md +12 -0
- data/docs/recipes/hanami_router_integration.md +3 -1
- data/docs/recipes/using_all_restful_methods.md +6 -5
- data/docs/testing.md +64 -0
- data/docs/using_rack_middlewares/composing_middlewares.md +2 -3
- data/docs/using_rack_middlewares/injecting_middlewares.md +6 -6
- data/docs/using_rack_middlewares/inspecting_middlewares.md +35 -0
- data/docs/using_rack_middlewares.md +6 -6
- data/lib/web_pipe/app.rb +22 -25
- data/lib/web_pipe/conn.rb +0 -1
- data/lib/web_pipe/conn_support/builder.rb +0 -7
- data/lib/web_pipe/conn_support/composition.rb +3 -26
- data/lib/web_pipe/conn_support/errors.rb +5 -5
- data/lib/web_pipe/conn_support/headers.rb +1 -50
- data/lib/web_pipe/conn_support/types.rb +3 -3
- data/lib/web_pipe/dsl/builder.rb +10 -19
- data/lib/web_pipe/dsl/class_context.rb +15 -40
- data/lib/web_pipe/dsl/instance_context.rb +53 -0
- data/lib/web_pipe/extensions/container/container.rb +2 -15
- data/lib/web_pipe/extensions/cookies/cookies.rb +2 -31
- data/lib/web_pipe/extensions/dry_schema/dry_schema.rb +2 -56
- data/lib/web_pipe/extensions/flash/flash.rb +2 -32
- data/lib/web_pipe/extensions/hanami_view/hanami_view.rb +67 -0
- data/lib/web_pipe/extensions/not_found/not_found.rb +26 -0
- data/lib/web_pipe/extensions/params/params.rb +2 -63
- data/lib/web_pipe/extensions/rails/rails.rb +2 -123
- data/lib/web_pipe/extensions/redirect/redirect.rb +2 -20
- data/lib/web_pipe/extensions/router_params/router_params.rb +1 -39
- data/lib/web_pipe/extensions/session/session.rb +2 -25
- data/lib/web_pipe/extensions/url/url.rb +2 -5
- data/lib/web_pipe/pipe.rb +229 -0
- data/lib/web_pipe/plug.rb +31 -65
- data/lib/web_pipe/plugs/config.rb +0 -2
- data/lib/web_pipe/plugs/content_type.rb +0 -2
- data/lib/web_pipe/rack_support/app_with_middlewares.rb +3 -26
- data/lib/web_pipe/rack_support/middleware.rb +2 -2
- data/lib/web_pipe/rack_support/middleware_specification.rb +19 -48
- data/lib/web_pipe/test_support.rb +28 -0
- data/lib/web_pipe/types.rb +1 -3
- data/lib/web_pipe/version.rb +1 -1
- data/lib/web_pipe.rb +77 -17
- data/web_pipe.gemspec +1 -2
- metadata +16 -9
- data/docs/recipes/dry_rb_integration.md +0 -18
- data/lib/web_pipe/dsl/dsl_context.rb +0 -85
- data/lib/web_pipe/dsl/instance_methods.rb +0 -114
- data/lib/web_pipe/extensions/dry_view/dry_view.rb +0 -158
@@ -0,0 +1,229 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'web_pipe/conn_support/composition'
|
4
|
+
require 'web_pipe/app'
|
5
|
+
require 'web_pipe/plug'
|
6
|
+
require 'web_pipe/rack_support/middleware_specification'
|
7
|
+
require 'web_pipe/rack_support/app_with_middlewares'
|
8
|
+
require 'web_pipe/types'
|
9
|
+
|
10
|
+
module WebPipe
|
11
|
+
# Composable rack application builder.
|
12
|
+
#
|
13
|
+
# An instance of this class helps build rack applications that can compose.
|
14
|
+
# Besides the DSL, which only adds a convenience layer, this is the higher
|
15
|
+
# abstraction on the library.
|
16
|
+
#
|
17
|
+
# Applications are built by plugging functions that take and return a
|
18
|
+
# {WebPipe::Conn} instance. That's an immutable struct that contains all the
|
19
|
+
# request information alongside methods to build the response. See {#plug} for
|
20
|
+
# details.
|
21
|
+
#
|
22
|
+
# Middlewares can also be added to the resulting application thanks to {#use}.
|
23
|
+
#
|
24
|
+
# Be aware that instances of this class are immutable, so methods return new
|
25
|
+
# objects every time.
|
26
|
+
#
|
27
|
+
# The instance itself is the final rack application.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# # config.ru
|
31
|
+
# require 'web_pipe/pipe'
|
32
|
+
#
|
33
|
+
# app = WebPipe::Pipe.new
|
34
|
+
# .use(:runtime, Rack::Runtime)
|
35
|
+
# .plug(:content_type) do |conn|
|
36
|
+
# conn.add_response_header('Content-Type', 'text/plain')
|
37
|
+
# end
|
38
|
+
# .plug(:render) do |conn|
|
39
|
+
# conn.set_response_body('Hello, World!')
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# run app
|
43
|
+
class Pipe
|
44
|
+
# Container that resolves nothing
|
45
|
+
EMPTY_CONTAINER = {}.freeze
|
46
|
+
|
47
|
+
# @!attribute [r] container
|
48
|
+
# Container from where resolve operations. See {#plug}.
|
49
|
+
attr_reader :container
|
50
|
+
|
51
|
+
# @!attribute [r] context
|
52
|
+
# Object from where resolve operations. See {#plug}.
|
53
|
+
attr_reader :context
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
EMPTY_PLUGS = [].freeze
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
EMPTY_MIDDLEWARE_SPECIFICATIONS = [].freeze
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
Container = Types.Interface(:[])
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
attr_reader :plugs
|
66
|
+
|
67
|
+
# @api private
|
68
|
+
attr_reader :middleware_specifications
|
69
|
+
|
70
|
+
# @param container [#to_h] Container from where resolve plug's operations
|
71
|
+
# (see {#plug}).
|
72
|
+
# @param context [Any] Object from where resolve plug's operations (see
|
73
|
+
# {#plug})
|
74
|
+
def initialize(
|
75
|
+
container: EMPTY_CONTAINER,
|
76
|
+
context: nil,
|
77
|
+
plugs: EMPTY_PLUGS,
|
78
|
+
middleware_specifications: EMPTY_MIDDLEWARE_SPECIFICATIONS
|
79
|
+
)
|
80
|
+
@plugs = plugs
|
81
|
+
@middleware_specifications = middleware_specifications
|
82
|
+
@container = Container[container]
|
83
|
+
@context = context
|
84
|
+
end
|
85
|
+
|
86
|
+
# Names and adds a plug operation to the application.
|
87
|
+
#
|
88
|
+
# The operation can be provided in several ways:
|
89
|
+
#
|
90
|
+
# - Through the `spec` parameter as:
|
91
|
+
# - Anything responding to `#call` (like a {Proc}).
|
92
|
+
# - As a string or symbol key for something registered in {#container}.
|
93
|
+
# - Anything responding to `#to_proc` (like another {WebPipe::Pipe}
|
94
|
+
# instance or an instance of a class including {WebPipe}).
|
95
|
+
# - As `nil` (default), meaning that the operation is a method in
|
96
|
+
# {#context} matching the `name` parameter.
|
97
|
+
# - Through a block, if the `spec` parameter is `nil`.
|
98
|
+
#
|
99
|
+
# @param name [Symbol]
|
100
|
+
# @param spec [#call, #to_proc, String, Symbol, nil]
|
101
|
+
# @yieldparam [WebPipe::Conn]
|
102
|
+
#
|
103
|
+
# @return [WebPipe::Pipe] A fresh new instance with the added plug.
|
104
|
+
def plug(name, spec = nil, &block_spec)
|
105
|
+
with(
|
106
|
+
plugs: [
|
107
|
+
*plugs,
|
108
|
+
Plug.new(name: name, spec: spec || block_spec)
|
109
|
+
]
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Names and adds a rack middleware to the final application.
|
114
|
+
#
|
115
|
+
# The middleware can be given in three forms:
|
116
|
+
#
|
117
|
+
# - As one or two arguments, the first one being a
|
118
|
+
# rack middleware class, and optionally a second one with its initialization
|
119
|
+
# options.
|
120
|
+
# - As something responding to `#to_middlewares` with an array of
|
121
|
+
# {WebPipe::RackSupport::Middleware} (like another {WebPipe::Pipe} instance
|
122
|
+
# or a class including {WebPipe}), case in which all middlewares are used.
|
123
|
+
#
|
124
|
+
# @overload use(name, middleware_class)
|
125
|
+
# @param name [Symbol]
|
126
|
+
# @param middleware_class [Class]
|
127
|
+
# @overload use(name, middleware_class, middleware_options)
|
128
|
+
# @param name [Symbol]
|
129
|
+
# @param middleware_class [Class]
|
130
|
+
# @param middleware_options [Any]
|
131
|
+
# @overload use(name, to_middlewares)
|
132
|
+
# @param name [Symbol]
|
133
|
+
# @param middleware_class [#to_middlewares]
|
134
|
+
#
|
135
|
+
# @return [WebPipe::Pipe] A fresh new instance with the added middleware.
|
136
|
+
def use(name, *spec)
|
137
|
+
with(
|
138
|
+
middleware_specifications: [
|
139
|
+
*middleware_specifications,
|
140
|
+
RackSupport::MiddlewareSpecification.new(name: name, spec: spec)
|
141
|
+
]
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Shortcut for {#plug} and {#use} a pipe at once.
|
146
|
+
#
|
147
|
+
# @param name [#to_sym]
|
148
|
+
# @param spec [#to_proc#to_middlewares]
|
149
|
+
def compose(name, spec)
|
150
|
+
use(name, spec)
|
151
|
+
.plug(name, spec)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Operations {#plug}ged to the app, mapped by their names.
|
155
|
+
#
|
156
|
+
# @return [Hash{Symbol => Proc}]
|
157
|
+
def operations
|
158
|
+
@operations ||= Hash[
|
159
|
+
plugs.map { |plug| [plug.name, plug.call(container, context)] }
|
160
|
+
]
|
161
|
+
end
|
162
|
+
|
163
|
+
# Middlewares {#use}d in the app, mapped by their names.
|
164
|
+
#
|
165
|
+
# Returns them wrapped within {WebPipe::RackSupport::Middleware} instances,
|
166
|
+
# from where you can access their classes and options.
|
167
|
+
#
|
168
|
+
# @return [Hash{Symbol=>Array<WebPipe::RackSupport::Middleware>}]
|
169
|
+
def middlewares
|
170
|
+
@middlewares ||= Hash[
|
171
|
+
middleware_specifications.map { |mw_spec| [mw_spec.name, mw_spec.call] }
|
172
|
+
]
|
173
|
+
end
|
174
|
+
|
175
|
+
# @api private
|
176
|
+
def to_proc
|
177
|
+
ConnSupport::Composition
|
178
|
+
.new(operations.values)
|
179
|
+
.method(:call)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @api private
|
183
|
+
def to_middlewares
|
184
|
+
middlewares.values.flatten
|
185
|
+
end
|
186
|
+
|
187
|
+
# @api private
|
188
|
+
def inject(plugs: {}, middleware_specifications: {})
|
189
|
+
res_mw_specs = RackSupport::MiddlewareSpecification.inject(
|
190
|
+
self.middleware_specifications, middleware_specifications
|
191
|
+
)
|
192
|
+
res_plugs = Plug.inject(
|
193
|
+
self.plugs, plugs
|
194
|
+
)
|
195
|
+
with(
|
196
|
+
plugs: res_plugs,
|
197
|
+
middleware_specifications: res_mw_specs
|
198
|
+
)
|
199
|
+
end
|
200
|
+
|
201
|
+
# @api private
|
202
|
+
def call(env)
|
203
|
+
rack_app.call(env)
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
def app
|
209
|
+
App.new(operations.values).freeze
|
210
|
+
end
|
211
|
+
|
212
|
+
def rack_app
|
213
|
+
RackSupport::AppWithMiddlewares.new(
|
214
|
+
to_middlewares,
|
215
|
+
app
|
216
|
+
).freeze
|
217
|
+
end
|
218
|
+
|
219
|
+
def with(plugs: nil, middleware_specifications: nil)
|
220
|
+
self.class.new(
|
221
|
+
container: container,
|
222
|
+
context: context,
|
223
|
+
middleware_specifications: middleware_specifications ||
|
224
|
+
self.middleware_specifications,
|
225
|
+
plugs: plugs || self.plugs
|
226
|
+
)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
data/lib/web_pipe/plug.rb
CHANGED
@@ -5,106 +5,72 @@ require 'web_pipe/types'
|
|
5
5
|
require 'web_pipe/conn_support/composition'
|
6
6
|
|
7
7
|
module WebPipe
|
8
|
-
# A plug is a specification to resolve a callable object.
|
9
|
-
#
|
10
|
-
# It is initialized with a {Name} and a {Spec} and, on resolution
|
11
|
-
# time, is called with a {Types::Container} and an {Object} to act
|
12
|
-
# in the following fashion:
|
13
|
-
#
|
14
|
-
# - When the spec responds to `#call`, it is returned itself as the
|
15
|
-
# callable object.
|
16
|
-
# - When the spec is `nil`, then a {Proc} wrapping a method with the
|
17
|
-
# plug name in `object` is returned.
|
18
|
-
# - Otherwise, spec is taken as the key to resolve the operation
|
19
|
-
# from the `container`.
|
20
|
-
#
|
21
8
|
# @api private
|
22
9
|
class Plug < Dry::Struct
|
23
|
-
#
|
10
|
+
# Raised when the specification for an operation is invalid.
|
24
11
|
class InvalidPlugError < ArgumentError
|
25
|
-
# @param name [Any] Name for the plug that can't be resolved
|
26
12
|
def initialize(name)
|
27
13
|
super(
|
28
14
|
<<~MSG
|
29
|
-
Plug with name +#{name}+
|
30
|
-
|
31
|
-
|
15
|
+
Plug with name +#{name}+ can't be resolved. You must provide
|
16
|
+
something responding to `#call` or `#to_proc`, or a key for
|
17
|
+
something registered in the container obeying those exact
|
18
|
+
constraints. If nothing is given, it's expected to be a method
|
19
|
+
defined in the context object.
|
32
20
|
MSG
|
33
21
|
)
|
34
22
|
end
|
35
23
|
end
|
36
|
-
|
37
|
-
# Type for the name of a plug.
|
38
24
|
Name = Types::Strict::Symbol.constructor(&:to_sym)
|
39
25
|
|
40
|
-
|
41
|
-
|
42
|
-
# {Plug}.
|
43
|
-
Spec = ConnSupport::Composition::Operation |
|
26
|
+
Spec = Types.Interface(:call) |
|
27
|
+
Types.Interface(:to_proc) |
|
44
28
|
Types.Constant(nil) |
|
45
29
|
Types::Strict::String |
|
46
30
|
Types::Strict::Symbol
|
47
31
|
|
48
|
-
# Type for an instance of self.
|
49
|
-
Instance = Types.Instance(self)
|
50
|
-
|
51
|
-
# Schema expected to inject plugs.
|
52
|
-
#
|
53
|
-
# @see #inject_and_resolve
|
54
32
|
Injections = Types::Strict::Hash.map(
|
55
|
-
|
33
|
+
Name, Spec
|
56
34
|
).default(Types::EMPTY_HASH)
|
57
35
|
|
58
|
-
# @!attribute [r] name
|
59
|
-
# @return [Name[]]
|
60
36
|
attribute :name, Name
|
61
37
|
|
62
|
-
# @!attribute [r] spec
|
63
|
-
# @return [Spec[]]
|
64
38
|
attribute :spec, Spec
|
65
39
|
|
66
|
-
# Creates a new instance with given `spec` but keeping `name`.
|
67
|
-
#
|
68
|
-
# @param new_spec [Spec[]]
|
69
|
-
# @return [self]
|
70
40
|
def with(new_spec)
|
71
41
|
new(spec: new_spec)
|
72
42
|
end
|
73
43
|
|
74
|
-
#
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
# @raise [InvalidPlugError] When nothing callable is resolved.
|
81
|
-
def call(container, pipe)
|
82
|
-
if spec.respond_to?(:call)
|
44
|
+
# rubocop:disable Metrics/MethodLength
|
45
|
+
# rubocop:disable Metrics/AbcSize
|
46
|
+
def call(container, context)
|
47
|
+
if spec.respond_to?(:to_proc) && !spec.is_a?(Symbol)
|
48
|
+
spec.to_proc
|
49
|
+
elsif spec.respond_to?(:call)
|
83
50
|
spec
|
84
51
|
elsif spec.nil?
|
85
|
-
|
86
|
-
elsif container[spec]
|
87
|
-
container[spec]
|
52
|
+
context.method(name)
|
53
|
+
elsif container[spec]
|
54
|
+
with(container[spec]).call(container, context)
|
88
55
|
else
|
89
56
|
raise InvalidPlugError, name
|
90
57
|
end
|
91
58
|
end
|
59
|
+
# rubocop:enable Metrics/MethodLength
|
60
|
+
# rubocop:enable Metrics/AbcSize
|
92
61
|
|
93
|
-
|
94
|
-
#
|
95
|
-
# @param plugs [Array<Plug>]
|
96
|
-
# @param injections [InstanceMethods::PlugInjections[]]
|
97
|
-
# @container container [Types::Container[]]
|
98
|
-
# @object [Object]
|
99
|
-
#
|
100
|
-
# @return [Array<ConnSupport::Composition::Operation[]>]
|
101
|
-
def self.inject_and_resolve(plugs, injections, container, object)
|
62
|
+
def self.inject(plugs, injections)
|
102
63
|
plugs.map do |plug|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
64
|
+
inject_plug(plug, injections)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.inject_plug(plug, injections)
|
69
|
+
name = plug.name
|
70
|
+
if injections.key?(name)
|
71
|
+
plug.with(injections[name])
|
72
|
+
else
|
73
|
+
plug
|
108
74
|
end
|
109
75
|
end
|
110
76
|
end
|
@@ -1,43 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'web_pipe/types'
|
4
3
|
require 'web_pipe/rack_support/middleware'
|
5
4
|
require 'rack'
|
6
5
|
|
7
6
|
module WebPipe
|
8
7
|
module RackSupport
|
9
|
-
# Helper to build and call a rack application with middlewares.
|
10
|
-
#
|
11
8
|
# @api private
|
12
9
|
class AppWithMiddlewares
|
13
|
-
|
14
|
-
#
|
15
|
-
# It should be something callable accepting a rack env and
|
16
|
-
# returning a rack response.
|
17
|
-
App = Types.Interface(:call)
|
18
|
-
|
19
|
-
# @!attribute [r] rack_middlewares
|
20
|
-
# @return [Array<RackMiddleware>]
|
21
|
-
attr_reader :rack_middlewares
|
22
|
-
|
23
|
-
# @!attribute [r] app
|
24
|
-
# @return [App[]]
|
25
|
-
attr_reader :app
|
26
|
-
|
27
|
-
# @return [Rack::Builder]
|
28
|
-
attr_reader :builder
|
10
|
+
attr_reader :rack_middlewares, :app, :builder
|
29
11
|
|
30
12
|
def initialize(rack_middlewares, app)
|
31
|
-
@rack_middlewares =
|
32
|
-
@app =
|
13
|
+
@rack_middlewares = rack_middlewares
|
14
|
+
@app = app
|
33
15
|
@builder = build_rack_app(rack_middlewares, app)
|
34
16
|
end
|
35
17
|
|
36
|
-
# Calls rack application.
|
37
|
-
#
|
38
|
-
# @param env [Hash] Rack env
|
39
|
-
#
|
40
|
-
# @return [Array] Rack resonse
|
41
18
|
def call(env)
|
42
19
|
builder.call(env)
|
43
20
|
end
|
@@ -5,10 +5,10 @@ require 'dry/struct'
|
|
5
5
|
|
6
6
|
module WebPipe
|
7
7
|
module RackSupport
|
8
|
+
# Wrapper for a rack middleware.
|
9
|
+
#
|
8
10
|
# Simple data structure to represent a rack middleware class with
|
9
11
|
# its initialization options.
|
10
|
-
#
|
11
|
-
# @api private
|
12
12
|
class Middleware < Dry::Struct
|
13
13
|
# Type for a rack middleware class.
|
14
14
|
MiddlewareClass = Types.Instance(Class)
|
@@ -6,74 +6,45 @@ require 'web_pipe/types'
|
|
6
6
|
|
7
7
|
module WebPipe
|
8
8
|
module RackSupport
|
9
|
-
# Specification on how to resolve {Rack::Middleware}'s.
|
10
|
-
#
|
11
|
-
# Rack middlewares can be specified in two ways:
|
12
|
-
#
|
13
|
-
# - As an array where fist element is a rack middleware class
|
14
|
-
# while the rest of elements are its initialization options.
|
15
|
-
# - A single element array where it is an instance of a class
|
16
|
-
# including {WebPipe}. This specifies all {RackSupport::Middlewares} for
|
17
|
-
# that {WebPipe}.
|
18
|
-
#
|
19
9
|
# @api private
|
20
10
|
class MiddlewareSpecification < Dry::Struct
|
21
|
-
# Type for the name given to a middleware.
|
22
11
|
Name = Types::Strict::Symbol.constructor(&:to_sym)
|
23
12
|
|
24
|
-
# Poor type for the specification to resolve a rack middleware.
|
25
13
|
Spec = Types::Strict::Array
|
26
14
|
|
27
|
-
# Schema expected to inject middleware specifications.
|
28
|
-
#
|
29
|
-
# @see #inject_and_resolve
|
30
15
|
Injections = Types::Strict::Hash.map(
|
31
|
-
|
16
|
+
Name, Spec
|
32
17
|
).default(Types::EMPTY_HASH)
|
33
18
|
|
34
|
-
# @!attribute [r] name
|
35
|
-
# @return [Name[]]
|
36
19
|
attribute :name, Name
|
37
20
|
|
38
|
-
# @!attribute [r] spec
|
39
|
-
# @return [Spec[]]
|
40
21
|
attribute :spec, Spec
|
41
22
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def self.
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end.flatten
|
23
|
+
def self.inject(middleware_specifications, injections)
|
24
|
+
middleware_specifications.map do |middleware_spec|
|
25
|
+
inject_middleware(middleware_spec, injections)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.inject_middleware(middleware_spec, injections)
|
30
|
+
name = middleware_spec.name
|
31
|
+
if injections.key?(name)
|
32
|
+
middleware_spec.with(injections[name])
|
33
|
+
else
|
34
|
+
middleware_spec
|
35
|
+
end
|
56
36
|
end
|
57
37
|
|
58
|
-
# Resolves {RackSupport::Middlewares} from given specification.
|
59
|
-
#
|
60
|
-
# @return [Array<RackSupport::Middleware>]
|
61
38
|
def call
|
62
|
-
|
39
|
+
klass_or_pipe = spec[0]
|
63
40
|
options = spec[1..] || Types::EMPTY_ARRAY
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
[Middleware.new(middleware: klass, options: options)]
|
41
|
+
if klass_or_pipe.respond_to?(:to_middlewares)
|
42
|
+
klass_or_pipe.to_middlewares
|
43
|
+
elsif klass_or_pipe.is_a?(Class)
|
44
|
+
[Middleware.new(middleware: klass_or_pipe, options: options)]
|
69
45
|
end
|
70
46
|
end
|
71
47
|
|
72
|
-
# Returns new instance with {#spec} replaced.
|
73
|
-
#
|
74
|
-
# @param new_spec [Spec[]]
|
75
|
-
#
|
76
|
-
# @return [MiddlewareSpecification]
|
77
48
|
def with(new_spec)
|
78
49
|
new(spec: new_spec)
|
79
50
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'web_pipe/conn_support/builder'
|
4
|
+
require 'rack/mock'
|
5
|
+
|
6
|
+
module WebPipe
|
7
|
+
# Test helper methods.
|
8
|
+
#
|
9
|
+
# This module is meant to be included in a test file to provide helper
|
10
|
+
# methods.
|
11
|
+
module TestSupport
|
12
|
+
# Builds a {WebPipe::Conn}
|
13
|
+
#
|
14
|
+
# @param uri [String] URI that will be used to populate the request
|
15
|
+
# attributes
|
16
|
+
# @param attributes [Hash<Symbol, Any>] Manually set attributes for the
|
17
|
+
# struct. It overrides what is taken from the `uri` parameter
|
18
|
+
# @param env_opts [Hash] Options to be added to the `env` from which the
|
19
|
+
# connection struct is created. See {Rack::MockRequest.env_for}.
|
20
|
+
# @return [Conn]
|
21
|
+
def build_conn(uri = '', attributes: {}, env_opts: {})
|
22
|
+
env = Rack::MockRequest.env_for(uri, env_opts)
|
23
|
+
ConnSupport::Builder
|
24
|
+
.call(env)
|
25
|
+
.new(attributes)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/web_pipe/types.rb
CHANGED
@@ -4,11 +4,9 @@ require 'dry/types'
|
|
4
4
|
require 'dry/core/constants'
|
5
5
|
|
6
6
|
module WebPipe
|
7
|
-
# Namespace for generic
|
7
|
+
# Namespace for generic types.
|
8
8
|
module Types
|
9
9
|
include Dry.Types()
|
10
10
|
include Dry::Core::Constants
|
11
|
-
|
12
|
-
Container = Interface(:[])
|
13
11
|
end
|
14
12
|
end
|
data/lib/web_pipe/version.rb
CHANGED