web_pipe 0.8.0 → 0.9.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +4 -2
  4. data/README.md +60 -310
  5. data/Rakefile +5 -3
  6. data/bin/console +4 -3
  7. data/docs/_config.yml +1 -0
  8. data/docs/building_a_rack_application.md +25 -0
  9. data/docs/composing_applications.md +43 -0
  10. data/docs/connection_struct/configuring_the_connection_struct.md +25 -0
  11. data/docs/connection_struct/halting_the_pipe.md +71 -0
  12. data/docs/connection_struct/sharing_data_downstream.md +46 -0
  13. data/docs/connection_struct.md +77 -0
  14. data/docs/design_model.md +44 -0
  15. data/docs/dsl_free_usage.md +26 -0
  16. data/docs/extensions/container.md +41 -0
  17. data/docs/extensions/cookies.md +47 -0
  18. data/docs/extensions/dry_schema.md +51 -0
  19. data/docs/extensions/dry_view.md +113 -0
  20. data/docs/extensions/flash.md +41 -0
  21. data/docs/extensions/params.md +117 -0
  22. data/docs/extensions/redirect.md +25 -0
  23. data/docs/extensions/router_params.md +40 -0
  24. data/docs/extensions/session.md +39 -0
  25. data/docs/extensions/url.md +11 -0
  26. data/docs/extensions.md +13 -0
  27. data/docs/introduction.md +73 -0
  28. data/docs/plugging_operations/composing_operations.md +36 -0
  29. data/docs/plugging_operations/injecting_operations.md +32 -0
  30. data/docs/plugging_operations/resolving_operations.md +71 -0
  31. data/docs/plugging_operations.md +21 -0
  32. data/docs/plugs/config.md +18 -0
  33. data/docs/plugs/content_type.md +16 -0
  34. data/docs/plugs.md +13 -0
  35. data/docs/recipes/dry_rb_integration.md +18 -0
  36. data/docs/recipes/hanami_router_integration.md +25 -0
  37. data/docs/recipes/using_all_restful_methods.md +25 -0
  38. data/docs/using_rack_middlewares/composing_middlewares.md +26 -0
  39. data/docs/using_rack_middlewares/injecting_middlewares.md +47 -0
  40. data/docs/using_rack_middlewares.md +22 -0
  41. data/lib/web_pipe/app.rb +4 -2
  42. data/lib/web_pipe/conn.rb +5 -3
  43. data/lib/web_pipe/conn_support/builder.rb +2 -0
  44. data/lib/web_pipe/conn_support/composition.rb +9 -7
  45. data/lib/web_pipe/conn_support/errors.rb +3 -1
  46. data/lib/web_pipe/conn_support/headers.rb +12 -10
  47. data/lib/web_pipe/conn_support/types.rb +11 -9
  48. data/lib/web_pipe/dsl/builder.rb +5 -3
  49. data/lib/web_pipe/dsl/class_context.rb +5 -3
  50. data/lib/web_pipe/dsl/dsl_context.rb +7 -5
  51. data/lib/web_pipe/dsl/instance_methods.rb +7 -5
  52. data/lib/web_pipe/extensions/container/container.rb +2 -0
  53. data/lib/web_pipe/extensions/cookies/cookies.rb +4 -3
  54. data/lib/web_pipe/extensions/dry_schema/dry_schema.rb +2 -0
  55. data/lib/web_pipe/extensions/dry_schema/plugs/sanitize_params.rb +4 -2
  56. data/lib/web_pipe/extensions/dry_view/dry_view.rb +13 -9
  57. data/lib/web_pipe/extensions/flash/flash.rb +7 -7
  58. data/lib/web_pipe/extensions/params/params/transf.rb +3 -1
  59. data/lib/web_pipe/extensions/params/params.rb +7 -5
  60. data/lib/web_pipe/extensions/redirect/redirect.rb +8 -6
  61. data/lib/web_pipe/extensions/router_params/router_params.rb +4 -2
  62. data/lib/web_pipe/extensions/session/session.rb +6 -4
  63. data/lib/web_pipe/extensions/url/url.rb +3 -1
  64. data/lib/web_pipe/plug.rb +7 -5
  65. data/lib/web_pipe/plugs.rb +7 -1
  66. data/lib/web_pipe/rack_support/app_with_middlewares.rb +3 -1
  67. data/lib/web_pipe/rack_support/middleware.rb +2 -0
  68. data/lib/web_pipe/rack_support/middleware_specification.rb +5 -3
  69. data/lib/web_pipe/types.rb +3 -1
  70. data/lib/web_pipe/version.rb +3 -1
  71. data/lib/web_pipe.rb +5 -3
  72. data/web_pipe.gemspec +34 -34
  73. metadata +82 -48
@@ -0,0 +1,77 @@
1
+ # Connection struct
2
+
3
+ First operation you plug in a `web_pipe` application receives an instance of
4
+ `WebPipe::Conn` which has been automatically created.
5
+
6
+ This is just a struct data type which contains all the information from current
7
+ web request. In this regard, you can think of it as a structured rack's env
8
+ hash.
9
+
10
+ Request related attributes of this struct are:
11
+
12
+ - `#scheme`: `:http` or `:https`.
13
+ - `#request_method`: `:get`, `:post`...
14
+ - `#host`: e.g. `'www.example.org'`.
15
+ - `#ip`: e.g. `'192.168.1.1'`.
16
+ - `#port`: e.g. `80` or `443`.
17
+ - `#script_name`: e.g. `'index.rb'`.
18
+ - `#path_info`: e.g. `'/foor/bar'`.
19
+ - `#query_string`: e.g. `'foo=bar&bar=foo'`
20
+ - `#request_body`: e.g. `'{ id: 1 }'`
21
+ - `#request_headers`: e.g. `{ 'Accept-Charset' => 'utf8' }`
22
+ - `#env`: Rack's env hash.
23
+ - `#request`: Rack::Request instance.
24
+
25
+ Your operations must return another (or same) instance of the struct, which
26
+ will be consumed by next operation downstream. The struct contains methods to
27
+ add response data to it:
28
+
29
+ - `#set_status(code)`: makes it accessible in `#status` attribute.
30
+ - `#set_response_body(body)`: makes it accessible in `#response_body`
31
+ attribute.
32
+ - `#set_response_headers(headers)`: makes them accessible in
33
+ `#response_headers` attribute. Besides, there are also
34
+ `#add_response_header(key, value)` and `#delete_response_header(key)`
35
+ methods.
36
+
37
+ Response in last struct returned in the pipe will be what is sent to client.
38
+
39
+ Every attribute and method is [fully
40
+ documented](https://www.rubydoc.info/github/waiting-for-dev/web_pipe/master/WebPipe/Conn)
41
+ in code documentation.
42
+
43
+ Here we have a contrived web application which just returns as response body
44
+ the request body it has received:
45
+
46
+ ```ruby
47
+ # config.ru
48
+ require 'web_pipe'
49
+
50
+ class DummyApp
51
+ include WebPipe
52
+
53
+ plug :build_response
54
+
55
+ private
56
+
57
+ def build_response(conn)
58
+ conn.
59
+ set_status(200).
60
+ add_response_header('Content-Type', 'text/html').
61
+ set_response_body(
62
+ "<p>#{conn.request_body}</p>"
63
+ )
64
+ end
65
+ end
66
+
67
+ run DummyApp.new
68
+ ```
69
+
70
+ As you can see, default available features are the very minimal to read from a
71
+ request and to write a response. However, you can pick from several
72
+ (extensions)[/docs/extensions.md] which will make your life much easier.
73
+
74
+ Immutability is a core design principle in `web_pipe`. All methods in
75
+ `WebPipe::Conn` which are used to add data to it (both in core behaviour and
76
+ extensions) return a fresh new instance. It also makes possible chaining
77
+ methods in a very readable way.
@@ -0,0 +1,44 @@
1
+ # Design model
2
+
3
+ If you are familiar with rack you know that it models a two-way pipe. In it,
4
+ each middleware has the ability to:
5
+
6
+ - During the outbound trip modifying the request as it heads to the actual
7
+ application.
8
+
9
+ - During the return trip modifying the response as it gets back from the
10
+ application.
11
+
12
+ ```
13
+
14
+ ---------------------> request ----------------------->
15
+
16
+ Middleware 1 Middleware 2 Application
17
+
18
+ <--------------------- response <-----------------------
19
+
20
+
21
+ ```
22
+
23
+ `web_pipe` follows a simpler but equally powerful model: a one-way pipe which
24
+ is abstracted on top of rack. A struct that contains data from a web request is
25
+ piped trough a stack of operations (functions). Each operation takes as
26
+ argument an instance of the struct and returns also an instance of it.
27
+ Response data can be added to the struct at any moment in the pipe.
28
+
29
+ ```
30
+
31
+ Operation 1 Operation 2 Operation 3
32
+
33
+ --------------------- request/response ---------------->
34
+
35
+ ```
36
+
37
+ Additionally, any operation in the stack can halt the propagation of the pipe,
38
+ leaving downstream operations unexecuted. In this way, final response is the
39
+ one contained in the struct at the moment the pipe was halted, or last one if
40
+ the pipe wasn't halted.
41
+
42
+ As you may know, this is the same model used by Elixir's
43
+ [`plug`](https://hexdocs.pm/plug/readme.html), from which `web_pipe` takes
44
+ inspiration.
@@ -0,0 +1,26 @@
1
+ # DSL free usage
2
+
3
+ DSL's (like the one in `web_pipe` with methods like `plug` or
4
+ `use`) provide developers with a user friendly and ergonomic way to
5
+ use a library. However, they usually come at expenses of increasing complexity
6
+ in internal code (which sooner than later translates into some kind of issue).
7
+
8
+ `web_pipe` has tried to do an extra effort to minimize these problems. For this
9
+ reason, DSL in this library is just a layer on top of an independent core
10
+ functionality.
11
+
12
+ To use `web_pipe` without its DSL layer, you just need to initialize a
13
+ `WebPipe::App` instance with an array of all the operations that otherwise
14
+ you'd `plug`. That instance will be a rack application ready to be used.
15
+
16
+ ```ruby
17
+ # config.ru
18
+ require 'web_pipe/app'
19
+
20
+ op_1 = ->(conn) { conn.set_status(200) }
21
+ op_2 = ->(conn) { conn.set_response_body('Hello, World!') }
22
+
23
+ app = WebPipe::App.new([op_1, op_2])
24
+
25
+ run app
26
+ ```
@@ -0,0 +1,41 @@
1
+ # Container
2
+
3
+ `:container` is a very simple extension which allows you to configure a
4
+ dependency injection container to be accessible from a `WebPipe::Conn`
5
+ instance.
6
+
7
+ The container to use must be configured under `:configuration` key. It will be
8
+ accessible through the `#container` method.
9
+
10
+ You may be thinking why you should worry about configuring a container for a
11
+ connection instance when you already have access to the container configured
12
+ for an application (from where you can resolve plugged operations). The idea
13
+ here is decoupling operations from application DSL. If at anytime in the future
14
+ you decide to get rid off the DSL, the process will be straightforward if
15
+ operations are using the container configured in a connection instance.
16
+
17
+ ```ruby
18
+ require 'web_pipe'
19
+ require 'web_pipe/plugs/config'
20
+ require 'my_container'
21
+
22
+ WebPipe.load_extensions(:container)
23
+
24
+ class MyApp
25
+ include WebPipe.(container: MyContainer)
26
+
27
+ plug :config, WebPipe::Plugs::Config.(
28
+ container: MyContainer
29
+ )
30
+ plug :this, :this # Resolved thanks to the container in `include`
31
+ plug :that
32
+
33
+ private
34
+
35
+ def that(conn)
36
+ conn.set_response_body(
37
+ conn.container['do'].() # Resolved thanks to the container in `:config`
38
+ )
39
+ end
40
+ end
41
+ ```
@@ -0,0 +1,47 @@
1
+ # Cookies
2
+
3
+ Extension helping to deal with request and response cookies.
4
+
5
+ Remember, cookies are just the value of `Set-Cookie` header.
6
+
7
+ This extension adds following methods:
8
+
9
+ - `#request_cookies`: Returns request cookies
10
+
11
+ - `#set_cookie(key, value)` or `#set_cookie(key, value, options)`: Instructs
12
+ browser to add a new cookie with given key and value.
13
+
14
+ Some options can be given as keyword arguments (see [MDN reference on
15
+ cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) for an
16
+ explanation):
17
+
18
+ - `domain:` must be a string.
19
+ - `path:` must be a string.
20
+ - `max_age:` must be an integer with the number of seconds.
21
+ - `expires:` must be a `Time`.
22
+ - `secure:` must be `true` or `false`.
23
+ - `http_only:` must be `true` or `false`.
24
+ - `same_site:` must be one of the symbols `:none`, `:lax` or `:strict`.
25
+
26
+ - `#delete_cookie(key)` or `#delete_cookie(key, options)`: Instructs browser to
27
+ delete a previously sent cookie. Deleting a cookie just means setting again
28
+ the same key with an expiration time in the past.
29
+
30
+ It accepts `domain:` and `path:` options (see above for a description of
31
+ them).
32
+
33
+ Example:
34
+
35
+ ```ruby
36
+ require 'web_pipe'
37
+
38
+ WebPipe.load_extensions(:cookies)
39
+
40
+ class MyApp
41
+ include WebPipe
42
+
43
+ plug(:set_cookie) do |conn|
44
+ conn.set_cookie('foo', 'bar', secure: true, http_only: true)
45
+ end
46
+ end
47
+ ```
@@ -0,0 +1,51 @@
1
+ # Dry Schema
2
+
3
+ Extension providing integration for a common
4
+ [`dry-schema`](https://dry-rb.org/gems/dry-schema/) workflow to validate
5
+ parameters.
6
+
7
+ A plug `WebPipe::Plugs::SanitizeParams` is added so that you can use it in your
8
+ pipe of operations. It takes as arguments a `dry-schema` schema and a handler.
9
+ On success, it makes output available at `WebPipe::Conn#sanitized_params`. On
10
+ error, it calls given handler with the connection struct and validation result.
11
+
12
+ This extension automatically loads [`:params` extension](/docs/extensions/params.md),
13
+ as it takes `WebPipe::Conn#params` as input for the validation schema.
14
+
15
+ Instead of providing an error handler as the second argument for the plug, you
16
+ can configure it under `:param_sanitization_handler` key. In this way, it can
17
+ be reused through composition by others applications.
18
+
19
+ ```ruby
20
+ require 'db'
21
+ require 'dry/schema'
22
+ require 'web_pipe'
23
+ require 'web_pipe/plugs/config'
24
+
25
+ WebPipe.load_extensions(:dry_schema)
26
+
27
+ class MyApp
28
+ include WebPipe
29
+
30
+ Schema = Dry::Schema.Params do
31
+ required(:name).filled(:string)
32
+ end
33
+
34
+ plug :config, WebPipe::Plugs::Config.(
35
+ param_sanitization_handler: lambda do |conn, result|
36
+ conn.
37
+ set_status(500).
38
+ set_response_body('Error with request parameters').
39
+ halt
40
+ end
41
+ )
42
+
43
+ plug :sanitize_params, WebPipe::Plugs::SanitizeParams.(
44
+ Schema
45
+ )
46
+
47
+ plug(:this) do |conn|
48
+ DB.persist(:entity, conn.sanitized_params)
49
+ end
50
+ end
51
+ ```
@@ -0,0 +1,113 @@
1
+ # Dry View
2
+
3
+ This extensions integrates with
4
+ [dry-view](https://dry-rb.org/gems/dry-view/) rendering system to
5
+ set a dry-view output as response body.
6
+
7
+ `WebPipe::Conn#view` method is at the core of this extension. In its basic
8
+ behaviour, you provide to it a view instance you want to render and any
9
+ exposures or options it may need:
10
+
11
+ ```ruby
12
+ require 'web_pipe'
13
+ require 'dry/view'
14
+ require 'my_context'
15
+
16
+ WebPipe.load_extensions(:dry_view)
17
+
18
+ class SayHelloView < Dry::View
19
+ config.paths = [File.join(__dir__, '..', 'templates')]
20
+ config.template = 'say_hello'
21
+ config.default_context = MyContext
22
+
23
+ expose :name
24
+ end
25
+
26
+ class MyApp
27
+ include WebPipe
28
+
29
+ plug :render
30
+
31
+ private
32
+
33
+ def render(conn)
34
+ conn.view(SayHelloView.new, name: 'Joe')
35
+ end
36
+ end
37
+ ```
38
+
39
+ However, you can resolve a view from a container if you also use (`:container`
40
+ extension)[/docs/extensions/container.md]:
41
+
42
+ ```ruby
43
+ require 'dry_view'
44
+ require 'my_container'
45
+ require 'web_pipe'
46
+ require 'web_pipe/plugs/config'
47
+
48
+ WebPipe.load_extensions(:dry_view, :container)
49
+
50
+ class MyApp
51
+ include WebPipe
52
+
53
+ plug :config, WebPipe::Plugs::Config.(
54
+ container: MyContainer
55
+ )
56
+ plug :render
57
+
58
+ def render(conn)
59
+ conn.view('views.say_hello', name: 'Joe')
60
+ end
61
+ end
62
+ ```
63
+
64
+ As in a standard call to `Dry::View#call`, you can override the context
65
+ (`Dry::View::Context`) to use through `context:` option. However, it is still
66
+ possible to leverage configured default context while being able to inject
67
+ request specific data to it.
68
+
69
+ For that to work, you have to specify required dependencies (in this case,
70
+ request specific data) to your dry-view's context. A very convenient way to do
71
+ that is with [`dry-auto_inject`](https://dry-rb.org/gems/dry-auto_inject):
72
+
73
+ ```ruby
74
+ require 'dry/view/context'
75
+ require 'my_import'
76
+
77
+ class MyContext < Dry::View::Context
78
+ include MyImport::Import[:current_path]
79
+
80
+ # Without `dry-auto_inject` you have to manually specify dependencies and
81
+ # override the initializer:
82
+ #
83
+ # attr_reader :current_path
84
+ #
85
+ # def initialize(current_path:, **options)
86
+ # @current_path = current_path
87
+ # super
88
+ # end
89
+ end
90
+ ```
91
+
92
+ Then, you have to configure a `:view_context` setting, which must be a lambda
93
+ accepting a `WebPipe::Conn` instance and returning a hash matching required
94
+ dependencies:
95
+
96
+ ```ruby
97
+ require 'web_pipe'
98
+ require 'web_pipe/plugs/config'
99
+
100
+ WebPipe.load_extensions(:url)
101
+
102
+ class MyApp
103
+ include WebPipe
104
+
105
+ plug :config, WebPipe::Plugs::Config.(
106
+ view_context: ->(conn) { { current_path: conn.full_path} }
107
+ )
108
+ plug(:render) do |conn|
109
+ conn.view(SayHelloView.new, name: 'Joe')
110
+ # `:current_path` will be provided to the context
111
+ end
112
+ end
113
+ ```
@@ -0,0 +1,41 @@
1
+ # Flash
2
+
3
+ This extension provides with typical flash messages functionality. Messages for
4
+ users are stored in session in order to be consumed by another request after a
5
+ redirect.
6
+
7
+ This extension depends on
8
+ [`Rack::Flash`](https://rubygems.org/gems/rack-flash3) (gem name is
9
+ `rack-flash3`) and `Rack::Session` (shipped with rack) middlewares.
10
+
11
+ `WebPipe::Conn#flash` contains the flash bag. In order to add a message to it,
12
+ you can use `#add_flash(key, value)` method.
13
+
14
+ There is also an `#add_flash_now(key, value)` method, which is used to add a
15
+ message to the bag with the intention for it to be consumed in the current
16
+ request. Be aware that it is in fact a coupling with the view layer.
17
+ Something that has to be consumed in the current request should be just data
18
+ given to the view layer, but it helps when it can treat both scenarios as flash
19
+ messages.
20
+
21
+ ```ruby
22
+ require 'web_pipe'
23
+ require 'rack/session/cookie'
24
+ require 'rack-flash'
25
+
26
+ WebPipe.load_extensions(:flash)
27
+
28
+ class MyApp
29
+ include WebPipe
30
+
31
+ use :session, Rack::Session::Cookie, secret: 'secret'
32
+ use :flash, Rack::Flash
33
+
34
+ plug :add_to_flash, ->(conn) { conn.add_flash(:notice, 'Hello world') }
35
+
36
+ # Usually you will end up making `conn.flash` available to your view
37
+ # system:
38
+ #
39
+ # <div class="notice"><%= flash[:notice] %></div>
40
+ end
41
+ ```
@@ -0,0 +1,117 @@
1
+ # Params
2
+
3
+ This extension adds a `WebPipe::Conn#params` method which returns
4
+ request parameters as a Hash where any number of transformations
5
+ can be configured.
6
+
7
+ When no transformations are configured, `#params` just returns GET and POST parameters as a hash:
8
+
9
+ ```ruby
10
+ # http://www.example.com?foo=bar
11
+ conn.params # => { 'foo' => 'bar' }
12
+ ```
13
+
14
+ You can configure a stack of transformations to be applied to the
15
+ parameter hash. For that, we lean on [`transproc`
16
+ gem](https://github.com/solnic/transproc) (you have to add it yourself to your
17
+ Gemfile). All hash transformations in `transproc` are available by default.
18
+
19
+ Transformations must be configured under `:param_transformations`
20
+ key:
21
+
22
+ ```ruby
23
+ require 'web_pipe'
24
+ require 'web_pipe/plugs/config'
25
+
26
+ WebPipe.load_extensions(:params)
27
+
28
+ class MyApp
29
+ incude WebPipe
30
+
31
+ plug :config, WebPipe::Plugs::Config.(
32
+ param_transformations: [:deep_symbolize_keys]
33
+ )
34
+
35
+ plug(:this) do |conn|
36
+ # http://www.example.com?foo=bar
37
+ conn.params => # => { foo: 'bar' }
38
+ # ...
39
+ end
40
+ end
41
+ ```
42
+
43
+ Extra needed arguments can be provided as an array:
44
+
45
+ ```ruby
46
+ # ...
47
+ plug :config, WebPipe::Plugs::Config.(
48
+ param_transformations: [
49
+ :deep_symbolize_keys, [:reject_keys, [:zoo]]
50
+ ]
51
+ )
52
+
53
+ plug(:this) do |conn|
54
+ # http://www.example.com?foo=bar&zoo=zoo
55
+ conn.params => # => { foo: 'bar' }
56
+ # ...
57
+ end
58
+ # ...
59
+ ```
60
+
61
+ Custom transformations can be registered in `WebPipe::Params::Transf` `transproc` register:
62
+
63
+ ```ruby
64
+ fake = ->(_params) { { fake: :params } }
65
+ WebPipe::Params::Transf.register(:fake, fake)
66
+
67
+ # ...
68
+ plug :config, WebPipe::Plugs::Config.(
69
+ param_transformations: [:fake]
70
+ )
71
+
72
+ plug(:this) do |conn|
73
+ # http://www.example.com?foo=bar
74
+ conn.params => # => { fake: :params }
75
+ # ...
76
+ end
77
+ # ...
78
+ ```
79
+
80
+ Your own transformation functions can depend on the `WebPipe::Conn`
81
+ instance at the moment of calling `#params`. Those functions must accept
82
+ the connection struct as its last argument:
83
+
84
+ ```ruby
85
+ add_name = ->(params, conn) { params.merge(name: conn.fetch(:name)) }
86
+ WebPipe::Params::Transf.register(:add_name, add_name)
87
+
88
+ # ...
89
+ plug :config, WebPipe::Plugs::Config.(
90
+ param_transformations: [:deep_symbolize_keys, :add_name]
91
+ )
92
+
93
+ plug(:add_name) do |conn|
94
+ conn.add(:name, 'Alice')
95
+ end
96
+
97
+ plug(:this) do |conn|
98
+ # http://www.example.com?foo=bar
99
+ conn.params => # => { foo: :bar, name: 'Alice' }
100
+ # ...
101
+ end
102
+ # ...
103
+ ```
104
+ Finally, you can override configured transformations injecting another set at the moment of calling `#params`:
105
+
106
+ ```ruby
107
+ # ...
108
+ plug :config, WebPipe::Plugs::Config.(
109
+ param_transformations: [:deep_symbolize_keys]
110
+ )
111
+
112
+ plug(:this) do |conn|
113
+ # http://www.example.com?foo=bar&zoo=zoo
114
+ conn.params([:reject_keys, ['zoo']]) => # => { 'foo' => 'zoo' }
115
+ # ...
116
+ end
117
+ # ...
@@ -0,0 +1,25 @@
1
+ # Redirect
2
+
3
+ This extension helps with creating a redirect response.
4
+
5
+ Redirect responses consist of two pieces:
6
+
7
+ - `Location` response header with the URL to which browsers should redirect.
8
+ - A 3xx status code.
9
+
10
+ A `#redirect(location, code)` method is added to `WebPipe::Conn` which takes
11
+ care of both steps. `code` argument is optional, defaulting to `302`.
12
+
13
+ ```ruby
14
+ require 'web_pipe'
15
+
16
+ WebPipe.load_extensions(:redirect)
17
+
18
+ class MyApp
19
+ include WebPipe
20
+
21
+ plug(:redirect) do |conn|
22
+ conn.redirect('/')
23
+ end
24
+ end
25
+ ```
@@ -0,0 +1,40 @@
1
+ # Router params
2
+
3
+ This extension can be used in order to merge placeholder parameters
4
+ that usually routers support (like `get /users/:id`) to the parameters hash
5
+ added through [`:params` extension](/docs/extensions/params.md) (which is
6
+ automatically loaded).
7
+
8
+ What this extension does is adding a transformation function to the registry
9
+ with name `:router_params`. Internally, it merges what is present in rack env's
10
+ `router.params` key.
11
+
12
+ It automatically integrates with
13
+ [`hanami-router`](https://github.com/hanami/router).
14
+
15
+ Don't forget that you have to add yourself the `:router_params`
16
+ transformation to the stack.
17
+
18
+ ```ruby
19
+ require 'web_pipe'
20
+ require 'web_pipe/plugs/config'
21
+
22
+ WebPipe.load_extensions(:router_params)
23
+
24
+ class MyApp
25
+ include WebPipe
26
+
27
+ plug :config, WebPipe::Plugs::Config.(
28
+ param_transformations: [:router_params, :deep_symbolize_keys]
29
+ )
30
+ plug :this
31
+
32
+ private
33
+
34
+ def this(conn)
35
+ # http://example.com/users/1/edit
36
+ conn.params # => { id: 1 }
37
+ # ...
38
+ end
39
+ end
40
+ ```
@@ -0,0 +1,39 @@
1
+ # Session
2
+
3
+ Wrapper around `Rack::Session` middleware to help working with
4
+ sessions in your plugged operations.
5
+
6
+ It depends on `Rack::Session` middleware, which is shipped by rack.
7
+
8
+ It adds following methods to `WebPipe::Conn`:
9
+
10
+ - `#fetch_session(key)`, `#fetch_session(key, default)` or
11
+ `#fetch_session(key) { default }`. Returns what is stored under
12
+ given session key. A default value can be given as a second
13
+ argument or a block.
14
+ - `#add_session(key, value)`. Adds given key/value pair to the
15
+ session.
16
+ - `#delete_session(key)`. Deletes given key from the session.
17
+ - `#clear_session`. Deletes everything from the session.
18
+
19
+ ```ruby
20
+ require 'web_pipe'
21
+ require 'rack/session'
22
+
23
+ WebPipe.load_extensions(:session)
24
+
25
+ class MyApp
26
+ include WebPipe
27
+
28
+ use Rack::Session::Cookie, secret: 'top_secret'
29
+
30
+ plug(:add_to_session) do |conn|
31
+ conn.add_session('foo', 'bar')
32
+ end
33
+ plug(:fetch_from_session) do |conn|
34
+ conn.add(
35
+ :foo, conn.fetch_session('foo')
36
+ )
37
+ end
38
+ end
39
+ ```
@@ -0,0 +1,11 @@
1
+ # URL
2
+
3
+ `:url` extension just adds a few methods which cook raw request information
4
+ about the URL into something more digestible.
5
+
6
+ Specifically, it adds:
7
+
8
+ - `#base_url`: Which is schema + host + port (unless it is the default for the scheme). I.e. `'https://example.org'` or `'http://example.org:8000'`.
9
+ - `#path`: Which is script name (if any) + path information. I.e. `'index.rb/users/1'` or `'users/1'`.
10
+ - `#full_path`: Which is path + query string (if any). I.e. `'users/1?view=table'`.
11
+ - `#url`: Which is base url + full path. I.e. `'http://example.org:8000/users/1?view=table'`.
@@ -0,0 +1,13 @@
1
+ # Extensions
2
+
3
+ `WebPipe::Conn` features are by default raw: the very minimal you
4
+ need to be able to build a web application. However, there are
5
+ several extensions to progressively add just the ingredients you
6
+ want to use.
7
+
8
+ In order to load the extensions, you have to call
9
+ `#load_extensions` method in `WebPipe`:
10
+
11
+ ```ruby
12
+ WebPipe.load_extensions(:params, :cookies)
13
+ ```