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