web_pipe 0.13.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- 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(
|