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
@@ -1,19 +1,19 @@
|
|
1
1
|
# Injecting middlewares
|
2
2
|
|
3
|
-
|
3
|
+
You can inject middlewares at the moment an application is initialized,
|
4
4
|
allowing you to override what you have defined in the DSL.
|
5
5
|
|
6
|
-
For that purpose, you have to use `middlewares:` keyword argument. It must be a
|
6
|
+
For that purpose, you have to use the `middlewares:` keyword argument. It must be a
|
7
7
|
hash where middlewares are matched by the name you gave them in its definition.
|
8
8
|
|
9
|
-
A middleware must be specified as an `Array`.
|
10
|
-
middleware class. The rest of arguments (if any) should be any
|
9
|
+
A middleware must be specified as an `Array`. The first item must be a rack
|
10
|
+
middleware class. The rest of the arguments (if any) should be any options it may
|
11
11
|
need.
|
12
12
|
|
13
|
-
|
13
|
+
That is mainly useful for testing purposes, where you can switch a heavy
|
14
14
|
middleware and use a mocked one instead.
|
15
15
|
|
16
|
-
In the following example, rack session mechanism
|
16
|
+
In the following example, we mock the rack session mechanism:
|
17
17
|
|
18
18
|
```ruby
|
19
19
|
# config.ru
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Inspecting middlewares
|
2
|
+
|
3
|
+
Once a `WebPipe` class is initialized, all its middlewares get resolved. You
|
4
|
+
can access them through the `#middlewares` method.
|
5
|
+
|
6
|
+
Each middleware is represented by a
|
7
|
+
`WebPipe::RackSupport::MiddlewareSpecification` instance, which contains two
|
8
|
+
accessors: `middleware` returns the middleware class. In contrast, `options`
|
9
|
+
returns an array with the arguments provided to the middleware on
|
10
|
+
initialization.
|
11
|
+
|
12
|
+
Keep in mind that every middleware is resolved as an array. That is because it
|
13
|
+
can be composed by a chain of middlewares built through
|
14
|
+
[composition](composing_middlewares.md).
|
15
|
+
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
require 'web_pipe'
|
19
|
+
require 'rack/session'
|
20
|
+
|
21
|
+
class MyApp
|
22
|
+
include WebPipe
|
23
|
+
|
24
|
+
use :session, Rack::Session::Cookie, key: 'my_app.session', secret: 'long'
|
25
|
+
|
26
|
+
plug(:hello) do |conn|
|
27
|
+
conn.set_response_body('Hello world!')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
app = MyApp.new
|
32
|
+
session_middleware = app.middlewares[:session][0]
|
33
|
+
session_middleware.middleware # => Rack::Session::Cookie
|
34
|
+
session_middleware.options # => [{ key: 'my_app.session', secret: 'long' }]
|
35
|
+
```
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# Using rack middlewares
|
2
2
|
|
3
3
|
A one-way pipe like the one `web_pipe` implements can deal with any required
|
4
|
-
feature in a web application. However, usually it is convenient to
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
feature in a web application. However, usually, it is convenient to use some
|
5
|
+
well-known rack middleware, so you don't have to reinvent the wheel. Even if
|
6
|
+
you can add them to the router layer, `web_pipe` allows you to encapsulate them
|
7
|
+
in your application definition.
|
8
8
|
|
9
|
-
|
9
|
+
To add rack middlewares to the stack, you have to use the DSL method
|
10
10
|
`use`. The first argument it takes is a `Symbol` with the name you want to
|
11
11
|
assign to it (which is needed to allow
|
12
12
|
[injection](using_rack_middlewares/injecting_middlewares.md) on
|
13
|
-
initialization). Then, it must follow the middleware class and any
|
13
|
+
initialization). Then, it must follow the middleware class and any options it
|
14
14
|
may need:
|
15
15
|
|
16
16
|
```ruby
|
data/lib/web_pipe/app.rb
CHANGED
@@ -1,54 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'web_pipe/types'
|
4
3
|
require 'web_pipe/conn'
|
5
4
|
require 'web_pipe/conn_support/builder'
|
6
5
|
require 'web_pipe/conn_support/composition'
|
7
6
|
|
8
7
|
module WebPipe
|
9
|
-
# Rack
|
10
|
-
#
|
8
|
+
# Rack app built from a chain of functions that take and return a
|
9
|
+
# {WebPipe::Conn}.
|
11
10
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
11
|
+
# This is the abstraction encompassing a rack application built only with the
|
12
|
+
# functions on {WebPipe::Conn}. {WebPipe::RackSupport::AppWithMiddlewares}
|
13
|
+
# takes middlewares also into account.
|
15
14
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# callable accepting a {Conn} and returning a {Conn}).
|
19
|
-
# - Convert last {Conn} back to a rack response and
|
20
|
-
# return it.
|
15
|
+
# A rack application is something callable that takes the rack environment as
|
16
|
+
# an argument, and returns a rack response. So, this class needs to:
|
21
17
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
18
|
+
# - Take rack's environment and create a {WebPipe::Conn} struct from there.
|
19
|
+
# - Starting from the initial struct, apply the pipe of functions.
|
20
|
+
# - Convert the last {WebPipe::Conn} back to a rack response.
|
21
|
+
#
|
22
|
+
# {WebPipe::Conn} can itself be of two different types (subclasses of it}:
|
23
|
+
# {Conn::Ongoing} and {Conn::Halted}. The pipe is stopped on two scenarios:
|
24
|
+
#
|
25
|
+
# - The end of the pipe is reached.
|
26
|
+
# - One function returns a {Conn::Halted}.
|
26
27
|
class App
|
27
|
-
# Type for a rack environment.
|
28
|
-
RackEnv = Types::Strict::Hash
|
29
|
-
|
30
28
|
include Dry::Monads::Result::Mixin
|
31
29
|
|
32
30
|
# @!attribute [r] operations
|
33
|
-
# @return [Array<
|
31
|
+
# @return [Array<Proc>]
|
34
32
|
attr_reader :operations
|
35
33
|
|
34
|
+
# @param operations [Array<Proc>]
|
36
35
|
def initialize(operations)
|
37
|
-
@operations =
|
38
|
-
ConnSupport::Composition::Operation
|
39
|
-
)[operations]
|
36
|
+
@operations = operations
|
40
37
|
end
|
41
38
|
|
42
|
-
# @param env [Hash] Rack
|
39
|
+
# @param env [Hash] Rack environment
|
43
40
|
#
|
44
41
|
# @return env [Array] Rack response
|
45
42
|
# @raise ConnSupport::Composition::InvalidOperationResult when an
|
46
|
-
# operation
|
43
|
+
# operation doesn't return a {WebPipe::Conn}
|
47
44
|
def call(env)
|
48
45
|
extract_rack_response(
|
49
46
|
apply_operations(
|
50
47
|
conn_from_env(
|
51
|
-
|
48
|
+
env
|
52
49
|
)
|
53
50
|
)
|
54
51
|
)
|
data/lib/web_pipe/conn.rb
CHANGED
@@ -6,15 +6,8 @@ require 'web_pipe/conn_support/headers'
|
|
6
6
|
|
7
7
|
module WebPipe
|
8
8
|
module ConnSupport
|
9
|
-
# Helper module to build a {Conn} from a rack's env.
|
10
|
-
#
|
11
|
-
# It always return a {Conn::Ongoing} subclass.
|
12
|
-
#
|
13
9
|
# @api private
|
14
10
|
module Builder
|
15
|
-
# @param env [Types::Env] Rack's env
|
16
|
-
#
|
17
|
-
# @return [Conn::Ongoing]
|
18
11
|
# rubocop:disable Metrics/MethodLength
|
19
12
|
def self.call(env)
|
20
13
|
rr = Rack::Request.new(env)
|
@@ -1,31 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'dry/monads/result'
|
4
|
-
require 'web_pipe/types'
|
5
4
|
require 'web_pipe/conn'
|
6
5
|
|
7
6
|
module WebPipe
|
8
7
|
module ConnSupport
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# It represents the composition of a series of functions which
|
12
|
-
# take a {Conn} as argument and return a {Conn}.
|
13
|
-
#
|
14
|
-
# However, {Conn} can itself be of two different types (subclasses
|
15
|
-
# of it): a {Conn::Ongoing} or a {Conn::Halted}. On execution time,
|
16
|
-
# the composition is stopped whenever the stack is emptied or a
|
17
|
-
# {Conn::Halted} is returned in any of the steps.
|
8
|
+
# @api private
|
18
9
|
class Composition
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# It should be anything callable expecting a {Conn} and
|
22
|
-
# returning a {Conn}.
|
23
|
-
Operation = Types.Interface(:call)
|
24
|
-
|
25
|
-
# Error raised when an {Operation} returns something that is not
|
26
|
-
# a {Conn}.
|
10
|
+
# Raised when operation doesn't return a {WebPipe::Conn}.
|
27
11
|
class InvalidOperationResult < RuntimeError
|
28
|
-
# @param returned [Any] What was returned from the {Operation}
|
29
12
|
def initialize(returned)
|
30
13
|
super(
|
31
14
|
<<~MSG
|
@@ -39,18 +22,12 @@ module WebPipe
|
|
39
22
|
|
40
23
|
include Dry::Monads::Result::Mixin
|
41
24
|
|
42
|
-
# @!attribute [r] operations
|
43
|
-
# @return [Array<Operation[]>]
|
44
25
|
attr_reader :operations
|
45
26
|
|
46
27
|
def initialize(operations)
|
47
|
-
@operations =
|
28
|
+
@operations = operations
|
48
29
|
end
|
49
30
|
|
50
|
-
# @param conn [Conn]
|
51
|
-
# @return [Conn]
|
52
|
-
# @raise InvalidOperationResult when an operation does not
|
53
|
-
# return a {Conn}
|
54
31
|
def call(conn)
|
55
32
|
extract_result(
|
56
33
|
apply_operations(
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module WebPipe
|
4
4
|
module ConnSupport
|
5
|
-
# Error raised when trying to fetch an entry in {Conn#bag} for an
|
5
|
+
# Error raised when trying to fetch an entry in {WebPipe::Conn#bag} for an
|
6
6
|
# unknown key.
|
7
7
|
class KeyNotFoundInBagError < KeyError
|
8
8
|
# @param key [Any] Key not found in the bag
|
@@ -15,8 +15,8 @@ module WebPipe
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
# Error raised when trying to fetch an entry in {
|
19
|
-
#
|
18
|
+
# Error raised when trying to fetch an entry in {WebPipeConn#config} for an
|
19
|
+
# unknown key.
|
20
20
|
class KeyNotFoundInConfigError < KeyError
|
21
21
|
# @param key [Any] Key not found in config
|
22
22
|
def initialize(key)
|
@@ -28,8 +28,8 @@ module WebPipe
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
# Error raised when trying to use a
|
32
|
-
#
|
31
|
+
# Error raised when trying to use a {WebPipe::Conn} feature which requires a
|
32
|
+
# rack middleware that is not present
|
33
33
|
class MissingMiddlewareError < RuntimeError
|
34
34
|
# @param feature [String] Name of the feature intended to be used
|
35
35
|
# @param middleware [String] Name of the missing middleware
|
@@ -2,25 +2,14 @@
|
|
2
2
|
|
3
3
|
module WebPipe
|
4
4
|
module ConnSupport
|
5
|
-
# Helpers to work with headers and its rack's env representation.
|
6
|
-
#
|
7
5
|
# @api private
|
8
6
|
module Headers
|
9
7
|
# Headers which come as plain CGI-like variables (without the `HTTP_`
|
10
8
|
# prefixed) from the rack server.
|
11
9
|
HEADERS_AS_CGI = %w[CONTENT_TYPE CONTENT_LENGTH].freeze
|
12
10
|
|
13
|
-
# Extracts headers from rack's env.
|
14
|
-
#
|
15
11
|
# Headers are all those pairs which key begins with `HTTP_` plus
|
16
12
|
# those detailed in {HEADERS_AS_CGI}.
|
17
|
-
#
|
18
|
-
# @param env [Types::Env[]]
|
19
|
-
#
|
20
|
-
# @return [Types::Headers[]]
|
21
|
-
#
|
22
|
-
# @see HEADERS_AS_CGI
|
23
|
-
# @see .normalize_key
|
24
13
|
def self.extract(env)
|
25
14
|
Hash[
|
26
15
|
env
|
@@ -34,51 +23,20 @@ module WebPipe
|
|
34
23
|
]
|
35
24
|
end
|
36
25
|
|
37
|
-
# Adds key/value pair to given headers.
|
38
|
-
#
|
39
|
-
# Key is normalized.
|
40
|
-
#
|
41
|
-
# @param headers [Type::Headers[]]
|
42
|
-
# @param key [String]
|
43
|
-
# @param value [String]
|
44
|
-
#
|
45
|
-
# @return [Type::Headers[]]
|
46
|
-
#
|
47
|
-
# @see .normalize_key
|
48
26
|
def self.add(headers, key, value)
|
49
27
|
Hash[
|
50
28
|
headers.to_a.push(pair(key, value))
|
51
29
|
]
|
52
30
|
end
|
53
31
|
|
54
|
-
# Deletes pair with given key form headers.
|
55
|
-
#
|
56
|
-
# Accepts a non normalized key.
|
57
|
-
#
|
58
|
-
# @param headers [Type::Headers[]]
|
59
|
-
# @param key [String]
|
60
|
-
#
|
61
|
-
# @return [Type::Headers[]]
|
62
|
-
#
|
63
|
-
# @see .normalize_key
|
64
32
|
def self.delete(headers, key)
|
65
33
|
headers.reject { |k, _v| normalize_key(key) == k }
|
66
34
|
end
|
67
35
|
|
68
|
-
# Creates a pair with normalized key and raw value.
|
69
|
-
#
|
70
|
-
# @param key [String]
|
71
|
-
# @param key [String]
|
72
|
-
#
|
73
|
-
# @return [Array<String>]
|
74
|
-
#
|
75
|
-
# @see .normalize_key
|
76
36
|
def self.pair(key, value)
|
77
37
|
[normalize_key(key), value]
|
78
38
|
end
|
79
39
|
|
80
|
-
# Normalizes a header key to convention.
|
81
|
-
#
|
82
40
|
# As per RFC2616, headers names are case insensitive. This
|
83
41
|
# function normalizes them to PascalCase acting on dashes ('-').
|
84
42
|
#
|
@@ -86,18 +44,11 @@ module WebPipe
|
|
86
44
|
# dashes and underscores (`_`) are treated as dashes. This
|
87
45
|
# function substitutes all '-' to '_'.
|
88
46
|
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# @return [String]
|
92
|
-
#
|
93
|
-
# @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
47
|
+
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
94
48
|
def self.normalize_key(key)
|
95
49
|
key.downcase.gsub('_', '-').split('-').map(&:capitalize).join('-')
|
96
50
|
end
|
97
51
|
|
98
|
-
# Returns a new hash with all keys normalized.
|
99
|
-
#
|
100
|
-
# @see #normalize_key
|
101
52
|
def self.normalize(headers)
|
102
53
|
headers.transform_keys { |k| normalize_key(k) }
|
103
54
|
end
|
@@ -5,10 +5,10 @@ require 'rack/request'
|
|
5
5
|
|
6
6
|
module WebPipe
|
7
7
|
module ConnSupport
|
8
|
-
# Types used
|
8
|
+
# Types used in the {WebPipe::Conn} struct.
|
9
9
|
#
|
10
|
-
#
|
11
|
-
# attributes for documentation.
|
10
|
+
# The implementation self-describes them, but you can look at the
|
11
|
+
# {WebPipe::Conn} attributes for documentation.
|
12
12
|
module Types
|
13
13
|
include Dry.Types()
|
14
14
|
|
data/lib/web_pipe/dsl/builder.rb
CHANGED
@@ -1,36 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'web_pipe/types'
|
4
3
|
require 'web_pipe/dsl/class_context'
|
5
|
-
require 'web_pipe/dsl/
|
4
|
+
require 'web_pipe/dsl/instance_context'
|
5
|
+
require 'web_pipe/pipe'
|
6
6
|
|
7
7
|
module WebPipe
|
8
8
|
module DSL
|
9
|
-
# When an instance of it is included in a module, the module
|
10
|
-
# extends a {ClassContext} instance and includes
|
11
|
-
# {InstanceMethods}.
|
12
|
-
#
|
13
9
|
# @api private
|
14
10
|
class Builder < Module
|
15
|
-
|
16
|
-
EMPTY_CONTAINER = Types::EMPTY_HASH
|
11
|
+
attr_reader :class_context, :instance_context
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def initialize(container: EMPTY_CONTAINER)
|
26
|
-
@container = Types::Container[container]
|
27
|
-
@class_context = ClassContext.new(container: container)
|
13
|
+
def initialize(container: Pipe::EMPTY_CONTAINER)
|
14
|
+
@class_context = ClassContext.new
|
15
|
+
@instance_context = InstanceContext.new(
|
16
|
+
class_context: class_context,
|
17
|
+
container: container
|
18
|
+
)
|
28
19
|
super()
|
29
20
|
end
|
30
21
|
|
31
22
|
def included(klass)
|
32
23
|
klass.extend(class_context)
|
33
|
-
klass.include(
|
24
|
+
klass.include(instance_context)
|
34
25
|
end
|
35
26
|
end
|
36
27
|
end
|
@@ -1,57 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'web_pipe/types'
|
4
|
-
require 'web_pipe/dsl/dsl_context'
|
5
|
-
|
6
3
|
module WebPipe
|
7
4
|
module DSL
|
8
|
-
# Defines the DSL and keeps the state for the pipe.
|
9
|
-
#
|
10
|
-
# This is good to be an instance because it keeps the
|
11
|
-
# configuration (state) for the pipe class: the container
|
12
|
-
# configured on initialization and both rack middlewares and plugs
|
13
|
-
# added through the DSL {DSLContext}.
|
14
|
-
#
|
15
|
-
# As the pipe is extended with an instance of this class, methods
|
16
|
-
# that are meant to be class methods in the pipe are defined as
|
17
|
-
# singleton methods of the instance.
|
18
|
-
#
|
19
5
|
# @api private
|
20
6
|
class ClassContext < Module
|
21
|
-
|
22
|
-
DSL_METHODS = %i[middleware_specifications use plugs plug compose].freeze
|
7
|
+
DSL_METHODS = %i[use plug compose].freeze
|
23
8
|
|
24
|
-
|
25
|
-
# @return [Types::Container[]]
|
26
|
-
attr_reader :container
|
9
|
+
attr_reader :ast
|
27
10
|
|
28
|
-
|
29
|
-
|
11
|
+
def initialize
|
12
|
+
@ast = []
|
13
|
+
super
|
14
|
+
end
|
30
15
|
|
31
|
-
def
|
32
|
-
|
33
|
-
@dsl_context = DSLContext.new([], [])
|
34
|
-
define_container
|
35
|
-
define_dsl
|
36
|
-
super()
|
16
|
+
def extended(klass)
|
17
|
+
define_dsl_methods(klass, ast)
|
37
18
|
end
|
38
19
|
|
39
20
|
private
|
40
21
|
|
41
|
-
def
|
42
|
-
module_exec(container) do |container|
|
43
|
-
define_method(:container) do
|
44
|
-
container
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def define_dsl
|
22
|
+
def define_dsl_methods(klass, ast)
|
50
23
|
DSL_METHODS.each do |method|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
24
|
+
klass.define_singleton_method(method) do |*args, **kwargs, &block|
|
25
|
+
ast << if block_given?
|
26
|
+
[method, args, kwargs, block]
|
27
|
+
else
|
28
|
+
[method, args, kwargs]
|
29
|
+
end
|
55
30
|
end
|
56
31
|
end
|
57
32
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'web_pipe/pipe'
|
4
|
+
|
5
|
+
module WebPipe
|
6
|
+
module DSL
|
7
|
+
# @api private
|
8
|
+
class InstanceContext < Module
|
9
|
+
PIPE_METHODS = %i[
|
10
|
+
call middlewares operations to_proc to_middlewares
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
attr_reader :container, :class_context
|
14
|
+
|
15
|
+
def initialize(container:, class_context:)
|
16
|
+
@container = container
|
17
|
+
@class_context = class_context
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
|
21
|
+
def included(klass)
|
22
|
+
define_initialize(klass, class_context.ast, container)
|
23
|
+
define_pipe_methods(klass)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# rubocop:disable Metrics/MethodLength
|
29
|
+
def define_initialize(klass, ast, container)
|
30
|
+
klass.define_method(:initialize) do |plugs: {}, middlewares: {}|
|
31
|
+
acc = Pipe.new(container: container, context: self)
|
32
|
+
@pipe = ast.reduce(acc) do |pipe, node|
|
33
|
+
method, args, kwargs, block = node
|
34
|
+
if block
|
35
|
+
pipe.send(method, *args, **kwargs, &block)
|
36
|
+
else
|
37
|
+
pipe.send(method, *args, **kwargs)
|
38
|
+
end
|
39
|
+
end.inject(plugs: plugs, middleware_specifications: middlewares)
|
40
|
+
end
|
41
|
+
# rubocop:enable Metrics/MethodLength
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_pipe_methods(klass)
|
45
|
+
PIPE_METHODS.each do |method|
|
46
|
+
klass.define_method(method) do |*args|
|
47
|
+
@pipe.send(method, *args)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -2,22 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'web_pipe'
|
4
4
|
|
5
|
-
|
5
|
+
# :nodoc:
|
6
6
|
module WebPipe
|
7
|
-
#
|
8
|
-
# `:container` key.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# require 'web_pipe'
|
12
|
-
#
|
13
|
-
# WebPipe.load_extensions(:container)
|
14
|
-
#
|
15
|
-
# class App
|
16
|
-
# include WebPipe
|
17
|
-
#
|
18
|
-
# plug :container, ->(conn) { conn.add_config(:container, MyContainer) }
|
19
|
-
# plug :render, ->(conn) { conn.set_response_body(conn.container['view']) }
|
20
|
-
# end
|
7
|
+
# See the docs for the extension linked from the README.
|
21
8
|
module Container
|
22
9
|
# Returns {Conn#config} `:container` value
|
23
10
|
#
|
@@ -1,40 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'web_pipe'
|
4
|
-
require 'web_pipe/types'
|
5
4
|
require 'rack/utils'
|
6
5
|
|
7
|
-
|
6
|
+
# :nodoc:
|
8
7
|
module WebPipe
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# This extension helps with the addition of the `Set-Cookie` header
|
12
|
-
# to the response, which is the way the server has to instruct the
|
13
|
-
# browser to keep a cookie. A cookie can be added with the
|
14
|
-
# {#set_cookie} method, while it can be marked for deletion with
|
15
|
-
# {#delete_cookie}. Remember that marking a cookie for deletion just
|
16
|
-
# means adding the same cookie name with an expiration time in the
|
17
|
-
# past.
|
18
|
-
#
|
19
|
-
# Besides, it also adds a {#request_cookies} method to get the
|
20
|
-
# cookies in the request.
|
21
|
-
#
|
22
|
-
# @example
|
23
|
-
# require 'web_pipe'
|
24
|
-
#
|
25
|
-
# WebPipe.load_extensions(:cookies)
|
26
|
-
#
|
27
|
-
# class SetCookie
|
28
|
-
# include WebPipe
|
29
|
-
#
|
30
|
-
# plug :set_cookie, ->(conn) { conn.set_cookie('foo', 'bar', path: '/') }
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# class DeleteCookie
|
34
|
-
# include WebPipe
|
35
|
-
#
|
36
|
-
# plug :delete_cookie, ->(conn) { conn.delete_cookie('foo', path: '/') }
|
37
|
-
# end
|
8
|
+
# See the docs for the extension linked from the README.
|
38
9
|
module Cookies
|
39
10
|
# Valid options for {#set_cookie}.
|
40
11
|
SET_COOKIE_OPTIONS = Types::Strict::Hash.schema(
|