hanami-api 0.1.2 → 0.3.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/.github/workflows/ci.yml +4 -2
- data/.rubocop.yml +6 -2
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/LICENSE.md +22 -0
- data/README.md +267 -21
- data/hanami-api.gemspec +6 -4
- data/lib/hanami/api/block/context.rb +31 -7
- data/lib/hanami/api/dsl.rb +136 -0
- data/lib/hanami/api/middleware.rb +3 -4
- data/lib/hanami/api/router.rb +17 -2
- data/lib/hanami/api/version.rb +1 -1
- data/lib/hanami/api.rb +78 -56
- metadata +14 -13
- data/lib/hanami/api/middleware/app.rb +0 -41
- data/lib/hanami/api/middleware/node.rb +0 -61
- data/lib/hanami/api/middleware/trie.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11eac74d32637ac1f12ee6d7a774a80501167f46b5a51f6a66f04f4518d82476
|
4
|
+
data.tar.gz: b7b1801a85993adcd839e5ce9d1b460e137ebbbf2a8913af2fb583bf765c7196
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 011cbdb50d4372d08d14d7c037599e3f0c3a6ff3838518702272969bfabf0f1692ee9a8bbbfe1f128b4bcc63d2ef6935ec01e330d78b731e264e3f9be62d9030
|
7
|
+
data.tar.gz: 49bec19e9eaf598b31935e02bd45dfbb2cb949f54beee482210f8a33f50b4eb5ab333915fd9d600c34773faaa4f6a8d480966192077f3428bfc7bb4a65f1a37c
|
data/.github/workflows/ci.yml
CHANGED
@@ -12,7 +12,7 @@ name: ci
|
|
12
12
|
- ".rubocop.yml"
|
13
13
|
pull_request:
|
14
14
|
branches:
|
15
|
-
-
|
15
|
+
- main
|
16
16
|
create:
|
17
17
|
|
18
18
|
jobs:
|
@@ -22,7 +22,9 @@ jobs:
|
|
22
22
|
fail-fast: false
|
23
23
|
matrix:
|
24
24
|
ruby:
|
25
|
-
- "2
|
25
|
+
- "3.2"
|
26
|
+
- "3.1"
|
27
|
+
- "3.0"
|
26
28
|
env:
|
27
29
|
CODACY_RUN_LOCAL: true
|
28
30
|
CODACY_PROJECT_TOKEN: ${{secrets.CODACY_PROJECT_TOKEN}}
|
data/.rubocop.yml
CHANGED
@@ -5,6 +5,10 @@
|
|
5
5
|
# * https://github.com/bbatsov/ruby-style-guide
|
6
6
|
# * https://rubocop.readthedocs.io/
|
7
7
|
inherit_from:
|
8
|
-
- https://raw.githubusercontent.com/hanami/devtools/
|
8
|
+
- https://raw.githubusercontent.com/hanami/devtools/main/.rubocop.yml
|
9
9
|
AllCops:
|
10
|
-
TargetRubyVersion:
|
10
|
+
TargetRubyVersion: 3.0
|
11
|
+
|
12
|
+
Lint/EmptyBlock:
|
13
|
+
Exclude:
|
14
|
+
- "./spec/integration/hanami/api/block_spec.rb"
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Hanami::API
|
2
2
|
Minimal, extremely fast, lightweight Ruby framework for HTTP APIs.
|
3
3
|
|
4
|
+
## v0.3.0 - 2022-12-25
|
5
|
+
|
6
|
+
### Added
|
7
|
+
- [Luca Guidi] Official support for Ruby 3.1 & 3.2
|
8
|
+
- [Thomas Jachmann] Streamed responses
|
9
|
+
- [Luca Guidi] Introduce `Hanami::API.helpers` to define helper methods to be used in route blocks
|
10
|
+
- [Luca Guidi] Introduce `Hanami::API#to_inspect` to inspect app routes
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
- [Luca Guidi] Drop support for Ruby 2.7
|
14
|
+
|
15
|
+
## v0.2.0 - 2021-01-05
|
16
|
+
### Added
|
17
|
+
- [Luca Guidi] Official support for Ruby: MRI 3.0
|
18
|
+
- [Luca Guidi] Introduce `Hanami::API::DSL` which gives the ability to other Ruby web frameworks to use the `Hanami::API` DSL
|
19
|
+
|
4
20
|
## v0.1.2 - 2020-10-21
|
5
21
|
### Fixed
|
6
22
|
- [Luca Guidi] Ensure to be able to instantiate an `Hanami::API` app multiple times
|
data/Gemfile
CHANGED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright © 2014-2022 - Hanami Team
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,7 +1,53 @@
|
|
1
1
|
# Hanami::API
|
2
2
|
|
3
3
|
Minimal, extremely fast, lightweight Ruby framework for HTTP APIs.
|
4
|
-
|
4
|
+
|
5
|
+
## Version
|
6
|
+
|
7
|
+
**This branch contains the code for `hanami-api` 0.3.x.**
|
8
|
+
|
9
|
+
## Status
|
10
|
+
|
11
|
+
[](https://badge.fury.io/rb/hanami-api)
|
12
|
+
[](https://github.com/hanami/api/actions?query=workflow%3Aci+branch%3Amain)
|
13
|
+
[](https://codecov.io/gh/hanami/api)
|
14
|
+
[](https://depfu.com/github/hanami/api?project=Bundler)
|
15
|
+
[](http://inch-ci.org/github/hanami/api)
|
16
|
+
|
17
|
+
## Contact
|
18
|
+
|
19
|
+
* Home page: http://hanamirb.org
|
20
|
+
* Mailing List: http://hanamirb.org/mailing-list
|
21
|
+
* API Doc: http://rdoc.info/gems/hanami-api
|
22
|
+
* Bugs/Issues: https://github.com/hanami/api/issues
|
23
|
+
* Support: http://stackoverflow.com/questions/tagged/hanami
|
24
|
+
* Chat: http://chat.hanamirb.org
|
25
|
+
|
26
|
+
## Rubies
|
27
|
+
|
28
|
+
__Hanami::API__ supports Ruby (MRI) 3.0+
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
Add these lines to your application's `Gemfile`:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
gem "hanami-api"
|
36
|
+
gem "puma" # or "webrick", or "thin", "falcon"
|
37
|
+
```
|
38
|
+
|
39
|
+
And then execute:
|
40
|
+
|
41
|
+
```shell
|
42
|
+
$ bundle install
|
43
|
+
```
|
44
|
+
|
45
|
+
Or install it yourself as:
|
46
|
+
|
47
|
+
```shell
|
48
|
+
$ gem install hanami-api
|
49
|
+
```
|
50
|
+
|
5
51
|
* [Performance](#performance)
|
6
52
|
+ [Runtime](#runtime)
|
7
53
|
+ [Memory](#memory)
|
@@ -13,9 +59,12 @@ Minimal, extremely fast, lightweight Ruby framework for HTTP APIs.
|
|
13
59
|
- [Rack endpoint](#rack-endpoint)
|
14
60
|
- [Block endpoint](#block-endpoint)
|
15
61
|
* [String (body)](#string-body)
|
62
|
+
* [Enumerator (body)](#enumerator-body)
|
16
63
|
* [Integer (status code)](#integer-status-code)
|
17
64
|
* [Integer, String (status code, body)](#integer-string-status-code-body)
|
65
|
+
* [Integer, Enumerator (status code, body)](#integer-enumerator-status-code-body)
|
18
66
|
* [Integer, Hash, String (status code, headers, body)](#integer-hash-string-status-code-headers-body)
|
67
|
+
* [Integer, Hash, Enumerator (status code, headers, body)](#integer-hash-enumerator-status-code-headers-body)
|
19
68
|
+ [Block context](#block-context)
|
20
69
|
- [env](#env)
|
21
70
|
- [status](#status)
|
@@ -27,31 +76,14 @@ Minimal, extremely fast, lightweight Ruby framework for HTTP APIs.
|
|
27
76
|
- [back](#back)
|
28
77
|
- [json](#json)
|
29
78
|
+ [Scope](#scope)
|
79
|
+
+ [Helpers](#helpers)
|
30
80
|
+ [Rack Middleware](#rack-middleware)
|
81
|
+
+ [Streamed Responses](#streamed-responses)
|
31
82
|
+ [Body Parsers](#body-parsers)
|
83
|
+
* [Testing](#testing)
|
32
84
|
* [Development](#development)
|
33
85
|
* [Contributing](#contributing)
|
34
86
|
|
35
|
-
## Installation
|
36
|
-
|
37
|
-
Add this line to your application's `Gemfile`:
|
38
|
-
|
39
|
-
```ruby
|
40
|
-
gem "hanami-api"
|
41
|
-
```
|
42
|
-
|
43
|
-
And then execute:
|
44
|
-
|
45
|
-
```shell
|
46
|
-
$ bundle install
|
47
|
-
```
|
48
|
-
|
49
|
-
Or install it yourself as:
|
50
|
-
|
51
|
-
```shell
|
52
|
-
$ gem install hanami-api
|
53
|
-
```
|
54
|
-
|
55
87
|
## Performance
|
56
88
|
|
57
89
|
Benchmark against an app with 10,000 routes, hitting the 10,000th to measure the worst case scenario.
|
@@ -145,6 +177,7 @@ get "/", to: MyEndpoint.new
|
|
145
177
|
* `post`
|
146
178
|
* `patch`
|
147
179
|
* `put`
|
180
|
+
* `delete`
|
148
181
|
* `options`
|
149
182
|
* `trace`
|
150
183
|
* `link`
|
@@ -177,6 +210,16 @@ end
|
|
177
210
|
|
178
211
|
It will return `[200, {}, ["Hello, world"]]`
|
179
212
|
|
213
|
+
##### Enumerator (body)
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
get "/" do
|
217
|
+
Enumerator.new { ... }
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
It will return `[200, {}, Enumerator]`, see [Streamed Responses](#streamed-responses)
|
222
|
+
|
180
223
|
##### Integer (status code)
|
181
224
|
|
182
225
|
```ruby
|
@@ -197,6 +240,16 @@ end
|
|
197
240
|
|
198
241
|
It will return `[401, {}, ["You shall not pass"]]`
|
199
242
|
|
243
|
+
##### Integer, Enumerator (status code, body)
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
get "/" do
|
247
|
+
[401, Enumerator.new { ... }]
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
It will return `[401, {}, Enumerator]`, see [Streamed Responses](#streamed-responses)
|
252
|
+
|
200
253
|
##### Integer, Hash, String (status code, headers, body)
|
201
254
|
|
202
255
|
```ruby
|
@@ -207,6 +260,16 @@ end
|
|
207
260
|
|
208
261
|
It will return `[401, {"X-Custom-Header" => "foo"}, ["You shall not pass"]]`
|
209
262
|
|
263
|
+
##### Integer, Hash, Enumerator (status code, headers, body)
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
get "/" do
|
267
|
+
[401, {"X-Custom-Header" => "foo"}, Enumerator.new { ... }]
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
271
|
+
It will return `[401, {"X-Custom-Header" => "foo"}, Enumerator]`, see [Streamed Responses](#streamed-responses)
|
272
|
+
|
210
273
|
### Block context
|
211
274
|
|
212
275
|
When using the block syntax there is a rich API to use.
|
@@ -272,6 +335,14 @@ get "/" do
|
|
272
335
|
end
|
273
336
|
```
|
274
337
|
|
338
|
+
Set HTTP response body using a [Streamed Response](#streamed-responses)
|
339
|
+
|
340
|
+
```ruby
|
341
|
+
get "/" do
|
342
|
+
body Enumerator.new { ... }
|
343
|
+
end
|
344
|
+
```
|
345
|
+
|
275
346
|
#### params
|
276
347
|
|
277
348
|
Access params for current request
|
@@ -307,6 +378,14 @@ end
|
|
307
378
|
|
308
379
|
It sets a Rack response: `[401, {}, ["You shall not pass"]]`
|
309
380
|
|
381
|
+
You can also use a [Streamed Response](#streamed-responses) here
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
get "/authenticate" do
|
385
|
+
halt(401, Enumerator.new { ... })
|
386
|
+
end
|
387
|
+
```
|
388
|
+
|
310
389
|
#### redirect
|
311
390
|
|
312
391
|
Redirects request and immediately halts it
|
@@ -363,6 +442,15 @@ get "/user/:id" do
|
|
363
442
|
end
|
364
443
|
```
|
365
444
|
|
445
|
+
If you want a [Streamed Response](#streamed-responses)
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
get "/users" do
|
449
|
+
users = Enumerator.new { ... }
|
450
|
+
json(users)
|
451
|
+
end
|
452
|
+
```
|
453
|
+
|
366
454
|
### Scope
|
367
455
|
|
368
456
|
Prefixing routes is possible with routing scopes:
|
@@ -377,6 +465,58 @@ end
|
|
377
465
|
|
378
466
|
It will generate a route with `"/api/v1/users"` as path.
|
379
467
|
|
468
|
+
### Helpers
|
469
|
+
|
470
|
+
Define helper methods available within the block context.
|
471
|
+
Helper methods have access to default utilities available in block context (e.g. `#halt`).
|
472
|
+
|
473
|
+
Helpers can be defined inline by passing a block to the `.helpers` method:
|
474
|
+
|
475
|
+
```ruby
|
476
|
+
require "hanami/api"
|
477
|
+
|
478
|
+
class MyAPI < Hanami::API
|
479
|
+
helpers do
|
480
|
+
def redirect_to_root
|
481
|
+
# redirect method is provided by Hanami::API block context
|
482
|
+
redirect "/"
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
root { "Hello, World" }
|
487
|
+
|
488
|
+
get "/legacy" do
|
489
|
+
redirect_to_root
|
490
|
+
end
|
491
|
+
end
|
492
|
+
```
|
493
|
+
|
494
|
+
Alternatively, `.helpers` accepts a module.
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
require "hanami/api"
|
498
|
+
|
499
|
+
class MyAPI < Hanami::API
|
500
|
+
module Authentication
|
501
|
+
private
|
502
|
+
|
503
|
+
def unauthorized
|
504
|
+
halt(401)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
helpers(Authentication)
|
509
|
+
|
510
|
+
root { "Hello, World" }
|
511
|
+
|
512
|
+
get "/secrets" do
|
513
|
+
unauthorized
|
514
|
+
end
|
515
|
+
end
|
516
|
+
```
|
517
|
+
|
518
|
+
You can use `.helpers` multiple times in the same app.
|
519
|
+
|
380
520
|
### Rack Middleware
|
381
521
|
|
382
522
|
To mount a Rack middleware it's possible with `.use`
|
@@ -411,6 +551,56 @@ it's part of the top level scope. `ApiAuthentication` it's used for all the API
|
|
411
551
|
versions, because it's defined in the `"api"` scope. `ApiV1Deprecation` is used
|
412
552
|
only by the routes in `"v1"` scope, but not by `"v2"`.
|
413
553
|
|
554
|
+
### Streamed Responses
|
555
|
+
|
556
|
+
When the work to be done by the server takes time, it may be a good idea to
|
557
|
+
stream your response. For this, you just use an `Enumerator` anywhere you would
|
558
|
+
normally use a `String` as body or another `Object` as JSON response. Here's an
|
559
|
+
example of streaming JSON data:
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
scope "stream" do
|
563
|
+
use ::Rack::Chunked
|
564
|
+
|
565
|
+
get "/data" do
|
566
|
+
Enumerator.new do |yielder|
|
567
|
+
data = %w[a b c]
|
568
|
+
data.each do |item|
|
569
|
+
yielder << item
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
get "/to_enum" do
|
575
|
+
%w[a b c].to_enum
|
576
|
+
end
|
577
|
+
|
578
|
+
get "/json" do
|
579
|
+
result = Enumerator.new do |yielder|
|
580
|
+
data = %w[a b c]
|
581
|
+
data.each do |item|
|
582
|
+
yielder << item
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
json(result)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
```
|
590
|
+
|
591
|
+
Note:
|
592
|
+
|
593
|
+
* Returning an `Enumerator` will also work without `Rack::Chunked`, it just
|
594
|
+
won't stream but return the whole body at the end instead.
|
595
|
+
* Data pushed to `yielder` MUST be a `String`.
|
596
|
+
* Streaming does not work with WEBrick as it buffers its response. We recommend
|
597
|
+
using `puma`, though you may find success with other servers.
|
598
|
+
* To manual test this feature use a web browser or cURL:
|
599
|
+
|
600
|
+
```shell
|
601
|
+
$ curl --raw -i http://localhost:2300/stream/data
|
602
|
+
```
|
603
|
+
|
414
604
|
### Body Parsers
|
415
605
|
|
416
606
|
Rack ignores request bodies unless they come from a form submission.
|
@@ -425,6 +615,59 @@ require "hanami/middleware/body_parser"
|
|
425
615
|
use Hanami::Middleware::BodyParser, :json
|
426
616
|
```
|
427
617
|
|
618
|
+
## Testing
|
619
|
+
|
620
|
+
## Unit testing
|
621
|
+
You can unit test your `Hanami::API` app by passing a `env` hash to your app's `#call` method.
|
622
|
+
|
623
|
+
The keys that (based on the Rack standard) `Hanami::API` uses for routing are:
|
624
|
+
* `PATH_INFO`
|
625
|
+
* `REQUEST_METHOD`
|
626
|
+
|
627
|
+
|
628
|
+
For example, a spec for the basic app in the [Usage section](https://github.com/hanami/api#usage) could be:
|
629
|
+
|
630
|
+
```ruby
|
631
|
+
require "my_project/app"
|
632
|
+
|
633
|
+
RSpec.describe App do
|
634
|
+
describe "#call" do
|
635
|
+
it "returns successfully" do
|
636
|
+
response = subject.call({"PATH_INFO" => "/", "REQUEST_METHOD" => "GET"})
|
637
|
+
expect(response).to eq([200, {}, ["Hello, world"]])
|
638
|
+
end
|
639
|
+
end
|
640
|
+
end
|
641
|
+
```
|
642
|
+
|
643
|
+
## Integration testing
|
644
|
+
Add this line to your application's `Gemfile`:
|
645
|
+
|
646
|
+
```ruby
|
647
|
+
gem "rack-test", group: :test
|
648
|
+
```
|
649
|
+
|
650
|
+
In a test, load `Rack::Test`:
|
651
|
+
|
652
|
+
```ruby
|
653
|
+
require "rack/test"
|
654
|
+
```
|
655
|
+
|
656
|
+
and then, inside your spec/test, include its helper methods:
|
657
|
+
|
658
|
+
```ruby
|
659
|
+
include Rack::Test::Methods
|
660
|
+
```
|
661
|
+
|
662
|
+
Then you can use its methods like `get` and `last_response`, e.g.:
|
663
|
+
|
664
|
+
```ruby
|
665
|
+
it "returns the status 200" do
|
666
|
+
get "/"
|
667
|
+
expect(last_response.status).to eq 200
|
668
|
+
end
|
669
|
+
```
|
670
|
+
|
428
671
|
## Development
|
429
672
|
|
430
673
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -435,3 +678,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
435
678
|
|
436
679
|
Bug reports and pull requests are welcome on GitHub at https://github.com/hanami/api.
|
437
680
|
|
681
|
+
## Copyright
|
682
|
+
|
683
|
+
Copyright © 2014-2022 Hanami Team – Released under MIT License.
|
data/hanami-api.gemspec
CHANGED
@@ -11,13 +11,14 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.summary = "Hanami API"
|
12
12
|
spec.description = "Extremely fast and lightweight HTTP API"
|
13
13
|
spec.homepage = "http://rubygems.org"
|
14
|
-
spec.
|
14
|
+
spec.licenses = ["MIT"]
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
|
15
16
|
|
16
17
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
17
18
|
|
18
19
|
spec.metadata["homepage_uri"] = spec.homepage
|
19
20
|
spec.metadata["source_code_uri"] = "https://github.com/hanami/api"
|
20
|
-
spec.metadata["changelog_uri"] = "https://github.com/hanami/api/blob/
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/hanami/api/blob/main/CHANGELOG.md"
|
21
22
|
|
22
23
|
# Specify which files should be added to the gem when it is released.
|
23
24
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -28,11 +29,12 @@ Gem::Specification.new do |spec|
|
|
28
29
|
spec.bindir = "exe"
|
29
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
31
|
spec.require_paths = ["lib"]
|
32
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
31
33
|
|
32
|
-
spec.add_dependency "hanami-router", "~> 2.0
|
34
|
+
spec.add_dependency "hanami-router", "~> 2.0"
|
33
35
|
|
34
36
|
spec.add_development_dependency "rake", "~> 13.0"
|
35
37
|
spec.add_development_dependency "rspec", "~> 3.8"
|
36
|
-
spec.add_development_dependency "rubocop", "~> 0
|
38
|
+
spec.add_development_dependency "rubocop", "~> 1.0"
|
37
39
|
spec.add_development_dependency "yard", "~> 0.9"
|
38
40
|
end
|
@@ -124,7 +124,12 @@ module Hanami
|
|
124
124
|
# end
|
125
125
|
def json(object, mime = "application/json")
|
126
126
|
headers["Content-Type"] = mime
|
127
|
-
|
127
|
+
case object
|
128
|
+
in Enumerator => collection
|
129
|
+
json_enum(collection)
|
130
|
+
else
|
131
|
+
JSON.generate(object)
|
132
|
+
end
|
128
133
|
end
|
129
134
|
|
130
135
|
# @since 0.1.0
|
@@ -132,10 +137,11 @@ module Hanami
|
|
132
137
|
#
|
133
138
|
def call
|
134
139
|
case caught
|
135
|
-
|
140
|
+
in String => body
|
136
141
|
[status, headers, [body]]
|
137
|
-
|
138
|
-
|
142
|
+
in Enumerator => body
|
143
|
+
[status, headers, body]
|
144
|
+
in Integer => status
|
139
145
|
#
|
140
146
|
# NOTE: It must use `self.body` so it will pick the method defined above.
|
141
147
|
#
|
@@ -144,12 +150,16 @@ module Hanami
|
|
144
150
|
# When that happens, the body that was manually set is ignored,
|
145
151
|
# which results in a bug.
|
146
152
|
[status, headers, [self.body || http_status(status)]]
|
147
|
-
|
148
|
-
in [Integer => status, String => body]
|
153
|
+
in [Integer => status, String => body]
|
149
154
|
[status, headers, [body]]
|
150
|
-
|
155
|
+
in [Integer => status, Enumerator => body]
|
156
|
+
[status, headers, body]
|
157
|
+
in [Integer => status, Hash => caught_headers, String => body]
|
151
158
|
headers.merge!(caught_headers)
|
152
159
|
[status, headers, [body]]
|
160
|
+
in [Integer => status, Hash => caught_headers, Enumerator => body]
|
161
|
+
headers.merge!(caught_headers)
|
162
|
+
[status, headers, body]
|
153
163
|
end
|
154
164
|
end
|
155
165
|
|
@@ -168,6 +178,20 @@ module Hanami
|
|
168
178
|
def http_status(code)
|
169
179
|
Rack::Utils::HTTP_STATUS_CODES.fetch(code)
|
170
180
|
end
|
181
|
+
|
182
|
+
# @since x.x.x
|
183
|
+
# @api private
|
184
|
+
def json_enum(collection)
|
185
|
+
Enumerator.new do |yielder|
|
186
|
+
yielder << "["
|
187
|
+
collection.each_with_index do |item, i|
|
188
|
+
yielder << "," if i.positive?
|
189
|
+
yielder << JSON.generate(item)
|
190
|
+
end
|
191
|
+
ensure
|
192
|
+
yielder << "]"
|
193
|
+
end
|
194
|
+
end
|
171
195
|
end
|
172
196
|
end
|
173
197
|
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class API
|
5
|
+
# Expose Hanami::API features to third party frameworks that need to expose
|
6
|
+
# a routing DSL.
|
7
|
+
#
|
8
|
+
# @since 0.2.0
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # framework.rb
|
12
|
+
# require "hanami/api"
|
13
|
+
#
|
14
|
+
# module Framework
|
15
|
+
# class App
|
16
|
+
# def self.inherited(base)
|
17
|
+
# super
|
18
|
+
# base.extend(Hanami::API::DSL)
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # app.rb
|
24
|
+
# require "framework/app"
|
25
|
+
#
|
26
|
+
# class MyApp < Framework::App
|
27
|
+
# routes do
|
28
|
+
# root { "Hello, World!" }
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # config.ru
|
33
|
+
# require_relative "./app"
|
34
|
+
#
|
35
|
+
# run MyApp.new
|
36
|
+
module DSL
|
37
|
+
# @since 0.2.0
|
38
|
+
# @api private
|
39
|
+
def self.extended(app)
|
40
|
+
super
|
41
|
+
|
42
|
+
app.extend(ClassMethods)
|
43
|
+
app.extend(ClassMethods::Routes)
|
44
|
+
app.include(InstanceMethods)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @since 0.2.0
|
48
|
+
# @api private
|
49
|
+
module ClassMethods
|
50
|
+
# @since 0.2.0
|
51
|
+
# @api private
|
52
|
+
attr_reader :router
|
53
|
+
|
54
|
+
# @since 0.2.0
|
55
|
+
# @api private
|
56
|
+
def self.extended(app)
|
57
|
+
super
|
58
|
+
|
59
|
+
app.class_eval do
|
60
|
+
klass = Block::Context.dup
|
61
|
+
app.const_set(:BlockContext, klass)
|
62
|
+
|
63
|
+
@router = Router.new(block_context: klass)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @since 0.2.0
|
68
|
+
# @api private
|
69
|
+
module Routes
|
70
|
+
# A block to define application routes
|
71
|
+
#
|
72
|
+
# This is ONLY available for third-party frameworks that use
|
73
|
+
# Hanami::API DSL.
|
74
|
+
#
|
75
|
+
# If you use Hanami::API directly, this method isn't available.
|
76
|
+
#
|
77
|
+
# @param blk [Proc] the block to define the routes
|
78
|
+
#
|
79
|
+
# @see Hanami::API::Router
|
80
|
+
#
|
81
|
+
# @since 0.2.0
|
82
|
+
# @api public
|
83
|
+
def routes(&blk)
|
84
|
+
router.instance_eval(&blk)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
module InstanceMethods
|
90
|
+
# Initialize the app
|
91
|
+
#
|
92
|
+
# @param router [Hanami::API::Router] the application router
|
93
|
+
#
|
94
|
+
# @since 0.2.0
|
95
|
+
# @api public
|
96
|
+
def initialize(router: self.class.router.dup)
|
97
|
+
@router = router
|
98
|
+
|
99
|
+
freeze
|
100
|
+
end
|
101
|
+
|
102
|
+
# @since 0.2.0
|
103
|
+
# @api private
|
104
|
+
def freeze
|
105
|
+
@app = @router.to_rack_app
|
106
|
+
@url_helpers = @router.url_helpers
|
107
|
+
@router.remove_instance_variable(:@url_helpers)
|
108
|
+
remove_instance_variable(:@router)
|
109
|
+
@url_helpers.freeze
|
110
|
+
@app.freeze
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
# Compatibility with Rack protocol
|
115
|
+
#
|
116
|
+
# @param env [Hash] a Rack env for the current request
|
117
|
+
#
|
118
|
+
# @since 0.2.0
|
119
|
+
# @api public
|
120
|
+
def call(env)
|
121
|
+
@app.call(env)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Printable routes
|
125
|
+
#
|
126
|
+
# @return [String] printable routes
|
127
|
+
#
|
128
|
+
# @since x.x.x
|
129
|
+
# @api public
|
130
|
+
def to_inspect
|
131
|
+
@app.to_inspect
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hanami/middleware/app"
|
4
|
+
|
3
5
|
module Hanami
|
4
6
|
class API
|
5
7
|
# Hanami::API middleware stack
|
@@ -7,9 +9,6 @@ module Hanami
|
|
7
9
|
# @since 0.1.0
|
8
10
|
# @api private
|
9
11
|
module Middleware
|
10
|
-
require "hanami/api/middleware/app"
|
11
|
-
require "hanami/api/middleware/trie"
|
12
|
-
|
13
12
|
# Middleware stack
|
14
13
|
#
|
15
14
|
# @since 0.1.0
|
@@ -44,7 +43,7 @@ module Hanami
|
|
44
43
|
mapping = to_hash
|
45
44
|
return app if mapping.empty?
|
46
45
|
|
47
|
-
App.new(app, mapping)
|
46
|
+
Hanami::Middleware::App.new(app, mapping)
|
48
47
|
end
|
49
48
|
|
50
49
|
private
|
data/lib/hanami/api/router.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "hanami/router"
|
4
|
+
require "hanami/router/inspector"
|
4
5
|
require "hanami/api/block/context"
|
5
6
|
|
6
7
|
module Hanami
|
7
8
|
class API
|
8
9
|
# @since 0.1.0
|
9
10
|
class Router < ::Hanami::Router
|
11
|
+
# @since x.x.x
|
12
|
+
# @api private
|
13
|
+
attr_reader :inspector
|
14
|
+
|
10
15
|
# @since 0.1.0
|
11
16
|
# @api private
|
12
|
-
def initialize(block_context: Block::Context, **kwargs)
|
13
|
-
super(block_context: block_context, **kwargs)
|
17
|
+
def initialize(block_context: Block::Context, inspector: Inspector.new, **kwargs)
|
18
|
+
super(block_context: block_context, inspector: inspector, **kwargs)
|
14
19
|
@stack = Middleware::Stack.new(@path_prefix.to_s)
|
15
20
|
end
|
16
21
|
|
@@ -34,6 +39,16 @@ module Hanami
|
|
34
39
|
def to_rack_app
|
35
40
|
@stack.finalize(self)
|
36
41
|
end
|
42
|
+
|
43
|
+
# Returns formatted routes
|
44
|
+
#
|
45
|
+
# @return [String] formatted routes
|
46
|
+
#
|
47
|
+
# @since x.x.x
|
48
|
+
# @api private
|
49
|
+
def to_inspect
|
50
|
+
@inspector.call
|
51
|
+
end
|
37
52
|
end
|
38
53
|
end
|
39
54
|
end
|
data/lib/hanami/api/version.rb
CHANGED
data/lib/hanami/api.rb
CHANGED
@@ -9,21 +9,66 @@ module Hanami
|
|
9
9
|
require "hanami/api/error"
|
10
10
|
require "hanami/api/router"
|
11
11
|
require "hanami/api/middleware"
|
12
|
+
require "hanami/api/dsl"
|
12
13
|
|
13
14
|
# @since 0.1.0
|
14
15
|
# @api private
|
15
16
|
def self.inherited(app)
|
16
17
|
super
|
17
18
|
|
18
|
-
app.
|
19
|
-
|
20
|
-
end
|
19
|
+
app.extend(DSL::ClassMethods)
|
20
|
+
app.include(DSL::InstanceMethods)
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
# Defines helper methods available within the block context.
|
24
|
+
# Helper methods have access to default utilities available in block
|
25
|
+
# context (e.g. `#halt`).
|
26
|
+
#
|
27
|
+
# @param mod [Module] optional module to include in block context
|
28
|
+
# @param blk [Proc] inline helper definitions
|
29
|
+
#
|
30
|
+
# @since x.x.x
|
31
|
+
#
|
32
|
+
# @example Inline helpers definition
|
33
|
+
# require "hanami/api"
|
34
|
+
#
|
35
|
+
# class MyAPI < Hanami::API
|
36
|
+
# helpers do
|
37
|
+
# def redirect_to_root
|
38
|
+
# # redirect method is provided by Hanami::API block context
|
39
|
+
# redirect "/"
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# root { "Hello, World" }
|
44
|
+
#
|
45
|
+
# get "/legacy" do
|
46
|
+
# redirect_to_root
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# @example Module helpers definition
|
51
|
+
# require "hanami/api"
|
52
|
+
#
|
53
|
+
# class MyAPI < Hanami::API
|
54
|
+
# module Authentication
|
55
|
+
# private
|
56
|
+
#
|
57
|
+
# def unauthorized
|
58
|
+
# halt(401)
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# helpers(Authentication)
|
63
|
+
#
|
64
|
+
# root { "Hello, World" }
|
65
|
+
#
|
66
|
+
# get "/secrets" do
|
67
|
+
# unauthorized
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
def self.helpers(mod = nil, &blk)
|
71
|
+
const_get(:BlockContext).include(mod || Module.new(&blk))
|
27
72
|
end
|
28
73
|
|
29
74
|
# Defines a named root route (a GET route for "/")
|
@@ -50,8 +95,8 @@ module Hanami
|
|
50
95
|
# "Hello from Hanami!"
|
51
96
|
# end
|
52
97
|
# end
|
53
|
-
def self.root(
|
54
|
-
@router.root(
|
98
|
+
def self.root(...)
|
99
|
+
@router.root(...)
|
55
100
|
end
|
56
101
|
|
57
102
|
# Defines a route that accepts GET requests for the given path.
|
@@ -87,8 +132,8 @@ module Hanami
|
|
87
132
|
# class MyAPI < Hanami::API
|
88
133
|
# get "/users/:id", to: ->(*) { [200, {}, ["OK"]] }, id: /\d+/
|
89
134
|
# end
|
90
|
-
def self.get(
|
91
|
-
@router.get(
|
135
|
+
def self.get(...)
|
136
|
+
@router.get(...)
|
92
137
|
end
|
93
138
|
|
94
139
|
# Defines a route that accepts POST requests for the given path.
|
@@ -102,8 +147,8 @@ module Hanami
|
|
102
147
|
# @since 0.1.0
|
103
148
|
#
|
104
149
|
# @see .get
|
105
|
-
def self.post(
|
106
|
-
@router.post(
|
150
|
+
def self.post(...)
|
151
|
+
@router.post(...)
|
107
152
|
end
|
108
153
|
|
109
154
|
# Defines a route that accepts PATCH requests for the given path.
|
@@ -117,8 +162,8 @@ module Hanami
|
|
117
162
|
# @since 0.1.0
|
118
163
|
#
|
119
164
|
# @see .get
|
120
|
-
def self.patch(
|
121
|
-
@router.patch(
|
165
|
+
def self.patch(...)
|
166
|
+
@router.patch(...)
|
122
167
|
end
|
123
168
|
|
124
169
|
# Defines a route that accepts PUT requests for the given path.
|
@@ -132,8 +177,8 @@ module Hanami
|
|
132
177
|
# @since 0.1.0
|
133
178
|
#
|
134
179
|
# @see .get
|
135
|
-
def self.put(
|
136
|
-
@router.put(
|
180
|
+
def self.put(...)
|
181
|
+
@router.put(...)
|
137
182
|
end
|
138
183
|
|
139
184
|
# Defines a route that accepts DELETE requests for the given path.
|
@@ -147,8 +192,8 @@ module Hanami
|
|
147
192
|
# @since 0.1.0
|
148
193
|
#
|
149
194
|
# @see .get
|
150
|
-
def self.delete(
|
151
|
-
@router.delete(
|
195
|
+
def self.delete(...)
|
196
|
+
@router.delete(...)
|
152
197
|
end
|
153
198
|
|
154
199
|
# Defines a route that accepts TRACE requests for the given path.
|
@@ -162,8 +207,8 @@ module Hanami
|
|
162
207
|
# @since 0.1.0
|
163
208
|
#
|
164
209
|
# @see .get
|
165
|
-
def self.trace(
|
166
|
-
@router.trace(
|
210
|
+
def self.trace(...)
|
211
|
+
@router.trace(...)
|
167
212
|
end
|
168
213
|
|
169
214
|
# Defines a route that accepts OPTIONS requests for the given path.
|
@@ -177,8 +222,8 @@ module Hanami
|
|
177
222
|
# @since 0.1.0
|
178
223
|
#
|
179
224
|
# @see .get
|
180
|
-
def self.options(
|
181
|
-
@router.options(
|
225
|
+
def self.options(...)
|
226
|
+
@router.options(...)
|
182
227
|
end
|
183
228
|
|
184
229
|
# Defines a route that accepts LINK requests for the given path.
|
@@ -192,8 +237,8 @@ module Hanami
|
|
192
237
|
# @since 0.1.0
|
193
238
|
#
|
194
239
|
# @see .get
|
195
|
-
def self.link(
|
196
|
-
@router.link(
|
240
|
+
def self.link(...)
|
241
|
+
@router.link(...)
|
197
242
|
end
|
198
243
|
|
199
244
|
# Defines a route that accepts UNLINK requests for the given path.
|
@@ -207,8 +252,8 @@ module Hanami
|
|
207
252
|
# @since 0.1.0
|
208
253
|
#
|
209
254
|
# @see .get
|
210
|
-
def self.unlink(
|
211
|
-
@router.unlink(
|
255
|
+
def self.unlink(...)
|
256
|
+
@router.unlink(...)
|
212
257
|
end
|
213
258
|
|
214
259
|
# Defines a route that redirects the incoming request to another path.
|
@@ -221,8 +266,8 @@ module Hanami
|
|
221
266
|
# @since 0.1.0
|
222
267
|
#
|
223
268
|
# @see .get
|
224
|
-
def self.redirect(
|
225
|
-
@router.redirect(
|
269
|
+
def self.redirect(...)
|
270
|
+
@router.redirect(...)
|
226
271
|
end
|
227
272
|
|
228
273
|
# Defines a routing scope. Routes defined in the context of a scope,
|
@@ -245,8 +290,8 @@ module Hanami
|
|
245
290
|
# end
|
246
291
|
#
|
247
292
|
# # It generates a route with a path `/v1/users`
|
248
|
-
def self.scope(
|
249
|
-
@router.scope(
|
293
|
+
def self.scope(...)
|
294
|
+
@router.scope(...)
|
250
295
|
end
|
251
296
|
|
252
297
|
# Mount a Rack application at the specified path.
|
@@ -270,8 +315,8 @@ module Hanami
|
|
270
315
|
# class MyAPI < Hanami::API
|
271
316
|
# mount MyRackApp.new, at: "/foo"
|
272
317
|
# end
|
273
|
-
def self.mount(
|
274
|
-
@router.mount(
|
318
|
+
def self.mount(...)
|
319
|
+
@router.mount(...)
|
275
320
|
end
|
276
321
|
|
277
322
|
# Use a Rack middleware
|
@@ -292,29 +337,6 @@ module Hanami
|
|
292
337
|
@router.use(middleware, *args, &blk)
|
293
338
|
end
|
294
339
|
|
295
|
-
# @since 0.1.0
|
296
|
-
def initialize(router: self.class.router.dup)
|
297
|
-
@router = router
|
298
|
-
|
299
|
-
freeze
|
300
|
-
end
|
301
|
-
|
302
|
-
# @since 0.1.0
|
303
|
-
def freeze
|
304
|
-
@app = @router.to_rack_app
|
305
|
-
@url_helpers = @router.url_helpers
|
306
|
-
@router.remove_instance_variable(:@url_helpers)
|
307
|
-
remove_instance_variable(:@router)
|
308
|
-
@url_helpers.freeze
|
309
|
-
@app.freeze
|
310
|
-
super
|
311
|
-
end
|
312
|
-
|
313
|
-
# @since 0.1.0
|
314
|
-
def call(env)
|
315
|
-
@app.call(env)
|
316
|
-
end
|
317
|
-
|
318
340
|
# TODO: verify if needed here on in block context
|
319
341
|
#
|
320
342
|
# @since 0.1.0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hanami-router
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.0
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.0
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0
|
61
|
+
version: '1.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0
|
68
|
+
version: '1.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: yard
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- ".rubocop.yml"
|
94
94
|
- CHANGELOG.md
|
95
95
|
- Gemfile
|
96
|
+
- LICENSE.md
|
96
97
|
- README.md
|
97
98
|
- Rakefile
|
98
99
|
- bin/console
|
@@ -100,20 +101,20 @@ files:
|
|
100
101
|
- hanami-api.gemspec
|
101
102
|
- lib/hanami/api.rb
|
102
103
|
- lib/hanami/api/block/context.rb
|
104
|
+
- lib/hanami/api/dsl.rb
|
103
105
|
- lib/hanami/api/error.rb
|
104
106
|
- lib/hanami/api/middleware.rb
|
105
|
-
- lib/hanami/api/middleware/app.rb
|
106
|
-
- lib/hanami/api/middleware/node.rb
|
107
|
-
- lib/hanami/api/middleware/trie.rb
|
108
107
|
- lib/hanami/api/router.rb
|
109
108
|
- lib/hanami/api/version.rb
|
110
109
|
homepage: http://rubygems.org
|
111
|
-
licenses:
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
112
|
metadata:
|
113
113
|
allowed_push_host: https://rubygems.org
|
114
114
|
homepage_uri: http://rubygems.org
|
115
115
|
source_code_uri: https://github.com/hanami/api
|
116
|
-
changelog_uri: https://github.com/hanami/api/blob/
|
116
|
+
changelog_uri: https://github.com/hanami/api/blob/main/CHANGELOG.md
|
117
|
+
rubygems_mfa_required: 'true'
|
117
118
|
post_install_message:
|
118
119
|
rdoc_options: []
|
119
120
|
require_paths:
|
@@ -122,14 +123,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
123
|
requirements:
|
123
124
|
- - ">="
|
124
125
|
- !ruby/object:Gem::Version
|
125
|
-
version:
|
126
|
+
version: 3.0.0
|
126
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
128
|
requirements:
|
128
129
|
- - ">="
|
129
130
|
- !ruby/object:Gem::Version
|
130
131
|
version: '0'
|
131
132
|
requirements: []
|
132
|
-
rubygems_version: 3.1
|
133
|
+
rubygems_version: 3.4.1
|
133
134
|
signing_key:
|
134
135
|
specification_version: 4
|
135
136
|
summary: Hanami API
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rack/builder"
|
4
|
-
|
5
|
-
module Hanami
|
6
|
-
class API
|
7
|
-
module Middleware
|
8
|
-
# Hanami::API middleware stack
|
9
|
-
#
|
10
|
-
# @since 0.1.1
|
11
|
-
# @api private
|
12
|
-
class App
|
13
|
-
# @since 0.1.1
|
14
|
-
# @api private
|
15
|
-
def initialize(app, mapping)
|
16
|
-
@trie = Hanami::API::Middleware::Trie.new(app)
|
17
|
-
|
18
|
-
mapping.each do |path, stack|
|
19
|
-
builder = Rack::Builder.new
|
20
|
-
|
21
|
-
stack.each do |middleware, args, blk|
|
22
|
-
builder.use(middleware, *args, &blk)
|
23
|
-
end
|
24
|
-
|
25
|
-
builder.run(app)
|
26
|
-
|
27
|
-
@trie.add(path, builder.to_app.freeze)
|
28
|
-
end
|
29
|
-
|
30
|
-
@trie.freeze
|
31
|
-
end
|
32
|
-
|
33
|
-
# @since 0.1.1
|
34
|
-
# @api private
|
35
|
-
def call(env)
|
36
|
-
@trie.find(env["PATH_INFO"]).call(env)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Hanami
|
4
|
-
class API
|
5
|
-
module Middleware
|
6
|
-
# Trie node to register scopes with custom Rack middleware
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
# @since 0.1.1
|
10
|
-
class Node
|
11
|
-
# @api private
|
12
|
-
# @since 0.1.1
|
13
|
-
attr_reader :app
|
14
|
-
|
15
|
-
# @api private
|
16
|
-
# @since 0.1.1
|
17
|
-
def initialize
|
18
|
-
@app = nil
|
19
|
-
@children = {}
|
20
|
-
end
|
21
|
-
|
22
|
-
# @api private
|
23
|
-
# @since 0.1.1
|
24
|
-
def freeze
|
25
|
-
@children.each(&:freeze)
|
26
|
-
super
|
27
|
-
end
|
28
|
-
|
29
|
-
# @api private
|
30
|
-
# @since 0.1.1
|
31
|
-
def put(segment)
|
32
|
-
@children[segment] ||= self.class.new
|
33
|
-
end
|
34
|
-
|
35
|
-
# @api private
|
36
|
-
# @since 0.1.1
|
37
|
-
def get(segment)
|
38
|
-
@children.fetch(segment) { self if leaf? }
|
39
|
-
end
|
40
|
-
|
41
|
-
# @api private
|
42
|
-
# @since 0.1.1
|
43
|
-
def app!(app)
|
44
|
-
@app = app
|
45
|
-
end
|
46
|
-
|
47
|
-
# @api private
|
48
|
-
# @since 0.1.1
|
49
|
-
def app?
|
50
|
-
@app
|
51
|
-
end
|
52
|
-
|
53
|
-
# @api private
|
54
|
-
# @since 0.1.1
|
55
|
-
def leaf?
|
56
|
-
@children.empty?
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "hanami/api/middleware/node"
|
4
|
-
|
5
|
-
module Hanami
|
6
|
-
class API
|
7
|
-
module Middleware
|
8
|
-
# Trie to register scopes with custom Rack middleware
|
9
|
-
#
|
10
|
-
# @api private
|
11
|
-
# @since 0.1.1
|
12
|
-
class Trie
|
13
|
-
# @api private
|
14
|
-
# @since 0.1.1
|
15
|
-
def initialize(app)
|
16
|
-
@app = app
|
17
|
-
@root = Node.new
|
18
|
-
end
|
19
|
-
|
20
|
-
# @api private
|
21
|
-
# @since 0.1.1
|
22
|
-
def freeze
|
23
|
-
@root.freeze
|
24
|
-
super
|
25
|
-
end
|
26
|
-
|
27
|
-
# @api private
|
28
|
-
# @since 0.1.1
|
29
|
-
def add(path, app)
|
30
|
-
node = @root
|
31
|
-
for_each_segment(path) do |segment|
|
32
|
-
node = node.put(segment)
|
33
|
-
end
|
34
|
-
|
35
|
-
node.app!(app)
|
36
|
-
end
|
37
|
-
|
38
|
-
# @api private
|
39
|
-
# @since 0.1.1
|
40
|
-
def find(path)
|
41
|
-
node = @root
|
42
|
-
|
43
|
-
for_each_segment(path) do |segment|
|
44
|
-
break unless node
|
45
|
-
|
46
|
-
node = node.get(segment)
|
47
|
-
end
|
48
|
-
|
49
|
-
return node.app if node&.app?
|
50
|
-
|
51
|
-
@root.app || @app
|
52
|
-
end
|
53
|
-
|
54
|
-
# @api private
|
55
|
-
# @since 0.1.1
|
56
|
-
def empty?
|
57
|
-
@root.leaf?
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
# @api private
|
63
|
-
# @since 0.1.1
|
64
|
-
def for_each_segment(path, &blk)
|
65
|
-
_, *segments = path.split(/\//)
|
66
|
-
segments.each(&blk)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|