web_pipe 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ```