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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -2
- data/README.md +60 -310
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/docs/_config.yml +1 -0
- data/docs/building_a_rack_application.md +25 -0
- data/docs/composing_applications.md +43 -0
- data/docs/connection_struct/configuring_the_connection_struct.md +25 -0
- data/docs/connection_struct/halting_the_pipe.md +71 -0
- data/docs/connection_struct/sharing_data_downstream.md +46 -0
- data/docs/connection_struct.md +77 -0
- data/docs/design_model.md +44 -0
- data/docs/dsl_free_usage.md +26 -0
- data/docs/extensions/container.md +41 -0
- data/docs/extensions/cookies.md +47 -0
- data/docs/extensions/dry_schema.md +51 -0
- data/docs/extensions/dry_view.md +113 -0
- data/docs/extensions/flash.md +41 -0
- data/docs/extensions/params.md +117 -0
- data/docs/extensions/redirect.md +25 -0
- data/docs/extensions/router_params.md +40 -0
- data/docs/extensions/session.md +39 -0
- data/docs/extensions/url.md +11 -0
- data/docs/extensions.md +13 -0
- data/docs/introduction.md +73 -0
- data/docs/plugging_operations/composing_operations.md +36 -0
- data/docs/plugging_operations/injecting_operations.md +32 -0
- data/docs/plugging_operations/resolving_operations.md +71 -0
- data/docs/plugging_operations.md +21 -0
- data/docs/plugs/config.md +18 -0
- data/docs/plugs/content_type.md +16 -0
- data/docs/plugs.md +13 -0
- data/docs/recipes/dry_rb_integration.md +18 -0
- data/docs/recipes/hanami_router_integration.md +25 -0
- data/docs/recipes/using_all_restful_methods.md +25 -0
- data/docs/using_rack_middlewares/composing_middlewares.md +26 -0
- data/docs/using_rack_middlewares/injecting_middlewares.md +47 -0
- data/docs/using_rack_middlewares.md +22 -0
- data/lib/web_pipe/app.rb +4 -2
- data/lib/web_pipe/conn.rb +5 -3
- data/lib/web_pipe/conn_support/builder.rb +2 -0
- data/lib/web_pipe/conn_support/composition.rb +9 -7
- data/lib/web_pipe/conn_support/errors.rb +3 -1
- data/lib/web_pipe/conn_support/headers.rb +12 -10
- data/lib/web_pipe/conn_support/types.rb +11 -9
- data/lib/web_pipe/dsl/builder.rb +5 -3
- data/lib/web_pipe/dsl/class_context.rb +5 -3
- data/lib/web_pipe/dsl/dsl_context.rb +7 -5
- data/lib/web_pipe/dsl/instance_methods.rb +7 -5
- data/lib/web_pipe/extensions/container/container.rb +2 -0
- data/lib/web_pipe/extensions/cookies/cookies.rb +4 -3
- data/lib/web_pipe/extensions/dry_schema/dry_schema.rb +2 -0
- data/lib/web_pipe/extensions/dry_schema/plugs/sanitize_params.rb +4 -2
- data/lib/web_pipe/extensions/dry_view/dry_view.rb +13 -9
- data/lib/web_pipe/extensions/flash/flash.rb +7 -7
- data/lib/web_pipe/extensions/params/params/transf.rb +3 -1
- data/lib/web_pipe/extensions/params/params.rb +7 -5
- data/lib/web_pipe/extensions/redirect/redirect.rb +8 -6
- data/lib/web_pipe/extensions/router_params/router_params.rb +4 -2
- data/lib/web_pipe/extensions/session/session.rb +6 -4
- data/lib/web_pipe/extensions/url/url.rb +3 -1
- data/lib/web_pipe/plug.rb +7 -5
- data/lib/web_pipe/plugs.rb +7 -1
- data/lib/web_pipe/rack_support/app_with_middlewares.rb +3 -1
- data/lib/web_pipe/rack_support/middleware.rb +2 -0
- data/lib/web_pipe/rack_support/middleware_specification.rb +5 -3
- data/lib/web_pipe/types.rb +3 -1
- data/lib/web_pipe/version.rb +3 -1
- data/lib/web_pipe.rb +5 -3
- data/web_pipe.gemspec +34 -34
- metadata +82 -48
@@ -0,0 +1,73 @@
|
|
1
|
+
# Introduction
|
2
|
+
|
3
|
+
`web_pipe` is a rack application builder.
|
4
|
+
|
5
|
+
It means that with it and a rack router (like
|
6
|
+
[`hanami-router`](https://github.com/hanami/router),
|
7
|
+
[`http_router`](https://github.com/joshbuddy/http_router) or plain
|
8
|
+
[rack](https://github.com/rack/rack) routing methods you can build a complete
|
9
|
+
web application. However, the idea behind `web_pipe` is being a decoupled
|
10
|
+
component within a web framework. For this reason, it plays extremely well
|
11
|
+
with [dry-rb](https://dry-rb.org/) ecosystem. If it helps, you can think of it
|
12
|
+
as a decoupled web controller (as the C in MVC).
|
13
|
+
|
14
|
+
`web_pipe` applications are built as a [pipe of
|
15
|
+
operations](/docs/design_model.md) on an [immutable
|
16
|
+
struct](/docs/connection_struct.md). The struct is automatically created
|
17
|
+
with data from an HTTP request, and it contains methods to
|
18
|
+
incrementally add data to generate an HTTP response. The pipe can
|
19
|
+
be [halted](/docs/connection_struct/halting_the_pipe.md) at any moment,
|
20
|
+
taking away from all operations downstream any chance to modify the
|
21
|
+
response.
|
22
|
+
|
23
|
+
`web_pipe` has a modular design, with only the minimal functionalities needed
|
24
|
+
to build a web application enabled by default. However, it ships with several
|
25
|
+
[extensions](/docs/extensions.md) to make your life easier.
|
26
|
+
|
27
|
+
Following there is a simple example. It is a web application that will check
|
28
|
+
the value of a `user` parameter. When it is `Alice` or `Joe`, it will kindly
|
29
|
+
say hello. Otherwise, it will unauthorize:
|
30
|
+
|
31
|
+
> In order to try this example you can paste it to a file with name `config.ru`
|
32
|
+
and launch the rack command `rackup` within the same directory. The application
|
33
|
+
will be available in `http://localhost:9292`.
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'web_pipe'
|
37
|
+
|
38
|
+
WebPipe.load_extensions(:params)
|
39
|
+
|
40
|
+
class HelloApp
|
41
|
+
include WebPipe
|
42
|
+
|
43
|
+
AUTHORIZED_USERS = %w[Alice Joe]
|
44
|
+
|
45
|
+
plug :html
|
46
|
+
plug :authorize
|
47
|
+
plug :greet
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def html(conn)
|
52
|
+
conn.add_response_header('Content-Type', 'text/html')
|
53
|
+
end
|
54
|
+
|
55
|
+
def authorize(conn)
|
56
|
+
user = conn.params['user']
|
57
|
+
if AUTHORIZED_USERS.include?(user)
|
58
|
+
conn.add(:user, user)
|
59
|
+
else
|
60
|
+
conn.
|
61
|
+
set_status(401).
|
62
|
+
set_response_body('<h1>Not authorized</h1>').
|
63
|
+
halt
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def greet(conn)
|
68
|
+
conn.set_response_body("<h1>Hello #{conn.fetch(:user)}</h1>")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
run HelloApp.new
|
73
|
+
``
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Composing operations
|
2
|
+
|
3
|
+
As we already have said, operations are functions taking a connection struct
|
4
|
+
and returning a connection struct. As a result, a composition of operations is
|
5
|
+
an operation in itself (as it also takes a connection struct and returns a
|
6
|
+
connection struct).
|
7
|
+
|
8
|
+
This can be leveraged to plug a whole `web_pipe` application as an operation
|
9
|
+
to another application. Doing so, you are plugging an operation which is the
|
10
|
+
composition of all operations for given application.
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class HtmlApp
|
14
|
+
include WebPipe
|
15
|
+
|
16
|
+
plug :content_type
|
17
|
+
plug :default_status
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def content_type(conn)
|
22
|
+
conn.add_response_header('Content-Type' => 'text/html')
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_status(conn)
|
26
|
+
conn.set_status(404)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class MyApp
|
31
|
+
include WebPipe
|
32
|
+
|
33
|
+
plug :html, HtmlApp.new
|
34
|
+
# plug ...
|
35
|
+
end
|
36
|
+
```
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Injecting operations
|
2
|
+
|
3
|
+
Operations can be injected at the moment an application is initialized,
|
4
|
+
which allows you to override what the definition declares.
|
5
|
+
|
6
|
+
To this effect, you must use `plugs:` keyword argument. It must be a hash where
|
7
|
+
operations are matched by the name you gave them in its definition.
|
8
|
+
|
9
|
+
This is mainly useful for testing purposes, where you can switch a heavy
|
10
|
+
operation and use another lighter one.
|
11
|
+
|
12
|
+
In the following example, the response body of the application will be
|
13
|
+
`'Hello from injection'`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# config.ru
|
17
|
+
require 'web_pipe'
|
18
|
+
|
19
|
+
class MyApp
|
20
|
+
include WebPipe
|
21
|
+
|
22
|
+
plug(:hello) do |conn|
|
23
|
+
conn.set_response_body('Hello from definition')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
injection = lambda do |conn|
|
28
|
+
conn.set_response_body('Hello from injection')
|
29
|
+
end
|
30
|
+
|
31
|
+
run MyApp.new(plugs: { hello: injection })
|
32
|
+
```
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Resolving operations
|
2
|
+
|
3
|
+
There are several ways you can specify how an operation is resolved.
|
4
|
+
|
5
|
+
## Instance method
|
6
|
+
|
7
|
+
Operations can be plugged as methods (both public and private) in the
|
8
|
+
application class:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class MyApp
|
12
|
+
include WebPipe
|
13
|
+
|
14
|
+
plug :html
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def html(conn)
|
19
|
+
conn.add_response_header('Content-Type' => 'text/html')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
## `#call`
|
25
|
+
|
26
|
+
Operations can be plugged inline as anything responding to `#call`, like a
|
27
|
+
`Proc` or a `lambda`:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class MyApp
|
31
|
+
include WebPipe
|
32
|
+
|
33
|
+
plug :html, ->(conn) { conn.add_response_header('Content-Type' => 'text/html') }
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
## Block
|
38
|
+
|
39
|
+
In the same way that `#call`, operations can also be plugged inline as blocks:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class MyApp
|
43
|
+
include WebPipe
|
44
|
+
|
45
|
+
plug :html do |conn|
|
46
|
+
conn.add_response_header('Content-Type' => 'text/html')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
## Container
|
52
|
+
|
53
|
+
Operations can be resolved from a dependency injection container.
|
54
|
+
|
55
|
+
A container is anything that responds to `#[]` (accepting `Symbol` or `String`
|
56
|
+
as argument) in order to resolve a dependency. It can be configured at the
|
57
|
+
moment `WebPipe` module is included:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
MyContainer = Hash[
|
61
|
+
'plugs.html' => lambda do |conn|
|
62
|
+
conn.add_response_header('Content-Type' => 'text/html')
|
63
|
+
end
|
64
|
+
]
|
65
|
+
|
66
|
+
class MyApp
|
67
|
+
include WebPipe.(container: MyContainer)
|
68
|
+
|
69
|
+
plug :html, 'plugs.html'
|
70
|
+
end
|
71
|
+
```
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Plugging operations
|
2
|
+
|
3
|
+
You can plug operations to your application with the DSL method `plug`. The
|
4
|
+
first argument it always takes is a symbol with the name you want
|
5
|
+
to give to the operation (which is needed to allow
|
6
|
+
[injection](/docs/plugging_operations/injecting_operations.md) on
|
7
|
+
initialization).
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class MyApp
|
11
|
+
include WebPipe
|
12
|
+
|
13
|
+
plug :dummy_operation, ->(conn) { conn }
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
Remember, an operation is just a function (in ruby, anything responding to
|
18
|
+
`#call`) that takes a struct with connection information and returns another
|
19
|
+
instance of it. First operation in the stack receives a struct which has been
|
20
|
+
automatically created with the request data. From then on, any operation can
|
21
|
+
add to it response data.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Config
|
2
|
+
|
3
|
+
`Config` plug helps in the addition of configuration settings (`#config` hash
|
4
|
+
attribute) to an instance of `WebPipe::Conn`.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
require 'web_pipe'
|
8
|
+
require 'web_pipe/plugs/config'
|
9
|
+
|
10
|
+
class MyApp
|
11
|
+
include WebPipe
|
12
|
+
|
13
|
+
plug :config, WebPipe::Plugs::Config.(
|
14
|
+
key1: :value1,
|
15
|
+
key2: :value2
|
16
|
+
)
|
17
|
+
end
|
18
|
+
```
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# ContentType
|
2
|
+
|
3
|
+
`ContentType` plug is just a helper to set `Content-Type` response header.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
require 'web_pipe'
|
9
|
+
require 'web_pipe/plugs/content_type'
|
10
|
+
|
11
|
+
class MyApp
|
12
|
+
include WebPipe
|
13
|
+
|
14
|
+
plug :html, WebPipe::Plugs::ContentType.('text/html')
|
15
|
+
end
|
16
|
+
```
|
data/docs/plugs.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Plugs
|
2
|
+
|
3
|
+
Some group of operations can be generalized as following same pattern. For
|
4
|
+
example, an operation setting `Content-Type` header to `text/html` is very
|
5
|
+
similar to another one setting same header to `application/json`. We name plugs
|
6
|
+
to this level of abstraction on top of operations: plugs are operation
|
7
|
+
builders. In other words, they are higher order functions which return
|
8
|
+
functions.
|
9
|
+
|
10
|
+
Being just functions, we take as convention that plugs respond to `#call` in
|
11
|
+
order to create an operation.
|
12
|
+
|
13
|
+
This library ships with some useful plugs.
|
@@ -0,0 +1,18 @@
|
|
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`](/docs/extensions/dry_view.md) or
|
8
|
+
[`:dry-schema`](/docs/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.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# hanami-router integration
|
2
|
+
|
3
|
+
A `web_pipe` application instance is a rack application.
|
4
|
+
Consequently, you can mount it with `hanami-router`'s' `to:`
|
5
|
+
option.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# config.ru
|
9
|
+
require 'hanami/router'
|
10
|
+
require 'web_pipe'
|
11
|
+
|
12
|
+
class MyApp
|
13
|
+
include WebPipe
|
14
|
+
|
15
|
+
plug :this, ->(conn) { conn.set_response_body('This') }
|
16
|
+
end
|
17
|
+
|
18
|
+
router = Hanami::Router.new do
|
19
|
+
get 'my_app', to: MyApp.new
|
20
|
+
end
|
21
|
+
|
22
|
+
run router
|
23
|
+
```
|
24
|
+
|
25
|
+
In order to perform [string matching with variables](https://github.com/hanami/router#string-matching-with-variables) you just need to load [`:router_params` extension](/docs/extensions/router_params.md).
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Using all RESTful methods
|
2
|
+
|
3
|
+
As you probably know, a lot of browsers don't support some RESTful
|
4
|
+
methods like `PATCH` or `PUT`. [Rack's `MethodOverride`
|
5
|
+
middleware](https://github.com/rack/rack/blob/master/lib/rack/method_override.rb)
|
6
|
+
provides a workaround for this limitation, allowing to override
|
7
|
+
request method in rack's env if a magical `_method` parameter or
|
8
|
+
`HTTP_METHOD_OVERRIDE` request header is found.
|
9
|
+
|
10
|
+
You have to be aware that if you use this middleware within a
|
11
|
+
`web_pipe` application (through [`use` DSL
|
12
|
+
method](docs/using_rack_middlewares.md)) it will have no effect.
|
13
|
+
When your `web_pipe` application takes control of the request it
|
14
|
+
has already gone through the router, which is the one who should
|
15
|
+
read the request method set by rack.
|
16
|
+
|
17
|
+
The solution for this is very simple. Just use `MethodOverride` middleware before your router does its work. For example, in `config.ru`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# config.ru
|
21
|
+
|
22
|
+
use Rack::MethodOverride
|
23
|
+
|
24
|
+
# Load your router and map to web_pipe applications
|
25
|
+
```
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Composing middlewares
|
2
|
+
|
3
|
+
In a similar way that you compose plugged operations, you can also compose rack
|
4
|
+
middlewares from another application.
|
5
|
+
|
6
|
+
For that, you just need to `use` another application. When you do so, all the
|
7
|
+
middlewares for that application will be added to the stack in the same order
|
8
|
+
they had there.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class HtmlApp
|
12
|
+
include WebPipe
|
13
|
+
|
14
|
+
use :session, Rack::Session::Cookie, key: 'my_app.session', secret: 'long'
|
15
|
+
use :csrf, Rack::Csrf, raise: true
|
16
|
+
end
|
17
|
+
|
18
|
+
class MyApp
|
19
|
+
include WebPipe
|
20
|
+
|
21
|
+
use :html, HtmlApp.new
|
22
|
+
# use ...
|
23
|
+
|
24
|
+
# plug ...
|
25
|
+
end
|
26
|
+
```
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Injecting middlewares
|
2
|
+
|
3
|
+
Middlewares can be injected at the moment an application is initialized,
|
4
|
+
allowing you to override what you have defined in the DSL.
|
5
|
+
|
6
|
+
For that purpose, you have to use `middlewares:` keyword argument. It must be a
|
7
|
+
hash where middlewares are matched by the name you gave them in its definition.
|
8
|
+
|
9
|
+
A middleware must be specified as an `Array`. First item must be a rack
|
10
|
+
middleware class. The rest of arguments (if any) should be any option it may
|
11
|
+
need.
|
12
|
+
|
13
|
+
This is mainly useful for testing purposes, where you can switch a heavy
|
14
|
+
middleware and use a mocked one instead.
|
15
|
+
|
16
|
+
In the following example, rack session mechanism is being mocked:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
# config.ru
|
20
|
+
require 'web_pipe'
|
21
|
+
require 'rack/session/cookie'
|
22
|
+
|
23
|
+
class MyApp
|
24
|
+
include WebPipe
|
25
|
+
|
26
|
+
use :session, Rack::Session::Cookie, key: 'my_app.session', secret: 'long'
|
27
|
+
|
28
|
+
plug(:serialize_session) do |conn|
|
29
|
+
conn.set_response_body(conn.env['rack.session'].inspect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class MockedSession
|
34
|
+
attr_reader :app, :key
|
35
|
+
|
36
|
+
def initialize(app, key)
|
37
|
+
@app = app
|
38
|
+
@key = key
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(env)
|
42
|
+
env['rack.session'] = "Mocked for '#{key}' key"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
run MyApp.new(middlewares: { session: [MockedSession, 'my_app_mocked'] })
|
47
|
+
```
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Using rack middlewares
|
2
|
+
|
3
|
+
A one-way pipe like the one `web_pipe` implements can deal with any required
|
4
|
+
feature in a web application. However, usually it is convenient to be able to
|
5
|
+
use some well-known rack middleware so you don't have to reinvent the wheel.
|
6
|
+
Even if you can add them at the router layer, `web_pipe` allows you to
|
7
|
+
encapsulate them in your application definition.
|
8
|
+
|
9
|
+
In order to add rack middlewares to the stack, you have to use the DSL method
|
10
|
+
`use`. The first argument it takes is a `Symbol` with the name you want to
|
11
|
+
assign to it (which is needed to allow
|
12
|
+
[injection](/docs/using_rack_middlewares/injecting_middlewares.md) on
|
13
|
+
initialization). Then, it must follow the middleware class and any option it
|
14
|
+
may need:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class MyApp
|
18
|
+
include WebPipe
|
19
|
+
|
20
|
+
use :cookies, Rack::Session::Cookie, key: 'my_app.session', secret: 'long'
|
21
|
+
end
|
22
|
+
```
|
data/lib/web_pipe/app.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'web_pipe/types'
|
2
4
|
require 'web_pipe/conn'
|
3
5
|
require 'web_pipe/conn_support/builder'
|
@@ -55,7 +57,7 @@ module WebPipe
|
|
55
57
|
private
|
56
58
|
|
57
59
|
def conn_from_env(env)
|
58
|
-
ConnSupport::Builder.(env)
|
60
|
+
ConnSupport::Builder.call(env)
|
59
61
|
end
|
60
62
|
|
61
63
|
def apply_operations(conn)
|
@@ -66,4 +68,4 @@ module WebPipe
|
|
66
68
|
conn.rack_response
|
67
69
|
end
|
68
70
|
end
|
69
|
-
end
|
71
|
+
end
|
data/lib/web_pipe/conn.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/struct'
|
2
4
|
require 'web_pipe/types'
|
3
5
|
require 'web_pipe/conn_support/types'
|
@@ -315,7 +317,7 @@ module WebPipe
|
|
315
317
|
def fetch(key, default = Types::Undefined)
|
316
318
|
return bag.fetch(key, default) unless default == Types::Undefined
|
317
319
|
|
318
|
-
bag.fetch(key) { raise ConnSupport::KeyNotFoundInBagError
|
320
|
+
bag.fetch(key) { raise ConnSupport::KeyNotFoundInBagError, key }
|
319
321
|
end
|
320
322
|
|
321
323
|
# Writes an item to the {#bag}.
|
@@ -343,7 +345,7 @@ module WebPipe
|
|
343
345
|
def fetch_config(key, default = Types::Undefined)
|
344
346
|
return config.fetch(key, default) unless default == Types::Undefined
|
345
347
|
|
346
|
-
config.fetch(key) { raise ConnSupport::KeyNotFoundInConfigError
|
348
|
+
config.fetch(key) { raise ConnSupport::KeyNotFoundInConfigError, key }
|
347
349
|
end
|
348
350
|
|
349
351
|
# Writes an item to {#config}.
|
@@ -402,4 +404,4 @@ module WebPipe
|
|
402
404
|
# cycle.
|
403
405
|
class Halted < Conn; end
|
404
406
|
end
|
405
|
-
end
|
407
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/monads/result'
|
2
4
|
require 'web_pipe/types'
|
3
5
|
require 'web_pipe/conn'
|
@@ -27,10 +29,10 @@ module WebPipe
|
|
27
29
|
def initialize(returned)
|
28
30
|
super(
|
29
31
|
<<~eos
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
An operation returned +#{returned.inspect}+. To be valid,
|
33
|
+
an operation must return whether a
|
34
|
+
WebPipe::Conn::Ongoing or a WebPipe::Conn::Halted.
|
35
|
+
eos
|
34
36
|
)
|
35
37
|
end
|
36
38
|
end
|
@@ -66,14 +68,14 @@ module WebPipe
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def apply_operation(conn, operation)
|
69
|
-
result = operation.(conn)
|
71
|
+
result = operation.call(conn)
|
70
72
|
case result
|
71
73
|
when Conn::Ongoing
|
72
74
|
Success(result)
|
73
75
|
when Conn::Halted
|
74
76
|
Failure(result)
|
75
77
|
else
|
76
|
-
raise InvalidOperationResult
|
78
|
+
raise InvalidOperationResult, result
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
@@ -84,4 +86,4 @@ module WebPipe
|
|
84
86
|
end
|
85
87
|
end
|
86
88
|
end
|
87
|
-
end
|
89
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module WebPipe
|
2
4
|
module ConnSupport
|
3
5
|
# Helpers to work with headers and its rack's env representation.
|
@@ -12,7 +14,7 @@ module WebPipe
|
|
12
14
|
#
|
13
15
|
# Headers are all those pairs which key begins with `HTTP_` plus
|
14
16
|
# those detailed in {HEADERS_AS_CGI}.
|
15
|
-
#
|
17
|
+
#
|
16
18
|
# @param env [Types::Env[]]
|
17
19
|
#
|
18
20
|
# @return [Types::Headers[]]
|
@@ -21,14 +23,14 @@ module WebPipe
|
|
21
23
|
# @see .normalize_key
|
22
24
|
def self.extract(env)
|
23
25
|
Hash[
|
24
|
-
env
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
env
|
27
|
+
.select { |k, _v| k.start_with?('HTTP_') }
|
28
|
+
.map { |k, v| pair(k[5..-1], v) }
|
29
|
+
.concat(
|
30
|
+
env
|
31
|
+
.select { |k, _v| HEADERS_AS_CGI.include?(k) }
|
32
|
+
.map { |k, v| pair(k, v) }
|
33
|
+
)
|
32
34
|
]
|
33
35
|
end
|
34
36
|
|
@@ -103,4 +105,4 @@ module WebPipe
|
|
103
105
|
end
|
104
106
|
end
|
105
107
|
end
|
106
|
-
end
|
108
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/types'
|
2
4
|
require 'rack/request'
|
3
5
|
|
@@ -25,18 +27,18 @@ module WebPipe
|
|
25
27
|
QueryString = Strict::String
|
26
28
|
RequestBody = Interface(:gets, :each, :read, :rewind)
|
27
29
|
|
28
|
-
Status = Strict::Integer
|
29
|
-
|
30
|
-
|
30
|
+
Status = Strict::Integer
|
31
|
+
.default(200)
|
32
|
+
.constrained(gteq: 100, lteq: 599)
|
31
33
|
ResponseBody = Interface(:each).default { [''] }
|
32
34
|
|
33
|
-
Headers = Strict::Hash
|
34
|
-
|
35
|
-
|
35
|
+
Headers = Strict::Hash
|
36
|
+
.map(Strict::String, Strict::String)
|
37
|
+
.default { {} }
|
36
38
|
|
37
|
-
Bag = Strict::Hash
|
38
|
-
|
39
|
-
|
39
|
+
Bag = Strict::Hash
|
40
|
+
.map(Strict::Symbol, Strict::Any)
|
41
|
+
.default { {} }
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
data/lib/web_pipe/dsl/builder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'web_pipe/types'
|
2
4
|
require 'web_pipe/dsl/class_context'
|
3
5
|
require 'web_pipe/dsl/instance_methods'
|
@@ -12,7 +14,7 @@ module WebPipe
|
|
12
14
|
class Builder < Module
|
13
15
|
# Container with nothing registered.
|
14
16
|
EMPTY_CONTAINER = Types::EMPTY_HASH
|
15
|
-
|
17
|
+
|
16
18
|
# @!attribute [r] container
|
17
19
|
# @return [Types::Container[]]
|
18
20
|
attr_reader :container
|
@@ -24,11 +26,11 @@ module WebPipe
|
|
24
26
|
@container = Types::Container[container]
|
25
27
|
@class_context = ClassContext.new(container: container)
|
26
28
|
end
|
27
|
-
|
29
|
+
|
28
30
|
def included(klass)
|
29
31
|
klass.extend(class_context)
|
30
32
|
klass.include(InstanceMethods)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
34
|
-
end
|
36
|
+
end
|