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.
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