openapi_blocks 0.4.1 → 0.5.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 +4 -0
- data/README.md +128 -51
- data/README.pt-BR.md +128 -51
- data/lib/generators/openapi_blocks/install/install_generator.rb +19 -0
- data/lib/generators/openapi_blocks/install/templates/initializer.rb.tt +36 -0
- data/lib/generators/openapi_blocks/openapi/openapi_generator.rb +15 -0
- data/lib/generators/openapi_blocks/openapi/templates/openapi.rb.tt +48 -0
- data/lib/generators/openapi_blocks/serializer/serializer_generator.rb +15 -0
- data/lib/generators/openapi_blocks/serializer/templates/serializer.rb.tt +15 -0
- data/lib/openapi_blocks/railtie.rb +6 -0
- data/lib/openapi_blocks/version.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a555c50cffdc3d57e766d1d644a2a7715ac4ab3ad730484169f86b09538d9da9
|
|
4
|
+
data.tar.gz: e55fe98e2c802e9823bef23683a8b431e97ecda4d7f6781d02995a7f5d9d1da7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 223d4851ce536c0d9cc685e5098bfe6bb3dbb9cd278ecf7f28287eab0aa6ed58fc06ecad9b1ee470f55a10843256c4e89f6c1ce298d5effadc3140597cdc375a
|
|
7
|
+
data.tar.gz: 65d27cbd44d7f8555d9fe9ad6fdf147f25e0d7e888411ba3aeef7db80ff085799d8c25d4a4dfebbfd33f0285bb3695c00af12c793609b3018f893b233e4f164d
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# OpenapiBlocks
|
|
2
2
|
|
|
3
|
-
OpenapiBlocks is a Rails gem that automatically generates OpenAPI 3.0/3.1 documentation from your ActiveRecord models, ActiveModel validations, and Rails routes — inspired by
|
|
3
|
+
OpenapiBlocks is a Rails gem that automatically generates OpenAPI 3.0/3.1 documentation from your ActiveRecord models, ActiveModel validations, and Rails routes — inspired by ActiveModel::Serializer (https://github.com/rails-api/active_model_serializers).
|
|
4
4
|
|
|
5
|
-
Versão em português brasileiro:
|
|
5
|
+
Versão em português brasileiro: README.pt-BR.md
|
|
6
6
|
|
|
7
|
-
No manual annotation. No DSL noise in your controllers. Just declare what to expose and the spec is generated automatically. Includes a high-performance built-in serializer — ~3.6× faster than
|
|
7
|
+
No manual annotation. No DSL noise in your controllers. Just declare what to expose and the spec is generated automatically. Includes a high-performance built-in serializer — ~3.6× faster than as_json with consistent linear scaling from 10 to 5000 records.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
|
-
Add to your
|
|
13
|
+
Add to your Gemfile:
|
|
14
14
|
|
|
15
15
|
```ruby
|
|
16
16
|
gem "openapi_blocks"
|
|
@@ -24,6 +24,70 @@ bundle install
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
## Generators
|
|
28
|
+
|
|
29
|
+
OpenapiBlocks provides three generators to get you started quickly.
|
|
30
|
+
|
|
31
|
+
### Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
rails generate openapi_blocks:install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Creates `config/initializers/openapi_blocks.rb` with all available options commented out, and mounts the engine in `config/routes.rb`:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
mount OpenapiBlocks::Engine => "/docs"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Openapi
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
rails generate openapi_blocks:openapi User
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Creates `app/openapi/user_openapi.rb` with all available DSL options commented out:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# app/openapi/user_openapi.rb
|
|
53
|
+
class UserOpenapi < OpenapiBlocks::Controller
|
|
54
|
+
# resource UserSerializer
|
|
55
|
+
# controller UsersController
|
|
56
|
+
|
|
57
|
+
# tags "Users"
|
|
58
|
+
|
|
59
|
+
# operation :index do
|
|
60
|
+
# summary "List all users"
|
|
61
|
+
# response 200, description: "List of users", schema: { type: :array, items: :User }
|
|
62
|
+
# end
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Serializer
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
rails generate openapi_blocks:serializer User
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Creates `app/serializers/user_serializer.rb` with all available DSL options commented out:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# app/serializers/user_serializer.rb
|
|
76
|
+
class UserSerializer < OpenapiBlocks::Serializer
|
|
77
|
+
# ignore :password_digest, :reset_password_token
|
|
78
|
+
|
|
79
|
+
# association :posts, type: :array, read_only: true
|
|
80
|
+
# association :company
|
|
81
|
+
|
|
82
|
+
# attribute :full_name, type: :string, read_only: true
|
|
83
|
+
# def full_name
|
|
84
|
+
# "#{object.first_name} #{object.last_name}"
|
|
85
|
+
# end
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
27
91
|
## Setup
|
|
28
92
|
|
|
29
93
|
### 1. Mount the Engine
|
|
@@ -48,7 +112,7 @@ GET /docs/openapi.yaml -> OpenAPI spec in YAML
|
|
|
48
112
|
|
|
49
113
|
### 2. Configure the initializer
|
|
50
114
|
|
|
51
|
-
|
|
115
|
+
OpenapiBlocks.configure is required. The gem raises OpenapiBlocks::Error on the first request if it was never called or if info.title / info.version are blank.
|
|
52
116
|
|
|
53
117
|
```ruby
|
|
54
118
|
# config/initializers/openapi_blocks.rb
|
|
@@ -101,9 +165,9 @@ end
|
|
|
101
165
|
|
|
102
166
|
OpenapiBlocks provides two base classes with distinct responsibilities:
|
|
103
167
|
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
168
|
+
- OpenapiBlocks::Serializer — defines the model, fields, associations, and serialization logic. Lives in app/serializers/.
|
|
169
|
+
- OpenapiBlocks::Controller — defines API operations, parameters, and responses for documentation. Lives in app/openapi/.
|
|
170
|
+
- OpenapiBlocks::Base — legacy base class that combines both concerns. Still supported.
|
|
107
171
|
|
|
108
172
|
### Recommended: Serializer + Controller
|
|
109
173
|
|
|
@@ -142,7 +206,7 @@ end
|
|
|
142
206
|
```ruby
|
|
143
207
|
# app/openapi/user_openapi.rb
|
|
144
208
|
class UserOpenapi < OpenapiBlocks::Controller
|
|
145
|
-
resource UserSerializer
|
|
209
|
+
resource UserSerializer # links to the serializer — schema is derived from it
|
|
146
210
|
controller UsersController
|
|
147
211
|
|
|
148
212
|
tags "Users"
|
|
@@ -169,6 +233,19 @@ class UserOpenapi < OpenapiBlocks::Controller
|
|
|
169
233
|
end
|
|
170
234
|
```
|
|
171
235
|
|
|
236
|
+
#### How the OpenAPI schema is generated
|
|
237
|
+
|
|
238
|
+
When `resource UserSerializer` is declared in a `Controller`, OpenapiBlocks derives the OpenAPI schema directly from the serializer — not from the model. This guarantees that what is documented is exactly what the API returns.
|
|
239
|
+
|
|
240
|
+
The schema is built from three sources on the serializer:
|
|
241
|
+
|
|
242
|
+
- ActiveRecord columns — read from `db/schema.rb` via the inferred model. Column types are mapped to OpenAPI types automatically.
|
|
243
|
+
- `attribute` declarations — virtual fields not present in the database. Fields declared with `read_only: true` appear in the `User` response schema but are excluded from the `UserInput` request schema.
|
|
244
|
+
- `association` declarations — resolved as `$ref` to the associated schema. Associations declared with `read_only: true` appear in the response but are excluded from `UserInput`.
|
|
245
|
+
- `ignore` declarations — columns excluded from both schemas.
|
|
246
|
+
|
|
247
|
+
The `UserInput` schema (used in POST, PUT and PATCH request bodies) is derived automatically from the `User` schema by removing `id`, `created_at`, `updated_at`, and any field marked `read_only: true`.
|
|
248
|
+
|
|
172
249
|
```ruby
|
|
173
250
|
# app/controllers/users_controller.rb
|
|
174
251
|
def index
|
|
@@ -229,7 +306,7 @@ def show
|
|
|
229
306
|
end
|
|
230
307
|
```
|
|
231
308
|
|
|
232
|
-
Serializer registration is automatic by convention (
|
|
309
|
+
Serializer registration is automatic by convention (UserSerializer -> User). For explicit registration:
|
|
233
310
|
|
|
234
311
|
```ruby
|
|
235
312
|
class AdminUserSerializer < OpenapiBlocks::Serializer
|
|
@@ -243,34 +320,34 @@ If no serializer is found, OpenapiBlocks falls back to default Rails rendering a
|
|
|
243
320
|
|
|
244
321
|
## Serializer
|
|
245
322
|
|
|
246
|
-
The built-in serializer compiles a monolithic extractor method per class at boot time using
|
|
323
|
+
The built-in serializer compiles a monolithic extractor method per class at boot time using class_eval. There are no loops, no lambda indirection, and no runtime branching per object.
|
|
247
324
|
|
|
248
325
|
### Performance (200 records, arm64, Ruby 4.0)
|
|
249
326
|
|
|
250
|
-
| Method | i/s |
|
|
327
|
+
| Method | i/s | us/i | vs serialize |
|
|
251
328
|
| ---------- | ----- | ---- | ------------ |
|
|
252
329
|
| serialize | 4 239 | 235 | — |
|
|
253
|
-
| to_json | 1 444 | 692 | 2.
|
|
254
|
-
| as_json | 1 186 | 843 | 3.
|
|
255
|
-
| oj+as_json | 1 126 | 888 | 3.
|
|
330
|
+
| to_json | 1 444 | 692 | 2.94x slower |
|
|
331
|
+
| as_json | 1 186 | 843 | 3.58x slower |
|
|
332
|
+
| oj+as_json | 1 126 | 888 | 3.77x slower |
|
|
256
333
|
|
|
257
|
-
Scaling is linear — the 3.
|
|
334
|
+
Scaling is linear — the 3.6x advantage over as_json holds from 10 to 5000 records.
|
|
258
335
|
|
|
259
336
|
### Virtual attributes and method resolution
|
|
260
337
|
|
|
261
|
-
| Declared with
|
|
262
|
-
|
|
|
263
|
-
|
|
|
264
|
-
|
|
|
265
|
-
| column in db
|
|
338
|
+
| Declared with | Method in serializer? | Calls |
|
|
339
|
+
| -------------------- | --------------------- | ------------------------------------- |
|
|
340
|
+
| attribute :full_name | yes | serializer_instance.full_name |
|
|
341
|
+
| attribute :full_name | no | object.full_name (delegated to model) |
|
|
342
|
+
| column in db | — | object.attribute (direct) |
|
|
266
343
|
|
|
267
344
|
### Association serializer resolution
|
|
268
345
|
|
|
269
346
|
For each association, the serializer resolves the serializer class in this order:
|
|
270
347
|
|
|
271
|
-
1.
|
|
272
|
-
2.
|
|
273
|
-
3. Fallback — calls
|
|
348
|
+
1. PostSerializer — has serialize, used directly.
|
|
349
|
+
2. PostOpenapi — is a Controller, delegates to its resource.
|
|
350
|
+
3. Fallback — calls as_json on the association value.
|
|
274
351
|
|
|
275
352
|
---
|
|
276
353
|
|
|
@@ -289,14 +366,14 @@ end
|
|
|
289
366
|
|
|
290
367
|
OpenapiBlocks generates:
|
|
291
368
|
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
- All paths from
|
|
369
|
+
- User schema from db/schema.rb columns and types
|
|
370
|
+
- UserInput schema for POST, PUT and PATCH request bodies (without id, created_at, updated_at and read_only fields)
|
|
371
|
+
- required fields from presence: true validations
|
|
372
|
+
- minLength, maxLength from length validations
|
|
373
|
+
- minimum, maximum from numericality validations
|
|
374
|
+
- enum from inclusion validations
|
|
375
|
+
- format: "email" from format validations
|
|
376
|
+
- All paths from config/routes.rb
|
|
300
377
|
|
|
301
378
|
---
|
|
302
379
|
|
|
@@ -339,10 +416,10 @@ association :posts, type: :array, read_only: true # excluded from UserInput (re
|
|
|
339
416
|
|
|
340
417
|
Virtual attributes are fields that exist in the API response but not in the database.
|
|
341
418
|
|
|
342
|
-
| Option
|
|
343
|
-
|
|
|
344
|
-
|
|
|
345
|
-
|
|
|
419
|
+
| Option | Description | Appears in User | Appears in UserInput |
|
|
420
|
+
| ---------------- | -------------------------------------- | :-------------: | :------------------: |
|
|
421
|
+
| read_only: true | Calculated or system-generated fields | YES | NO |
|
|
422
|
+
| read_only: false | Fields the client can send and receive | YES | YES |
|
|
346
423
|
|
|
347
424
|
```ruby
|
|
348
425
|
attribute :full_name, type: :string, read_only: true # response only
|
|
@@ -354,19 +431,19 @@ attribute :nickname, type: :string # request and response
|
|
|
354
431
|
|
|
355
432
|
## Type Mapping
|
|
356
433
|
|
|
357
|
-
| ActiveRecord type | OpenAPI type
|
|
358
|
-
| ----------------- |
|
|
359
|
-
|
|
|
360
|
-
|
|
|
361
|
-
|
|
|
362
|
-
|
|
|
363
|
-
|
|
|
364
|
-
|
|
|
365
|
-
|
|
|
366
|
-
|
|
|
367
|
-
|
|
|
368
|
-
|
|
|
369
|
-
|
|
|
434
|
+
| ActiveRecord type | OpenAPI type |
|
|
435
|
+
| ----------------- | ------------------ |
|
|
436
|
+
| integer | integer / int32 |
|
|
437
|
+
| bigint | integer / int64 |
|
|
438
|
+
| float | number / float |
|
|
439
|
+
| decimal | number / double |
|
|
440
|
+
| string | string |
|
|
441
|
+
| text | string |
|
|
442
|
+
| boolean | boolean |
|
|
443
|
+
| date | string / date |
|
|
444
|
+
| datetime | string / date-time |
|
|
445
|
+
| uuid | string / uuid |
|
|
446
|
+
| json / jsonb | object |
|
|
370
447
|
|
|
371
448
|
---
|
|
372
449
|
|
|
@@ -382,7 +459,7 @@ config/routes.rb
|
|
|
382
459
|
db/schema.rb
|
|
383
460
|
```
|
|
384
461
|
|
|
385
|
-
The spec is automatically regenerated on the next request to
|
|
462
|
+
The spec is automatically regenerated on the next request to /docs/openapi.json whenever any of these files change. No server restart needed.
|
|
386
463
|
|
|
387
464
|
---
|
|
388
465
|
|
|
@@ -395,4 +472,4 @@ The spec is automatically regenerated on the next request to `/docs/openapi.json
|
|
|
395
472
|
|
|
396
473
|
## License
|
|
397
474
|
|
|
398
|
-
|
|
475
|
+
MIT (LICENSE.txt)
|
data/README.pt-BR.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# OpenapiBlocks
|
|
2
2
|
|
|
3
|
-
OpenapiBlocks é uma gem Rails que gera automaticamente documentação OpenAPI 3.0/3.1 a partir dos seus models ActiveRecord, validações ActiveModel e rotas do Rails — inspirada no
|
|
3
|
+
OpenapiBlocks é uma gem Rails que gera automaticamente documentação OpenAPI 3.0/3.1 a partir dos seus models ActiveRecord, validações ActiveModel e rotas do Rails — inspirada no ActiveModel::Serializer (https://github.com/rails-api/active_model_serializers).
|
|
4
4
|
|
|
5
|
-
English version:
|
|
5
|
+
English version: README.md
|
|
6
6
|
|
|
7
|
-
Sem anotações manuais. Sem DSL nos controllers. Basta declarar o que expor e a spec é gerada automaticamente. Inclui um serializer de alta performance — ~3.6× mais rápido que
|
|
7
|
+
Sem anotações manuais. Sem DSL nos controllers. Basta declarar o que expor e a spec é gerada automaticamente. Inclui um serializer de alta performance — ~3.6× mais rápido que as_json com escalabilidade linear de 10 a 5000 registros.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## Instalação
|
|
12
12
|
|
|
13
|
-
Adicione ao seu
|
|
13
|
+
Adicione ao seu Gemfile:
|
|
14
14
|
|
|
15
15
|
```ruby
|
|
16
16
|
gem "openapi_blocks"
|
|
@@ -24,6 +24,70 @@ bundle install
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
## Generators
|
|
28
|
+
|
|
29
|
+
O OpenapiBlocks oferece três generators para começar rapidamente.
|
|
30
|
+
|
|
31
|
+
### Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
rails generate openapi_blocks:install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Cria `config/initializers/openapi_blocks.rb` com todas as opções disponíveis comentadas, e monta o engine no `config/routes.rb`:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
mount OpenapiBlocks::Engine => "/docs"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Openapi
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
rails generate openapi_blocks:openapi User
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Cria `app/openapi/user_openapi.rb` com todas as opções de DSL disponíveis comentadas:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# app/openapi/user_openapi.rb
|
|
53
|
+
class UserOpenapi < OpenapiBlocks::Controller
|
|
54
|
+
# resource UserSerializer
|
|
55
|
+
# controller UsersController
|
|
56
|
+
|
|
57
|
+
# tags "Usuários"
|
|
58
|
+
|
|
59
|
+
# operation :index do
|
|
60
|
+
# summary "Lista todos os usuários"
|
|
61
|
+
# response 200, description: "Lista de usuários", schema: { type: :array, items: :User }
|
|
62
|
+
# end
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Serializer
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
rails generate openapi_blocks:serializer User
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Cria `app/serializers/user_serializer.rb` com todas as opções de DSL disponíveis comentadas:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# app/serializers/user_serializer.rb
|
|
76
|
+
class UserSerializer < OpenapiBlocks::Serializer
|
|
77
|
+
# ignore :password_digest, :reset_password_token
|
|
78
|
+
|
|
79
|
+
# association :posts, type: :array, read_only: true
|
|
80
|
+
# association :company
|
|
81
|
+
|
|
82
|
+
# attribute :full_name, type: :string, read_only: true
|
|
83
|
+
# def full_name
|
|
84
|
+
# "#{object.first_name} #{object.last_name}"
|
|
85
|
+
# end
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
27
91
|
## Configuração
|
|
28
92
|
|
|
29
93
|
### 1. Monte o Engine
|
|
@@ -48,7 +112,7 @@ GET /docs/openapi.yaml -> Spec OpenAPI em YAML
|
|
|
48
112
|
|
|
49
113
|
### 2. Configure o initializer
|
|
50
114
|
|
|
51
|
-
|
|
115
|
+
OpenapiBlocks.configure é obrigatório. A gem lança OpenapiBlocks::Error na primeira requisição se nunca foi chamado ou se info.title / info.version estiverem em branco.
|
|
52
116
|
|
|
53
117
|
```ruby
|
|
54
118
|
# config/initializers/openapi_blocks.rb
|
|
@@ -99,11 +163,11 @@ end
|
|
|
99
163
|
|
|
100
164
|
## Uso
|
|
101
165
|
|
|
102
|
-
OpenapiBlocks oferece duas classes base com responsabilidades distintas:
|
|
166
|
+
O OpenapiBlocks oferece duas classes base com responsabilidades distintas:
|
|
103
167
|
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
168
|
+
- OpenapiBlocks::Serializer — define o model, campos, associações e lógica de serialização. Fica em app/serializers/.
|
|
169
|
+
- OpenapiBlocks::Controller — define operações, parâmetros e respostas para documentação. Fica em app/openapi/.
|
|
170
|
+
- OpenapiBlocks::Base — classe base legada que combina ambas as responsabilidades. Ainda suportada.
|
|
107
171
|
|
|
108
172
|
### Recomendado: Serializer + Controller
|
|
109
173
|
|
|
@@ -142,7 +206,7 @@ end
|
|
|
142
206
|
```ruby
|
|
143
207
|
# app/openapi/user_openapi.rb
|
|
144
208
|
class UserOpenapi < OpenapiBlocks::Controller
|
|
145
|
-
resource UserSerializer
|
|
209
|
+
resource UserSerializer # vincula ao serializer — o schema é derivado dele
|
|
146
210
|
controller UsersController
|
|
147
211
|
|
|
148
212
|
tags "Usuários"
|
|
@@ -169,6 +233,19 @@ class UserOpenapi < OpenapiBlocks::Controller
|
|
|
169
233
|
end
|
|
170
234
|
```
|
|
171
235
|
|
|
236
|
+
#### Como o schema OpenAPI é gerado
|
|
237
|
+
|
|
238
|
+
Quando `resource UserSerializer` é declarado em um `Controller`, o OpenapiBlocks deriva o schema OpenAPI diretamente do serializer — não do model. Isso garante que o que está documentado é exatamente o que a API retorna.
|
|
239
|
+
|
|
240
|
+
O schema é construído a partir de três fontes no serializer:
|
|
241
|
+
|
|
242
|
+
- Colunas do ActiveRecord — lidas do `db/schema.rb` via o model inferido. Os tipos das colunas são mapeados para tipos OpenAPI automaticamente.
|
|
243
|
+
- Declarações `attribute` — campos virtuais que não existem no banco. Campos declarados com `read_only: true` aparecem no schema de resposta `User` mas são excluídos do schema de requisição `UserInput`.
|
|
244
|
+
- Declarações `association` — resolvidas como `$ref` para o schema associado. Associações com `read_only: true` aparecem na resposta mas são excluídas do `UserInput`.
|
|
245
|
+
- Declarações `ignore` — colunas excluídas de ambos os schemas.
|
|
246
|
+
|
|
247
|
+
O schema `UserInput` (usado nos request bodies de POST, PUT e PATCH) é derivado automaticamente do schema `User` removendo `id`, `created_at`, `updated_at` e qualquer campo marcado com `read_only: true`.
|
|
248
|
+
|
|
172
249
|
```ruby
|
|
173
250
|
# app/controllers/users_controller.rb
|
|
174
251
|
def index
|
|
@@ -229,7 +306,7 @@ def show
|
|
|
229
306
|
end
|
|
230
307
|
```
|
|
231
308
|
|
|
232
|
-
O registro do serializer é automático por convenção (
|
|
309
|
+
O registro do serializer é automático por convenção (UserSerializer -> User). Para registro explícito:
|
|
233
310
|
|
|
234
311
|
```ruby
|
|
235
312
|
class AdminUserSerializer < OpenapiBlocks::Serializer
|
|
@@ -243,34 +320,34 @@ Se nenhum serializer for encontrado, o OpenapiBlocks usa o comportamento padrão
|
|
|
243
320
|
|
|
244
321
|
## Serializer
|
|
245
322
|
|
|
246
|
-
O serializer compila um método extrator monolítico por classe no boot usando
|
|
323
|
+
O serializer compila um método extrator monolítico por classe no boot usando class_eval. Sem loops, sem indireção via lambda e sem branching por objeto em tempo de execução.
|
|
247
324
|
|
|
248
325
|
### Performance (200 registros, arm64, Ruby 4.0)
|
|
249
326
|
|
|
250
|
-
| Método | i/s |
|
|
251
|
-
|
|
252
|
-
| serialize | 4 239 | 235 | —
|
|
327
|
+
| Método | i/s | us/i | vs serialize |
|
|
328
|
+
| ---------- | ----- | ---- | ---------------- |
|
|
329
|
+
| serialize | 4 239 | 235 | — |
|
|
253
330
|
| to_json | 1 444 | 692 | 2.94× mais lento |
|
|
254
331
|
| as_json | 1 186 | 843 | 3.58× mais lento |
|
|
255
332
|
| oj+as_json | 1 126 | 888 | 3.77× mais lento |
|
|
256
333
|
|
|
257
|
-
A escalabilidade é linear — a vantagem de 3.6× sobre o
|
|
334
|
+
A escalabilidade é linear — a vantagem de 3.6× sobre o as_json se mantém de 10 a 5000 registros.
|
|
258
335
|
|
|
259
336
|
### Atributos virtuais e resolução de métodos
|
|
260
337
|
|
|
261
|
-
| Declarado com
|
|
262
|
-
|
|
263
|
-
|
|
|
264
|
-
|
|
|
265
|
-
| coluna no banco
|
|
338
|
+
| Declarado com | Método no serializer? | Chama |
|
|
339
|
+
| -------------------- | --------------------- | ------------------------------------ |
|
|
340
|
+
| attribute :full_name | sim | serializer_instance.full_name |
|
|
341
|
+
| attribute :full_name | não | object.full_name (delegado ao model) |
|
|
342
|
+
| coluna no banco | — | object.attribute (direto) |
|
|
266
343
|
|
|
267
344
|
### Resolução do serializer de associações
|
|
268
345
|
|
|
269
346
|
Para cada associação, o serializer resolve a classe na seguinte ordem:
|
|
270
347
|
|
|
271
|
-
1.
|
|
272
|
-
2.
|
|
273
|
-
3. Fallback — chama
|
|
348
|
+
1. PostSerializer — tem serialize, usado diretamente.
|
|
349
|
+
2. PostOpenapi — é um Controller, delega para o \_resource.
|
|
350
|
+
3. Fallback — chama as_json no valor da associação.
|
|
274
351
|
|
|
275
352
|
---
|
|
276
353
|
|
|
@@ -289,14 +366,14 @@ end
|
|
|
289
366
|
|
|
290
367
|
O OpenapiBlocks gera:
|
|
291
368
|
|
|
292
|
-
- Schema
|
|
293
|
-
- Schema
|
|
294
|
-
- Campos
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
- Todos os paths a partir do
|
|
369
|
+
- Schema User a partir das colunas e tipos do db/schema.rb
|
|
370
|
+
- Schema UserInput para os request bodies de POST, PUT e PATCH (sem id, created_at, updated_at e campos read_only)
|
|
371
|
+
- Campos required a partir das validações presence: true
|
|
372
|
+
- minLength, maxLength a partir das validações length
|
|
373
|
+
- minimum, maximum a partir das validações numericality
|
|
374
|
+
- enum a partir das validações inclusion
|
|
375
|
+
- format: "email" a partir das validações de formato
|
|
376
|
+
- Todos os paths a partir do config/routes.rb
|
|
300
377
|
|
|
301
378
|
---
|
|
302
379
|
|
|
@@ -339,10 +416,10 @@ association :posts, type: :array, read_only: true # excluído do UserInput (som
|
|
|
339
416
|
|
|
340
417
|
Atributos virtuais são campos que existem na resposta da API mas não no banco de dados.
|
|
341
418
|
|
|
342
|
-
| Opção
|
|
343
|
-
|
|
344
|
-
|
|
|
345
|
-
|
|
|
419
|
+
| Opção | Descrição | Aparece em User | Aparece em UserInput |
|
|
420
|
+
| ---------------- | ------------------------------------------ | :-------------: | :------------------: |
|
|
421
|
+
| read_only: true | Campos calculados ou gerados pelo sistema | SIM | NÃO |
|
|
422
|
+
| read_only: false | Campos que o cliente pode enviar e receber | SIM | SIM |
|
|
346
423
|
|
|
347
424
|
```ruby
|
|
348
425
|
attribute :full_name, type: :string, read_only: true # somente resposta
|
|
@@ -354,19 +431,19 @@ attribute :nickname, type: :string # requisição e respos
|
|
|
354
431
|
|
|
355
432
|
## Mapeamento de Tipos
|
|
356
433
|
|
|
357
|
-
| Tipo ActiveRecord | Tipo OpenAPI
|
|
358
|
-
|
|
359
|
-
|
|
|
360
|
-
|
|
|
361
|
-
|
|
|
362
|
-
|
|
|
363
|
-
|
|
|
364
|
-
|
|
|
365
|
-
|
|
|
366
|
-
|
|
|
367
|
-
|
|
|
368
|
-
|
|
|
369
|
-
|
|
|
434
|
+
| Tipo ActiveRecord | Tipo OpenAPI |
|
|
435
|
+
| ----------------- | ------------------ |
|
|
436
|
+
| integer | integer / int32 |
|
|
437
|
+
| bigint | integer / int64 |
|
|
438
|
+
| float | number / float |
|
|
439
|
+
| decimal | number / double |
|
|
440
|
+
| string | string |
|
|
441
|
+
| text | string |
|
|
442
|
+
| boolean | boolean |
|
|
443
|
+
| date | string / date |
|
|
444
|
+
| datetime | string / date-time |
|
|
445
|
+
| uuid | string / uuid |
|
|
446
|
+
| json / jsonb | object |
|
|
370
447
|
|
|
371
448
|
---
|
|
372
449
|
|
|
@@ -382,7 +459,7 @@ config/routes.rb
|
|
|
382
459
|
db/schema.rb
|
|
383
460
|
```
|
|
384
461
|
|
|
385
|
-
A spec é regenerada automaticamente na próxima requisição a
|
|
462
|
+
A spec é regenerada automaticamente na próxima requisição a /docs/openapi.json sempre que algum desses arquivos for alterado. Sem precisar reiniciar o servidor.
|
|
386
463
|
|
|
387
464
|
---
|
|
388
465
|
|
|
@@ -395,4 +472,4 @@ A spec é regenerada automaticamente na próxima requisição a `/docs/openapi.j
|
|
|
395
472
|
|
|
396
473
|
## Licença
|
|
397
474
|
|
|
398
|
-
|
|
475
|
+
MIT (LICENSE.txt)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base # rubocop:disable Style/Documentation
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Creates an OpenapiBlocks initializer and mounts the engine in routes.rb"
|
|
9
|
+
|
|
10
|
+
def create_initializer
|
|
11
|
+
template "initializer.rb.tt", "config/initializers/openapi_blocks.rb"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def mount_engine
|
|
15
|
+
route 'mount OpenapiBlocks::Engine => "/docs"'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
OpenapiBlocks.configure do |config|
|
|
4
|
+
# config.openapi_version = "3.1.0" # required — "3.0.3" or "3.1.0"
|
|
5
|
+
|
|
6
|
+
# config.auto_serialize = true # default is false
|
|
7
|
+
|
|
8
|
+
config.info do
|
|
9
|
+
title "My API" # required
|
|
10
|
+
version "1.0.0" # required
|
|
11
|
+
description "API documentation generated automatically"
|
|
12
|
+
|
|
13
|
+
# contact do
|
|
14
|
+
# name "My Team"
|
|
15
|
+
# email "api@mycompany.com"
|
|
16
|
+
# url "https://mycompany.com"
|
|
17
|
+
# end
|
|
18
|
+
|
|
19
|
+
# license do
|
|
20
|
+
# name "MIT"
|
|
21
|
+
# url "https://opensource.org/licenses/MIT"
|
|
22
|
+
# end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# config.servers do
|
|
26
|
+
# server do
|
|
27
|
+
# url "http://localhost:3000"
|
|
28
|
+
# description "Development"
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
|
|
32
|
+
# config.security do
|
|
33
|
+
# bearer_token format: "JWT" # Authorization: Bearer <token>
|
|
34
|
+
# api_key name: "X-API-Key", in: :header # X-API-Key: <key>
|
|
35
|
+
# end
|
|
36
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Generators
|
|
5
|
+
class OpenapiGenerator < Rails::Generators::NamedBase # rubocop:disable Style/Documentation
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Creates an OpenapiBlocks Controller class in app/openapi/"
|
|
9
|
+
|
|
10
|
+
def create_openapi_file
|
|
11
|
+
template "openapi.rb.tt", "app/openapi/#{file_name}_openapi.rb"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>Openapi < OpenapiBlocks::Controller
|
|
4
|
+
# resource <%= class_name %>Serializer
|
|
5
|
+
# controller <%= class_name %>Controller
|
|
6
|
+
|
|
7
|
+
# tags "<%= class_name.pluralize %>"
|
|
8
|
+
|
|
9
|
+
# operation :index do
|
|
10
|
+
# summary "List all <%= class_name.pluralize.downcase %>"
|
|
11
|
+
# description "Returns a paginated list of <%= class_name.pluralize.downcase %>"
|
|
12
|
+
#
|
|
13
|
+
# parameter :page, in: :query, type: :integer, description: "Page number"
|
|
14
|
+
# parameter :per_page, in: :query, type: :integer, description: "Items per page"
|
|
15
|
+
#
|
|
16
|
+
# response 200, description: "List of <%= class_name.pluralize.downcase %>", schema: { type: :array, items: :<%= class_name %> }
|
|
17
|
+
# response 401, description: "Unauthorized"
|
|
18
|
+
# end
|
|
19
|
+
|
|
20
|
+
# operation :show do
|
|
21
|
+
# summary "Get a <%= class_name.downcase %>"
|
|
22
|
+
#
|
|
23
|
+
# response 200, description: "<%= class_name %> found", schema: :<%= class_name %>
|
|
24
|
+
# response 404, description: "<%= class_name %> not found"
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
# operation :create do
|
|
28
|
+
# summary "Create a <%= class_name.downcase %>"
|
|
29
|
+
#
|
|
30
|
+
# response 201, description: "<%= class_name %> created", schema: :<%= class_name %>
|
|
31
|
+
# response 422, description: "Invalid data"
|
|
32
|
+
# end
|
|
33
|
+
|
|
34
|
+
# operation :update do
|
|
35
|
+
# summary "Update a <%= class_name.downcase %>"
|
|
36
|
+
#
|
|
37
|
+
# response 200, description: "<%= class_name %> updated", schema: :<%= class_name %>
|
|
38
|
+
# response 404, description: "<%= class_name %> not found"
|
|
39
|
+
# response 422, description: "Invalid data"
|
|
40
|
+
# end
|
|
41
|
+
|
|
42
|
+
# operation :destroy do
|
|
43
|
+
# summary "Delete a <%= class_name.downcase %>"
|
|
44
|
+
#
|
|
45
|
+
# response 200, description: "<%= class_name %> deleted"
|
|
46
|
+
# response 404, description: "<%= class_name %> not found"
|
|
47
|
+
# end
|
|
48
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Generators
|
|
5
|
+
class SerializerGenerator < Rails::Generators::NamedBase # rubocop:disable Style/Documentation
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Creates an OpenapiBlocks Serializer class in app/serializers/"
|
|
9
|
+
|
|
10
|
+
def create_serializer_file
|
|
11
|
+
template "serializer.rb.tt", "app/serializers/#{file_name}_serializer.rb"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>Serializer < OpenapiBlocks::Serializer
|
|
4
|
+
# model <%= class_name %> is inferred automatically from the class name
|
|
5
|
+
|
|
6
|
+
# ignore :password_digest, :reset_password_token
|
|
7
|
+
|
|
8
|
+
# association :posts, type: :array, read_only: true
|
|
9
|
+
# association :company
|
|
10
|
+
|
|
11
|
+
# attribute :full_name, type: :string, read_only: true
|
|
12
|
+
# def full_name
|
|
13
|
+
# "#{object.first_name} #{object.last_name}"
|
|
14
|
+
# end
|
|
15
|
+
end
|
|
@@ -4,6 +4,12 @@ require "rails"
|
|
|
4
4
|
|
|
5
5
|
module OpenapiBlocks
|
|
6
6
|
class Railtie < Rails::Railtie # rubocop:disable Style/Documentation
|
|
7
|
+
generators do
|
|
8
|
+
require "generators/openapi_blocks/install/install_generator"
|
|
9
|
+
require "generators/openapi_blocks/openapi/openapi_generator"
|
|
10
|
+
require "generators/openapi_blocks/serializer/serializer_generator"
|
|
11
|
+
end
|
|
12
|
+
|
|
7
13
|
initializer "openapi_blocks.autoload", before: :set_autoload_paths do |app|
|
|
8
14
|
app.config.eager_load_paths << app.root.join("app/openapi")
|
|
9
15
|
app.config.eager_load_paths << app.root.join("app/serializers")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openapi_blocks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Caio Santos
|
|
@@ -139,6 +139,12 @@ files:
|
|
|
139
139
|
- Rakefile
|
|
140
140
|
- app/controllers/openapi_blocks/spec_controller.rb
|
|
141
141
|
- config/routes.rb
|
|
142
|
+
- lib/generators/openapi_blocks/install/install_generator.rb
|
|
143
|
+
- lib/generators/openapi_blocks/install/templates/initializer.rb.tt
|
|
144
|
+
- lib/generators/openapi_blocks/openapi/openapi_generator.rb
|
|
145
|
+
- lib/generators/openapi_blocks/openapi/templates/openapi.rb.tt
|
|
146
|
+
- lib/generators/openapi_blocks/serializer/serializer_generator.rb
|
|
147
|
+
- lib/generators/openapi_blocks/serializer/templates/serializer.rb.tt
|
|
142
148
|
- lib/openapi_blocks.rb
|
|
143
149
|
- lib/openapi_blocks/auto_serialize.rb
|
|
144
150
|
- lib/openapi_blocks/base.rb
|