web_pipe 0.15.1 → 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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +10 -0
  4. data/README.md +13 -10
  5. data/docs/building_a_rack_application.md +1 -1
  6. data/docs/composing_applications.md +4 -4
  7. data/docs/connection_struct/configuring_the_connection_struct.md +4 -4
  8. data/docs/connection_struct/halting_the_pipe.md +17 -19
  9. data/docs/connection_struct/sharing_data_downstream.md +9 -8
  10. data/docs/connection_struct.md +22 -19
  11. data/docs/design_model.md +10 -9
  12. data/docs/dsl_free_usage.md +85 -14
  13. data/docs/extensions/container.md +9 -10
  14. data/docs/extensions/cookies.md +4 -2
  15. data/docs/extensions/dry_schema.md +5 -4
  16. data/docs/extensions/flash.md +9 -11
  17. data/docs/extensions/hanami_view.md +10 -14
  18. data/docs/extensions/params.md +6 -4
  19. data/docs/extensions/rails.md +28 -34
  20. data/docs/extensions/redirect.md +5 -4
  21. data/docs/extensions/router_params.md +5 -5
  22. data/docs/extensions/session.md +4 -4
  23. data/docs/extensions/url.md +6 -6
  24. data/docs/extensions.md +5 -6
  25. data/docs/introduction.md +7 -7
  26. data/docs/plugging_operations/composing_operations.md +3 -3
  27. data/docs/plugging_operations/injecting_operations.md +4 -4
  28. data/docs/plugging_operations/inspecting_operations.md +1 -2
  29. data/docs/plugging_operations/resolving_operations.md +3 -3
  30. data/docs/plugging_operations.md +3 -3
  31. data/docs/plugs/config.md +1 -1
  32. data/docs/plugs/content_type.md +2 -1
  33. data/docs/plugs.md +6 -7
  34. data/docs/recipes/hanami_2_and_dry_rb_integration.md +12 -0
  35. data/docs/recipes/hanami_router_integration.md +3 -1
  36. data/docs/recipes/using_all_restful_methods.md +6 -5
  37. data/docs/using_rack_middlewares/composing_middlewares.md +2 -3
  38. data/docs/using_rack_middlewares/injecting_middlewares.md +6 -6
  39. data/docs/using_rack_middlewares/inspecting_middlewares.md +7 -6
  40. data/docs/using_rack_middlewares.md +6 -6
  41. data/lib/web_pipe/app.rb +22 -25
  42. data/lib/web_pipe/conn.rb +0 -1
  43. data/lib/web_pipe/conn_support/builder.rb +0 -7
  44. data/lib/web_pipe/conn_support/composition.rb +3 -26
  45. data/lib/web_pipe/conn_support/errors.rb +5 -5
  46. data/lib/web_pipe/conn_support/headers.rb +1 -50
  47. data/lib/web_pipe/conn_support/types.rb +3 -3
  48. data/lib/web_pipe/dsl/builder.rb +10 -19
  49. data/lib/web_pipe/dsl/class_context.rb +15 -40
  50. data/lib/web_pipe/dsl/instance_context.rb +53 -0
  51. data/lib/web_pipe/extensions/container/container.rb +2 -15
  52. data/lib/web_pipe/extensions/cookies/cookies.rb +2 -31
  53. data/lib/web_pipe/extensions/dry_schema/dry_schema.rb +2 -56
  54. data/lib/web_pipe/extensions/flash/flash.rb +2 -32
  55. data/lib/web_pipe/extensions/hanami_view/hanami_view.rb +2 -93
  56. data/lib/web_pipe/extensions/not_found/not_found.rb +2 -40
  57. data/lib/web_pipe/extensions/params/params.rb +2 -63
  58. data/lib/web_pipe/extensions/rails/rails.rb +2 -119
  59. data/lib/web_pipe/extensions/redirect/redirect.rb +2 -20
  60. data/lib/web_pipe/extensions/router_params/router_params.rb +1 -39
  61. data/lib/web_pipe/extensions/session/session.rb +2 -25
  62. data/lib/web_pipe/extensions/url/url.rb +2 -5
  63. data/lib/web_pipe/pipe.rb +229 -0
  64. data/lib/web_pipe/plug.rb +30 -75
  65. data/lib/web_pipe/plugs/config.rb +0 -2
  66. data/lib/web_pipe/plugs/content_type.rb +0 -2
  67. data/lib/web_pipe/rack_support/app_with_middlewares.rb +3 -26
  68. data/lib/web_pipe/rack_support/middleware.rb +2 -2
  69. data/lib/web_pipe/rack_support/middleware_specification.rb +17 -55
  70. data/lib/web_pipe/types.rb +1 -3
  71. data/lib/web_pipe/version.rb +1 -1
  72. data/lib/web_pipe.rb +77 -21
  73. data/web_pipe.gemspec +1 -0
  74. metadata +7 -6
  75. data/docs/recipes/dry_rb_integration.md +0 -17
  76. data/lib/web_pipe/dsl/dsl_context.rb +0 -85
  77. 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
- # Composition of a pipe of {Operation} on a {Conn}.
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
- # Type for an operation.
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 = Types.Array(Operation)[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 {Conn#config} for
19
- # an unknown key.
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 conn's feature which requires
32
- # a rack middleware which is not present
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
- # @param key [String]
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 for {Conn} struct.
8
+ # Types used in the {WebPipe::Conn} struct.
9
9
  #
10
- # Implementation self-describes them, but you can look at {Conn}
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
 
@@ -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/instance_methods'
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
- # Container with nothing registered.
16
- EMPTY_CONTAINER = Types::EMPTY_HASH
11
+ attr_reader :class_context, :instance_context
17
12
 
18
- # @!attribute [r] container
19
- # @return [Types::Container[]]
20
- attr_reader :container
21
-
22
- # @return [ClassContext]
23
- attr_reader :class_context
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(InstanceMethods)
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
- # Methods to be imported from the {DSLContext}.
22
- DSL_METHODS = %i[middleware_specifications use plugs plug compose].freeze
7
+ DSL_METHODS = %i[use plug compose].freeze
23
8
 
24
- # @!attribute [r] container
25
- # @return [Types::Container[]]
26
- attr_reader :container
9
+ attr_reader :ast
27
10
 
28
- # @return [DSLContext]
29
- attr_reader :dsl_context
11
+ def initialize
12
+ @ast = []
13
+ super
14
+ end
30
15
 
31
- def initialize(container:)
32
- @container = Types::Container[container]
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 define_container
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
- module_exec(dsl_context) do |dsl_context|
52
- define_method(method) do |*args, &block|
53
- dsl_context.method(method).call(*args, &block)
54
- end
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
- #:nodoc:
5
+ # :nodoc:
6
6
  module WebPipe
7
- # Extension adding a `#container` method which returns {Conn#config}
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
- #:nodoc:
6
+ # :nodoc:
8
7
  module WebPipe
9
- # Extension to help dealing with request and response cookies.
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
- #:nodoc:
7
+ # :nodoc:
8
8
  module WebPipe
9
- # Integration with `dry-schema` validation library.
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
- #:nodoc:
6
+ # :nodoc:
7
7
  module WebPipe
8
- # Provides with a typical flash messages functionality.
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
- #:nodoc:
7
+ # :nodoc:
8
8
  module WebPipe
9
- # Integration with `hanami-view` rendering system.
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