web_pipe 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/CHANGELOG.md +29 -0
  4. data/Gemfile +2 -2
  5. data/README.md +18 -11
  6. data/docs/building_a_rack_application.md +1 -1
  7. data/docs/composing_applications.md +4 -4
  8. data/docs/connection_struct/configuring_the_connection_struct.md +4 -4
  9. data/docs/connection_struct/halting_the_pipe.md +17 -19
  10. data/docs/connection_struct/sharing_data_downstream.md +9 -8
  11. data/docs/connection_struct.md +22 -19
  12. data/docs/design_model.md +10 -9
  13. data/docs/dsl_free_usage.md +85 -14
  14. data/docs/extensions/container.md +9 -10
  15. data/docs/extensions/cookies.md +4 -2
  16. data/docs/extensions/dry_schema.md +5 -4
  17. data/docs/extensions/flash.md +9 -11
  18. data/docs/extensions/{dry_view.md → hanami_view.md} +20 -22
  19. data/docs/extensions/not_found.md +40 -0
  20. data/docs/extensions/params.md +6 -4
  21. data/docs/extensions/rails.md +31 -38
  22. data/docs/extensions/redirect.md +5 -4
  23. data/docs/extensions/router_params.md +5 -5
  24. data/docs/extensions/session.md +4 -4
  25. data/docs/extensions/url.md +6 -6
  26. data/docs/extensions.md +5 -6
  27. data/docs/introduction.md +7 -7
  28. data/docs/plugging_operations/composing_operations.md +3 -3
  29. data/docs/plugging_operations/injecting_operations.md +4 -4
  30. data/docs/plugging_operations/inspecting_operations.md +24 -0
  31. data/docs/plugging_operations/resolving_operations.md +3 -3
  32. data/docs/plugging_operations.md +3 -3
  33. data/docs/plugs/config.md +1 -1
  34. data/docs/plugs/content_type.md +2 -1
  35. data/docs/plugs.md +6 -7
  36. data/docs/recipes/hanami_2_and_dry_rb_integration.md +12 -0
  37. data/docs/recipes/hanami_router_integration.md +3 -1
  38. data/docs/recipes/using_all_restful_methods.md +6 -5
  39. data/docs/testing.md +64 -0
  40. data/docs/using_rack_middlewares/composing_middlewares.md +2 -3
  41. data/docs/using_rack_middlewares/injecting_middlewares.md +6 -6
  42. data/docs/using_rack_middlewares/inspecting_middlewares.md +35 -0
  43. data/docs/using_rack_middlewares.md +6 -6
  44. data/lib/web_pipe/app.rb +22 -25
  45. data/lib/web_pipe/conn.rb +0 -1
  46. data/lib/web_pipe/conn_support/builder.rb +0 -7
  47. data/lib/web_pipe/conn_support/composition.rb +3 -26
  48. data/lib/web_pipe/conn_support/errors.rb +5 -5
  49. data/lib/web_pipe/conn_support/headers.rb +1 -50
  50. data/lib/web_pipe/conn_support/types.rb +3 -3
  51. data/lib/web_pipe/dsl/builder.rb +10 -19
  52. data/lib/web_pipe/dsl/class_context.rb +15 -40
  53. data/lib/web_pipe/dsl/instance_context.rb +53 -0
  54. data/lib/web_pipe/extensions/container/container.rb +2 -15
  55. data/lib/web_pipe/extensions/cookies/cookies.rb +2 -31
  56. data/lib/web_pipe/extensions/dry_schema/dry_schema.rb +2 -56
  57. data/lib/web_pipe/extensions/flash/flash.rb +2 -32
  58. data/lib/web_pipe/extensions/hanami_view/hanami_view.rb +67 -0
  59. data/lib/web_pipe/extensions/not_found/not_found.rb +26 -0
  60. data/lib/web_pipe/extensions/params/params.rb +2 -63
  61. data/lib/web_pipe/extensions/rails/rails.rb +2 -123
  62. data/lib/web_pipe/extensions/redirect/redirect.rb +2 -20
  63. data/lib/web_pipe/extensions/router_params/router_params.rb +1 -39
  64. data/lib/web_pipe/extensions/session/session.rb +2 -25
  65. data/lib/web_pipe/extensions/url/url.rb +2 -5
  66. data/lib/web_pipe/pipe.rb +229 -0
  67. data/lib/web_pipe/plug.rb +31 -65
  68. data/lib/web_pipe/plugs/config.rb +0 -2
  69. data/lib/web_pipe/plugs/content_type.rb +0 -2
  70. data/lib/web_pipe/rack_support/app_with_middlewares.rb +3 -26
  71. data/lib/web_pipe/rack_support/middleware.rb +2 -2
  72. data/lib/web_pipe/rack_support/middleware_specification.rb +19 -48
  73. data/lib/web_pipe/test_support.rb +28 -0
  74. data/lib/web_pipe/types.rb +1 -3
  75. data/lib/web_pipe/version.rb +1 -1
  76. data/lib/web_pipe.rb +77 -17
  77. data/web_pipe.gemspec +1 -2
  78. metadata +16 -9
  79. data/docs/recipes/dry_rb_integration.md +0 -18
  80. data/lib/web_pipe/dsl/dsl_context.rb +0 -85
  81. data/lib/web_pipe/dsl/instance_methods.rb +0 -114
  82. data/lib/web_pipe/extensions/dry_view/dry_view.rb +0 -158
data/lib/web_pipe.rb CHANGED
@@ -1,23 +1,83 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'dry/core/extensions'
3
4
  require 'web_pipe/dsl/builder'
4
5
 
5
- # See [the
6
- # README](https://github.com/waiting-for-dev/web_pipe/blob/master/README.md)
7
- # for a general overview of this library.
6
+ # Entry-point for the DSL layer.
7
+ #
8
+ # Including this module into your class adds to it a DSL layer which makes it
9
+ # convenient to interact with an instance of {WebPipe::Pipe} transparently. It
10
+ # means that the DSL is actually an optional layer, and you can achieve
11
+ # everything by using {WebPipe::Pipe} instances.
12
+ #
13
+ # Your class gets access to {WebPipe::DSL::ClassContext::DSL_METHODS} at the
14
+ # class level, while {WebPipe::DSL::InstanceContext::PIPE_METHODS} are available
15
+ # for every instance of it. Both groups of methods are delegating to
16
+ # {WebPipe::Pipe}, so you can look there for documentation.
17
+ #
18
+ # @example
19
+ # class HelloWorld
20
+ # include WebPipe
21
+ #
22
+ # use :runtime, Rack::Runtime
23
+ #
24
+ # plug :content_type do |conn|
25
+ # conn.add_response_header('Content-Type', 'plain/text')
26
+ # end
27
+ #
28
+ # plug :render do |conn|
29
+ # conn.set_response_body('Hello, World!')
30
+ # end
31
+ # end
32
+ #
33
+ # The instance of your class is itself the final rack application. When you
34
+ # initialize it, you have the chance to inject different plugs or middlewares
35
+ # from those defined at the class level.
36
+ #
37
+ # @example
38
+ # HelloWorld.new(
39
+ # middlewares: {
40
+ # runtime: [Class.new do
41
+ # def initialize(app)
42
+ # @app = app
43
+ # end
44
+ #
45
+ # def call(env)
46
+ # status, headers, body = @app.call(env)
47
+ # [status, headers.merge('Injected' => '1'), body]
48
+ # end
49
+ # end]
50
+ # },
51
+ # plugs: {
52
+ # render: ->(conn) { conn.set_response_body('Injected!') }
53
+ # }
54
+ # )
8
55
  module WebPipe
9
56
  extend Dry::Core::Extensions
10
57
 
11
- # Including just delegates to an instance of `Builder`, so
12
- # `Builder#included` is finally called.
58
+ # Called via {Module#include}, makes available web_pipe's DSL.
59
+ #
60
+ # Includes an instance of `Builder`. That means that `Builder#included` is
61
+ # eventually called.
13
62
  def self.included(klass)
14
63
  klass.include(call)
15
64
  end
16
65
 
66
+ # Chained to {Module#include} to make the DSL available and provide options.
67
+ #
68
+ # @param container [#[]] Container from where resolve operations. See
69
+ # {WebPipe::Plug}.
70
+ #
71
+ # @example
72
+ # include WebPipe.call(container: Container)
17
73
  def self.call(**opts)
18
74
  DSL::Builder.new(**opts)
19
75
  end
20
76
 
77
+ register_extension :container do
78
+ require 'web_pipe/extensions/container/container'
79
+ end
80
+
21
81
  register_extension :cookies do
22
82
  require 'web_pipe/extensions/cookies/cookies'
23
83
  end
@@ -27,24 +87,16 @@ module WebPipe
27
87
  require 'web_pipe/extensions/dry_schema/plugs/sanitize_params'
28
88
  end
29
89
 
30
- register_extension :dry_view do
31
- require 'web_pipe/extensions/dry_view/dry_view'
32
- end
33
-
34
- register_extension :container do
35
- require 'web_pipe/extensions/container/container'
36
- end
37
-
38
90
  register_extension :flash do
39
91
  require 'web_pipe/extensions/flash/flash'
40
92
  end
41
93
 
42
- register_extension :router_params do
43
- require 'web_pipe/extensions/router_params/router_params'
94
+ register_extension :hanami_view do
95
+ require 'web_pipe/extensions/hanami_view/hanami_view'
44
96
  end
45
97
 
46
- register_extension :redirect do
47
- require 'web_pipe/extensions/redirect/redirect'
98
+ register_extension :not_found do
99
+ require 'web_pipe/extensions/not_found/not_found'
48
100
  end
49
101
 
50
102
  register_extension :params do
@@ -55,6 +107,14 @@ module WebPipe
55
107
  require 'web_pipe/extensions/rails/rails'
56
108
  end
57
109
 
110
+ register_extension :redirect do
111
+ require 'web_pipe/extensions/redirect/redirect'
112
+ end
113
+
114
+ register_extension :router_params do
115
+ require 'web_pipe/extensions/router_params/router_params'
116
+ end
117
+
58
118
  register_extension :session do
59
119
  require 'web_pipe/extensions/session/session'
60
120
  end
data/web_pipe.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = 'Rack application builder through a pipe of operations on an immutable struct.'
14
14
  spec.homepage = 'https://github.com/waiting-for-dev/web_pipe'
15
+ spec.licenses = ['MIT']
15
16
 
16
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
18
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -43,8 +44,6 @@ Gem::Specification.new do |spec|
43
44
  spec.add_development_dependency 'bundler'
44
45
  spec.add_development_dependency 'dry-schema', '~> 1.0'
45
46
  spec.add_development_dependency 'dry-transformer', '~> 0.1'
46
- # TODO: Readd when dry-rb 0.8 is released (ruby 3.0 support)
47
- # spec.add_development_dependency 'dry-view', '~> 0.8'
48
47
  spec.add_development_dependency 'pry-byebug'
49
48
  spec.add_development_dependency 'rack-flash3', '~> 1.0'
50
49
  spec.add_development_dependency 'rack-test', '~> 1.1'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: web_pipe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Busqué
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-15 00:00:00.000000000 Z
11
+ date: 2021-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-monads
@@ -277,8 +277,9 @@ files:
277
277
  - docs/extensions/container.md
278
278
  - docs/extensions/cookies.md
279
279
  - docs/extensions/dry_schema.md
280
- - docs/extensions/dry_view.md
281
280
  - docs/extensions/flash.md
281
+ - docs/extensions/hanami_view.md
282
+ - docs/extensions/not_found.md
282
283
  - docs/extensions/params.md
283
284
  - docs/extensions/rails.md
284
285
  - docs/extensions/redirect.md
@@ -289,16 +290,19 @@ files:
289
290
  - docs/plugging_operations.md
290
291
  - docs/plugging_operations/composing_operations.md
291
292
  - docs/plugging_operations/injecting_operations.md
293
+ - docs/plugging_operations/inspecting_operations.md
292
294
  - docs/plugging_operations/resolving_operations.md
293
295
  - docs/plugs.md
294
296
  - docs/plugs/config.md
295
297
  - docs/plugs/content_type.md
296
- - docs/recipes/dry_rb_integration.md
298
+ - docs/recipes/hanami_2_and_dry_rb_integration.md
297
299
  - docs/recipes/hanami_router_integration.md
298
300
  - docs/recipes/using_all_restful_methods.md
301
+ - docs/testing.md
299
302
  - docs/using_rack_middlewares.md
300
303
  - docs/using_rack_middlewares/composing_middlewares.md
301
304
  - docs/using_rack_middlewares/injecting_middlewares.md
305
+ - docs/using_rack_middlewares/inspecting_middlewares.md
302
306
  - lib/web_pipe.rb
303
307
  - lib/web_pipe/app.rb
304
308
  - lib/web_pipe/conn.rb
@@ -309,14 +313,14 @@ files:
309
313
  - lib/web_pipe/conn_support/types.rb
310
314
  - lib/web_pipe/dsl/builder.rb
311
315
  - lib/web_pipe/dsl/class_context.rb
312
- - lib/web_pipe/dsl/dsl_context.rb
313
- - lib/web_pipe/dsl/instance_methods.rb
316
+ - lib/web_pipe/dsl/instance_context.rb
314
317
  - lib/web_pipe/extensions/container/container.rb
315
318
  - lib/web_pipe/extensions/cookies/cookies.rb
316
319
  - lib/web_pipe/extensions/dry_schema/dry_schema.rb
317
320
  - lib/web_pipe/extensions/dry_schema/plugs/sanitize_params.rb
318
- - lib/web_pipe/extensions/dry_view/dry_view.rb
319
321
  - lib/web_pipe/extensions/flash/flash.rb
322
+ - lib/web_pipe/extensions/hanami_view/hanami_view.rb
323
+ - lib/web_pipe/extensions/not_found/not_found.rb
320
324
  - lib/web_pipe/extensions/params/params.rb
321
325
  - lib/web_pipe/extensions/params/params/transf.rb
322
326
  - lib/web_pipe/extensions/rails/rails.rb
@@ -324,6 +328,7 @@ files:
324
328
  - lib/web_pipe/extensions/router_params/router_params.rb
325
329
  - lib/web_pipe/extensions/session/session.rb
326
330
  - lib/web_pipe/extensions/url/url.rb
331
+ - lib/web_pipe/pipe.rb
327
332
  - lib/web_pipe/plug.rb
328
333
  - lib/web_pipe/plugs.rb
329
334
  - lib/web_pipe/plugs/config.rb
@@ -331,11 +336,13 @@ files:
331
336
  - lib/web_pipe/rack_support/app_with_middlewares.rb
332
337
  - lib/web_pipe/rack_support/middleware.rb
333
338
  - lib/web_pipe/rack_support/middleware_specification.rb
339
+ - lib/web_pipe/test_support.rb
334
340
  - lib/web_pipe/types.rb
335
341
  - lib/web_pipe/version.rb
336
342
  - web_pipe.gemspec
337
343
  homepage: https://github.com/waiting-for-dev/web_pipe
338
- licenses: []
344
+ licenses:
345
+ - MIT
339
346
  metadata:
340
347
  allowed_push_host: https://rubygems.org
341
348
  homepage_uri: https://github.com/waiting-for-dev/web_pipe
@@ -356,7 +363,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
356
363
  - !ruby/object:Gem::Version
357
364
  version: '0'
358
365
  requirements: []
359
- rubygems_version: 3.2.3
366
+ rubygems_version: 3.1.2
360
367
  signing_key:
361
368
  specification_version: 4
362
369
  summary: Rack application builder through a pipe of operations on an immutable struct.
@@ -1,18 +0,0 @@
1
- # dry-rb integration
2
-
3
- `web_pipe` has been designed to integrate smoothly with
4
- [dry-rb](https://dry-rb.org/) ecosystem. It shares same design
5
- principles and it ships with some extensions which even make this
6
- integration tighter (like
7
- [`:dry-view`](../extensions/dry_view.md) or
8
- [`:dry-schema`](../extensions/dry_schema.md) extensions).
9
-
10
- If you want to use `web_pipe` with the rest of dry-rb libraries,
11
- your best bet is to use
12
- [`dry-web-web_pipe`](https://github.com/waiting-for-dev/dry-web-web_pipe)
13
- skeleton generator. It is a fork of
14
- [`dry-web-roda`](https://github.com/dry-rb/dry-web-roda) with
15
- `roda` dependency switched to a combination of `web_pipe` and
16
- [`hanami-router`](https://github.com/hanami/router).
17
-
18
- Look at `dry-web-web_pipe` README for more details.
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'web_pipe'
4
- require 'web_pipe/types'
5
- require 'web_pipe/plug'
6
- require 'web_pipe/rack_support/middleware_specification'
7
-
8
- module WebPipe
9
- module DSL
10
- # Defines the DSL for the pipe class and keeps it state.
11
- #
12
- # This allows adding rack middlewares and plugs at the class
13
- # definition level.
14
- #
15
- # @api private
16
- class DSLContext
17
- # @!attribute middleware_specifications
18
- # @return [Array<RackSupport::MiddlewareSpecifications>]
19
- attr_reader :middleware_specifications
20
-
21
- # @!attribute plugs
22
- # @return [Array<Plug>]
23
- attr_reader :plugs
24
-
25
- def initialize(middleware_specifications, plugs)
26
- @middleware_specifications = Types.Array(
27
- RackSupport::MiddlewareSpecification
28
- )[middleware_specifications]
29
- @plugs = Types.Array(Plug::Instance)[plugs]
30
- end
31
-
32
- # Creates and add rack middleware specifications to the stack.
33
- #
34
- # The spec can be given in two forms:
35
- #
36
- # - As one or two arguments, first one being a
37
- # rack middleware class and second one optionally its
38
- # initialization options.
39
- # - As a {WebPipe} class instance, in which case all its rack
40
- # middlewares will be considered.
41
- #
42
- # @param name [RackSupport::MiddlewareSpecification::Name[]]
43
- # @param spec [RackSupport::MiddlewareSpecification::Spec[]]
44
- #
45
- # @return [Array<RackSupport::Middleware>]
46
- def use(name, *spec)
47
- middleware_specifications << RackSupport::MiddlewareSpecification.new(name: name, spec: spec)
48
- end
49
-
50
- # Creates and adds a plug to the stack.
51
- #
52
- # The spec can be given as a {Plug::Spec}, as a block (which
53
- # is captured into a {Proc}, one of the options for a
54
- # {Plug::Spec} or as a {WebPipe} (in which case all its plugs
55
- # will be composed).
56
- #
57
- # @param name [Plug::Name[]]
58
- # @param spec [Plug::Spec[], WebPipe]
59
- # @param block_spec [Proc]
60
- #
61
- # @return [Array<Plug>]
62
- def plug(name, spec = nil, &block_spec)
63
- plug_spec = if spec.is_a?(WebPipe)
64
- spec.to_proc
65
- elsif spec
66
- spec
67
- else
68
- block_spec
69
- end
70
-
71
- plugs << Plug.new(name: name, spec: plug_spec)
72
- end
73
-
74
- # Adds middlewares and plugs from a WebPipe to respective
75
- # stacks.
76
- #
77
- # @param name [Plug::Name[], Middleware::Name[]]
78
- # @param spec [WebPipe]
79
- def compose(name, spec)
80
- use(name, spec)
81
- plug(name, spec)
82
- end
83
- end
84
- end
85
- end
@@ -1,114 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'web_pipe/types'
4
- require 'web_pipe/conn'
5
- require 'web_pipe/app'
6
- require 'web_pipe/plug'
7
- require 'web_pipe/rack_support/app_with_middlewares'
8
- require 'web_pipe/rack_support/middleware_specification'
9
- require 'web_pipe/conn_support/composition'
10
-
11
- module WebPipe
12
- module DSL
13
- # Instance methods for the pipe.
14
- #
15
- # It is from here that you get the rack application you can route
16
- # to. The initialization phase gives you the chance to inject any
17
- # of the plugs or middlewares, while the instance you get has the
18
- # `#call` method expected by rack.
19
- #
20
- # The pipe state can be accessed through the pipe class, which
21
- # has been configured through {ClassContext}.
22
- #
23
- # @api private
24
- module InstanceMethods
25
- # No injections at all.
26
- EMPTY_INJECTIONS = {
27
- plugs: Types::EMPTY_HASH,
28
- middlewares: Types::EMPTY_HASH
29
- }.freeze
30
-
31
- # Type for how plugs and middlewares should be injected.
32
- Injections = Types::Strict::Hash.schema(
33
- plugs: Plug::Injections,
34
- middlewares: RackSupport::MiddlewareSpecification::Injections
35
- )
36
-
37
- # @!attribute [r] injections [Injections[]]
38
- # Injected plugs and middlewares that allow overriding what
39
- # has been configured.
40
- attr_reader :injections
41
-
42
- # @return [RackSupport::AppWithMiddlewares[]]
43
- attr_reader :rack_app
44
-
45
- # @return [ConnSupport::Composition::Operation[]]
46
- attr_reader :operations
47
-
48
- # @return [Array<RackSupport::Middlewares>]
49
- attr_reader :middlewares
50
-
51
- # rubocop:disable Metrics/AbcSize
52
- def initialize(injects = EMPTY_INJECTIONS)
53
- @injections = Injections[injects]
54
- container = self.class.container
55
- @middlewares = RackSupport::MiddlewareSpecification.inject_and_resolve(
56
- self.class.middleware_specifications, injections[:middlewares]
57
- )
58
- @operations = Plug.inject_and_resolve(
59
- self.class.plugs, injections[:plugs], container, self
60
- )
61
- app = App.new(operations)
62
- @rack_app = RackSupport::AppWithMiddlewares.new(middlewares, app)
63
- end
64
- # rubocop:enable Metrics/AbcSize
65
-
66
- # Expected interface for rack.
67
- #
68
- # @param env [Hash] Rack env
69
- #
70
- # @return [Array] Rack response
71
- def call(env)
72
- rack_app.call(env)
73
- end
74
-
75
- # Proc for the composition of all operations.
76
- #
77
- # This can be used to plug a {WebPipe} itself as an operation.
78
- #
79
- # @example
80
- # class HtmlApp
81
- # include WebPipe
82
- #
83
- # plug :html
84
- #
85
- # private
86
- #
87
- # def html(conn)
88
- # conn.add_response_header('Content-Type', 'text/html')
89
- # end
90
- # end
91
- #
92
- # class App
93
- # include WebPipe
94
- #
95
- # plug :html, &HtmlApp.new
96
- # plug :body
97
- #
98
- # private
99
- #
100
- # def body(conn)
101
- # conn.set_response_body('Hello, world!')
102
- # end
103
- # end
104
- #
105
- # @see ConnSupport::Composition
106
- def to_proc
107
- ConnSupport::Composition
108
- .new(operations)
109
- .method(:call)
110
- .to_proc
111
- end
112
- end
113
- end
114
- end
@@ -1,158 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'web_pipe/types'
4
- require 'web_pipe/conn'
5
- require 'dry/view'
6
-
7
- #:nodoc:
8
- module WebPipe
9
- # Integration with `dry-view` rendering system.
10
- #
11
- # This extensions adds a {#view} method to {WebPipe::Conn} which
12
- # sets the string output of a `dry-view` view as response body.
13
- #
14
- # @example
15
- # WebPipe.load_extensions(:dry_view)
16
- #
17
- # class SayHelloView < Dry::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(:dry_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 ({Dry::View::Context}) for the view can be set explicetly
54
- # through the `context:` argument, as in a standard call to
55
- # {Dry::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
- # {Dry::View::Context#with} to create the final context at the
61
- # moment {#view} is called.
62
- #
63
- # @example
64
- # class MyContext < Dry::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 < Dry::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://dry-rb.org/gems/dry-view/
100
- # @see WebPipe::Container
101
- module DryView
102
- # Where to find in {#config} request's view context generator.
103
- VIEW_CONTEXT_KEY = :view_context
104
-
105
- # Default request's view context
106
- DEFAULT_VIEW_CONTEXT = ->(_conn) { Types::EMPTY_HASH }
107
-
108
- # Sets string output of a view as response body.
109
- #
110
- # If the view is not a {Dry::View} instance, it is resolved from
111
- # the configured container.
112
- #
113
- # `kwargs` is used as the input for the view (the arguments that
114
- # {Dry::View#call} receives). If they doesn't contain an explicit
115
- # `context:` key, it can be added through the injection of the
116
- # result of a lambda present in context's `:view_context`.(see
117
- # {Dry::View::Context#with}).
118
- #
119
- # @param view_spec [Dry::View, Any]
120
- # @param kwargs [Hash] Arguments to pass along to `Dry::View#call`
121
- #
122
- # @return WebPipe::Conn
123
- def view(view_spec, **kwargs)
124
- view_instance = view_instance(view_spec)
125
- view_input = view_input(kwargs, view_instance)
126
-
127
- set_response_body(
128
- view_instance.call(
129
- **view_input
130
- ).to_str
131
- )
132
- end
133
-
134
- private
135
-
136
- def view_instance(view_spec)
137
- return view_spec if view_spec.is_a?(Dry::View)
138
-
139
- fetch_config(:container)[view_spec]
140
- end
141
-
142
- def view_input(kwargs, view_instance)
143
- return kwargs if kwargs.key?(:context)
144
-
145
- context = view_instance
146
- .config
147
- .default_context
148
- .with(
149
- **fetch_config(
150
- VIEW_CONTEXT_KEY, DEFAULT_VIEW_CONTEXT
151
- ).call(self)
152
- )
153
- kwargs.merge(context: context)
154
- end
155
- end
156
-
157
- Conn.include(DryView)
158
- end