web_pipe 0.15.1 → 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 +1 -1
- data/CHANGELOG.md +10 -0
- data/README.md +13 -10
- 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/hanami_view.md +10 -14
- data/docs/extensions/params.md +6 -4
- data/docs/extensions/rails.md +28 -34
- 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 +1 -2
- 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/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 +7 -6
- 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 +2 -93
- data/lib/web_pipe/extensions/not_found/not_found.rb +2 -40
- data/lib/web_pipe/extensions/params/params.rb +2 -63
- data/lib/web_pipe/extensions/rails/rails.rb +2 -119
- 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 +30 -75
- 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 +17 -55
- data/lib/web_pipe/types.rb +1 -3
- data/lib/web_pipe/version.rb +1 -1
- data/lib/web_pipe.rb +77 -21
- data/web_pipe.gemspec +1 -0
- metadata +7 -6
- data/docs/recipes/dry_rb_integration.md +0 -17
- data/lib/web_pipe/dsl/dsl_context.rb +0 -85
- data/lib/web_pipe/dsl/instance_methods.rb +0 -114
@@ -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(
|
@@ -4,63 +4,9 @@ require 'web_pipe'
|
|
4
4
|
|
5
5
|
WebPipe.load_extensions(:params)
|
6
6
|
|
7
|
-
|
7
|
+
# :nodoc:
|
8
8
|
module WebPipe
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# This extension provides a simple integration with `dry-schema`
|
12
|
-
# library to streamline param sanitization.
|
13
|
-
#
|
14
|
-
# On its own, the library just provides with a
|
15
|
-
# `Conn#sanitized_params` method, which will return what is set into
|
16
|
-
# config's `:sanitized_params` key.
|
17
|
-
#
|
18
|
-
# This key in config is what will be populated by `SanitizeParams` plug. It
|
19
|
-
# takes as arguments the `dry-schema` schema that will be applied to
|
20
|
-
# `Conn#params` and an error handler taking `Conn` instance and the validation
|
21
|
-
# result:
|
22
|
-
#
|
23
|
-
# @example
|
24
|
-
# require 'web_pipe'
|
25
|
-
#
|
26
|
-
# WebPipe.load_extensions(:dry_schema)
|
27
|
-
#
|
28
|
-
# class App
|
29
|
-
# include WebPipe
|
30
|
-
#
|
31
|
-
# Schema = Dry::Schema.Params do
|
32
|
-
# required(:name).filled(:string)
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# plug :sanitize_params, WebPipe::Plugs::SanitizeParams.(
|
36
|
-
# Schema,
|
37
|
-
# ->(conn, result) { ... }
|
38
|
-
# )
|
39
|
-
# plug(:do_something_with_params) do |conn|
|
40
|
-
# DB.persist(:entity, conn.sanitized_params)
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# A common workflow is applying the same handler for all param
|
45
|
-
# sanitization across your application. This can be achieved configuring
|
46
|
-
# a `:param_sanitization_handler` in a upstream operation which can
|
47
|
-
# be composed downstream from any number of pipes. `SanitizeParams`
|
48
|
-
# will used configured handler if none is injected as
|
49
|
-
# argument.
|
50
|
-
#
|
51
|
-
# @example
|
52
|
-
# class App
|
53
|
-
# plug :sanitization_handler, ->(conn, result) { ... }
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# class Subapp
|
57
|
-
# Schema = Dry::Schema.Params { ... }
|
58
|
-
#
|
59
|
-
# plug :app, App.new
|
60
|
-
# plug :sanitize_params, WebPipe::Plugs::SanitizeParams.call(Schema)
|
61
|
-
# end
|
62
|
-
#
|
63
|
-
# @see https://dry-rb.org/gems/dry-schema/
|
9
|
+
# See the docs for the extension linked from the README.
|
64
10
|
module DrySchema
|
65
11
|
SANITIZED_PARAMS_KEY = :sanitized_params
|
66
12
|
|
@@ -3,39 +3,9 @@
|
|
3
3
|
require 'web_pipe/conn'
|
4
4
|
require 'web_pipe/conn_support/errors'
|
5
5
|
|
6
|
-
|
6
|
+
# :nodoc:
|
7
7
|
module WebPipe
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# require 'web_pipe'
|
12
|
-
# require 'rack/session/cookie'
|
13
|
-
# require 'rack-flash'
|
14
|
-
#
|
15
|
-
# WebPipe.load_extensions(:flash)
|
16
|
-
#
|
17
|
-
# class MyApp
|
18
|
-
# include WebPipe
|
19
|
-
#
|
20
|
-
# use :session, Rack::Session::Cookie, secret: 'secret'
|
21
|
-
# use :flash, Rack::Flash
|
22
|
-
#
|
23
|
-
# plug :add_to_flash, ->(conn) { conn.add_flash(:notice, 'Hello world') }
|
24
|
-
# plug :add_to_flash_now, ->(conn) { conn.add_flash_now(:notice_now, 'Hello world now') }
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# Usually, you will end up making `conn.flash` available to your view system:
|
28
|
-
#
|
29
|
-
# @example
|
30
|
-
# <div class="notice"><%= flash[:notice] %></div>
|
31
|
-
#
|
32
|
-
# For this extension to be used, `Rack::Flash` middleware must be
|
33
|
-
# added to the stack (gem name is `rack-flash3`. This middleware in
|
34
|
-
# turns depend on `Rack::Session` middleware.
|
35
|
-
#
|
36
|
-
# This extension is a very simple wrapper around `Rack::Flash` API.
|
37
|
-
#
|
38
|
-
# @see https://github.com/nakajima/rack-flash
|
8
|
+
# See the docs for the extension linked from the README.
|
39
9
|
module Flash
|
40
10
|
RACK_FLASH_KEY = 'x-rack.flash'
|
41
11
|
|
@@ -4,100 +4,9 @@ require 'web_pipe/types'
|
|
4
4
|
require 'web_pipe/conn'
|
5
5
|
require 'hanami/view'
|
6
6
|
|
7
|
-
|
7
|
+
# :nodoc:
|
8
8
|
module WebPipe
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# This extensions adds a {#view} method to {WebPipe::Conn} which
|
12
|
-
# sets the string output of a `hanami-view` view as response body.
|
13
|
-
#
|
14
|
-
# @example
|
15
|
-
# WebPipe.load_extensions(:hanami_view)
|
16
|
-
#
|
17
|
-
# class SayHelloView < Hanami::View
|
18
|
-
# config.paths = [File.join(__dir__, '..', 'templates')]
|
19
|
-
# config.template = 'say_hello'
|
20
|
-
#
|
21
|
-
# expose :name
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# class App
|
25
|
-
# include WebPipe
|
26
|
-
#
|
27
|
-
# plug :render
|
28
|
-
#
|
29
|
-
# def render(conn)
|
30
|
-
# conn.view(SayHello.new, name: 'Joe')
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
#
|
34
|
-
# If there is a `:container` configured (in {Conn#config}), the view
|
35
|
-
# instance can be resolved from it.
|
36
|
-
#
|
37
|
-
# @example
|
38
|
-
# WebPipe.load_extensions(:hanami_view, :container)
|
39
|
-
#
|
40
|
-
# class App
|
41
|
-
# include WebPipe
|
42
|
-
#
|
43
|
-
# Container = { 'views.say_hello' => SayHelloView.new }.freeze
|
44
|
-
#
|
45
|
-
# plug :config_container, ->(conn) { conn.add_config(:container, Container) }
|
46
|
-
# plug :render
|
47
|
-
#
|
48
|
-
# def render(conn)
|
49
|
-
# conn.view('views.say_hello', name: 'Joe')
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# Context ({Hanami::View::Context}) for the view can be set explicetly
|
54
|
-
# through the `context:` argument, as in a standard call to
|
55
|
-
# {Hanami::View#call}. However, it is possible to leverage configured
|
56
|
-
# default context while still being able to inject request specific
|
57
|
-
# context. For that to work, `:view_context` should be present in
|
58
|
-
# {WebPipe::Conn#config}. Its value must be a lambda accepting the
|
59
|
-
# {Conn} instance and returning a hash, which will be passed to
|
60
|
-
# {Hanami::View::Context#with} to create the final context at the
|
61
|
-
# moment {#view} is called.
|
62
|
-
#
|
63
|
-
# @example
|
64
|
-
# class MyContext < Hanami::View::Context
|
65
|
-
# attr_reader :current_path
|
66
|
-
#
|
67
|
-
# def initialize(current_path: nil, **options)
|
68
|
-
# @current_path = current_path
|
69
|
-
# super
|
70
|
-
# end
|
71
|
-
# end
|
72
|
-
#
|
73
|
-
# class SayHelloView < Hanami::View
|
74
|
-
# config.paths = [File.join(__dir__, '..', 'templates')]
|
75
|
-
# config.template = 'say_hello'
|
76
|
-
# config.default_context = MyContext.new
|
77
|
-
#
|
78
|
-
# expose :name
|
79
|
-
# end
|
80
|
-
#
|
81
|
-
# WebPipe.load_extensions(:url)
|
82
|
-
#
|
83
|
-
# class App
|
84
|
-
# include WebPipe
|
85
|
-
#
|
86
|
-
# plug :config_view_context
|
87
|
-
# plug :render
|
88
|
-
#
|
89
|
-
# def config_view_context(conn)
|
90
|
-
# conn.add_config(:view_context, ->(conn) { { current_path: conn.full_path } })
|
91
|
-
# end
|
92
|
-
#
|
93
|
-
# def render(conn)
|
94
|
-
# conn.view(SayHelloView.new, name: 'Joe') # `current_path`
|
95
|
-
# # will be available in the view scope
|
96
|
-
# end
|
97
|
-
# end
|
98
|
-
#
|
99
|
-
# @see https://github.com/hanami/view
|
100
|
-
# @see WebPipe::Container
|
9
|
+
# See the docs for the extension linked from the README.
|
101
10
|
module DryView
|
102
11
|
# Where to find in {#config} request's view context generator.
|
103
12
|
VIEW_CONTEXT_KEY = :view_context
|