openapi_blocks 0.2.0 → 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.
data/README.pt-BR.md CHANGED
@@ -4,11 +4,35 @@ OpenapiBlocks é uma gem Rails que gera automaticamente documentação OpenAPI 3
4
4
 
5
5
  Sem anotações manuais. Sem ruído de DSL nos controllers. Basta declarar o que deve ser exposto e o spec é gerado automaticamente.
6
6
 
7
+ ## Principais mudanças (recentes)
8
+ - Versão padrão do OpenAPI: `3.1.0` (suportado: `3.1.0`, `3.0.3`).
9
+ - A Swagger UI é servida no caminho onde a engine foi montada e usa endpoints do mesmo origin (same-origin) para evitar CORS — a UI mostra uma lista de servidores, mas buscará o spec a partir da URL montada.
10
+ - A saída YAML é normalizada para chaves em string (`deep_stringify_keys`) para que o campo `openapi` seja reconhecido pelo Swagger UI.
11
+ - O DSL `association` usa `read_only: true` para marcar associações como somente resposta e excluí-las dos schemas `*Input`; associações/atributos `read_only` continuam presentes nas respostas.
12
+ - O `tags` é gerado no nível do documento a partir dos paths e pode ser customizado via `tags` nas classes e operações.
13
+ - Referências de schema aceitam `Symbol` (ex.: `schema: :user`) e arrays com items como símbolos (ex.: `items: :user`).
14
+ # OpenapiBlocks
15
+
16
+ OpenapiBlocks é uma gem Rails que gera automaticamente documentação OpenAPI 3.0/3.1 a partir dos seus modelos ActiveRecord, validações do ActiveModel e rotas do Rails — inspirada em ActiveModel::Serializer.
17
+
18
+ Sem anotações manuais. Sem ruído de DSL nos controllers. Basta declarar o que deve ser exposto e o spec é gerado automaticamente. Inclui um serializer interno de alto desempenho — aproximadamente 3.6× mais rápido que `as_json` com escalabilidade linear consistente.
19
+
20
+ ## Principais mudanças (recentes)
21
+ - `OpenapiBlocks::Resource` e `OpenapiBlocks::Controller` foram introduzidos para separar responsabilidades de serialização e documentação.
22
+ - Versão padrão do OpenAPI: `3.1.0` (suportado: `3.1.0`, `3.0.3`).
23
+ - Scalar UI agora é servido em `/docs/scalar` ao lado da Swagger UI em `/docs`.
24
+ - A Swagger UI usa endpoints same-origin para evitar problemas de CORS ao usar "Try it out"; a UI mostra servidores configurados, mas busca o spec a partir da URL montada da engine.
25
+ - A saída YAML é normalizada para chaves em string (`deep_stringify_keys`) para que o campo `openapi` seja reconhecido pelo Swagger UI.
26
+ - O DSL `association` utiliza `read_only: true` para marcar associações como somente-resposta e excluí-las dos schemas `*Input`; atributos/associações `read_only` continuam presentes em respostas.
27
+ - `tags` são gerados no nível do documento a partir dos paths e podem ser customizados via `tags` nas classes e operações.
28
+ - Referências de schema aceitam `Symbol` (ex.: `schema: :user`) e arrays com `items` como símbolos (ex.: `items: :user`).
29
+ - O serializer compila um método extrator monolítico por classe em tempo de boot usando `class_eval`, eliminando ramificações por objeto e chamadas lambda em tempo de execução.
30
+
7
31
  ---
8
32
 
9
33
  ## Instalação
10
34
 
11
- Adicione ao seu `Gemfile`:
35
+ Adicione ao seu Gemfile:
12
36
 
13
37
  ```ruby
14
38
  gem "openapi_blocks"
@@ -28,468 +52,331 @@ bundle install
28
52
 
29
53
  ```ruby
30
54
  # config/routes.rb
31
- ```
32
-
33
- ```ruby
34
55
  Rails.application.routes.draw do
35
56
  mount OpenapiBlocks::Engine => "/docs"
57
+
36
58
  resources :users
37
59
  end
38
60
  ```
39
61
 
40
62
  Isso expõe:
41
63
 
42
- <br />
43
- GET /docs/openapi.json
44
-
45
- <br />
46
- GET /docs/openapi.yaml
64
+ ```
65
+ GET /docs -> Scalar UI
66
+ GET /docs/swagger -> Swagger UI
67
+ GET /docs/openapi.json -> OpenAPI spec in JSON
68
+ GET /docs/openapi.yaml -> OpenAPI spec in YAML
69
+ ```
47
70
 
48
71
  ### 2. Configure o initializer
49
72
 
50
73
  ```ruby
51
74
  # config/initializers/openapi_blocks.rb
52
-
53
75
  OpenapiBlocks.configure do |config|
54
- # Versões suportadas: "3.1.0" e "3.0.3". O padrão desta gem é "3.1.0".
55
- config.openapi_version = "3.1.0" # "3.0.3" ou "3.1.0"
76
+ config.openapi_version = "3.1.0" # "3.0.3" ou "3.1.0"
77
+
56
78
  config.info do
57
79
  title "Minha API"
58
80
  version "1.0.0"
59
81
  description "Documentação da API gerada automaticamente"
82
+
60
83
  contact do
61
84
  name "Minha equipe"
62
85
  email "api@mycompany.com"
63
86
  url "https://mycompany.com"
64
87
  end
88
+
65
89
  license do
66
90
  name "MIT"
67
91
  url "https://opensource.org/licenses/MIT"
68
92
  end
69
93
  end
94
+
70
95
  config.servers do
71
96
  server do
72
97
  url "https://api.mycompany.com"
73
98
  description "Produção"
74
99
  end
100
+
75
101
  server do
76
102
  url "http://localhost:3000"
77
103
  description "Desenvolvimento"
78
104
  end
79
105
  end
80
- config.watch = :development # recarrega automaticamente em mudanças de arquivo
81
- end
82
- ```
83
106
 
84
- Observações:
107
+ config.watch = :development # auto-reload em mudanças de arquivo
85
108
 
86
- - A interface Swagger UI fornecida pela engine prioriza a origem atual da
87
- requisição (same-origin) como servidor primário para evitar problemas de
88
- CORS ao usar o recurso "Try it out". Você ainda pode listar outros
89
- servidores em `config.servers` apenas para fins informacionais; a UI
90
- buscará o documento OpenAPI a partir da URL do spec na mesma origem,
91
- construída automaticamente com o prefixo usado ao montar a engine.
109
+ # opcional: esquemas de segurança
110
+ config.security do
111
+ bearer_token format: "JWT"
112
+ api_key name: "X-API-Key", in: :header
113
+ end
114
+ end
115
+ ```
92
116
 
93
117
  ---
94
118
 
95
119
  ## Uso
96
120
 
97
- ### Criando uma classe OpenAPI
121
+ OpenapiBlocks fornece duas classes base com responsabilidades distintas:
98
122
 
99
- Crie um arquivo em `app/openapi/` seguindo a mesma convenção de nomes do ActiveModel::Serializer:
123
+ - `OpenapiBlocks::Resource` define o model, campos, associações e lógica de serialização.
124
+ - `OpenapiBlocks::Controller` — define operações da API, parâmetros e respostas para documentação.
125
+ - `OpenapiBlocks::Base` — classe legada que combina ambas as responsabilidades. Ainda suportada.
100
126
 
101
- ```text
127
+ ### Resource + Controller (recomendado)
128
+
129
+ ```
102
130
  app/
103
- openapi/
104
- user_openapi.rb User model
105
- post_openapi.rb Post model
106
- order_openapi.rb → Order model
131
+ openapi/
132
+ user_resource.rb -> serialização + schema
133
+ user_openapi.rb -> documentação da API
134
+ post_resource.rb
135
+ post_openapi.rb
107
136
  ```
108
137
 
109
138
  ```ruby
110
- # app/openapi/user_openapi.rb
111
-
112
- class UserOpenapi < OpenapiBlocks::Base
139
+ # app/openapi/user_resource.rb
140
+ class UserResource < OpenapiBlocks::Resource
113
141
  # o model User é inferido automaticamente pelo nome da classe
114
142
 
115
- # ignora campos sensíveis ou desnecessários
116
143
  ignore :password_digest, :reset_password_token
117
144
 
118
- # associações opt-in
119
- association :company
120
- association :posts, type: :array
121
-
122
- # atributos virtuais (não existem no banco)
123
- attribute :full_name, type: :string
124
- attribute :token, type: :string, read_only: true
125
- end
126
- ```
145
+ association :posts, type: :array, read_only: true
127
146
 
128
- ### O que é gerado automaticamente
147
+ attribute :full_name, type: :string, read_only: true
148
+ attribute :access_token, type: :string, read_only: true
149
+ attribute :nickname, type: :string
129
150
 
130
- Dado este model:
151
+ # método definido aqui — chamado na instância do recurso
152
+ def full_name
153
+ "#{object.name} (#{object.email})"
154
+ end
131
155
 
132
- ```ruby
133
- class User < ApplicationRecord
134
- validates :name, presence: true, length: { minimum: 2, maximum: 100 }
135
- validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
136
- validates :age, numericality: { greater_than: 0 }
137
- validates :role, inclusion: { in: %w[admin user guest] }
156
+ # ou omita o método e ele será delegado ao model automaticamente
138
157
  end
139
158
  ```
140
159
 
141
- OpenapiBlocks gera:
142
-
143
- - `User` schema a partir das colunas e tipos de `db/schema.rb`
144
- - `UserInput` schema para bodies de request de `POST`, `PUT` e `PATCH` (sem `id`, `created_at`, `updated_at`)
145
- - `UserInput` schema para bodies de request de `POST`, `PUT` e `PATCH` (sem `id`, `created_at`, `updated_at` e atributos virtuais marcados como `read_only`)
146
- - campos `required` a partir de validações `presence: true`
147
- - `minLength` e `maxLength` a partir de validações `length`
148
- - `minimum` e `maximum` a partir de validações `numericality`
149
- - `enum` a partir de validações `inclusion`
150
- - `format: "email"` a partir de validações de formato
151
- - todos os paths a partir de `config/routes.rb`
152
-
153
- ### Atributos Virtuais
154
-
155
- Atributos virtuais são campos que existem apenas na resposta da API e não no banco de dados.
156
-
157
- | Opção | Descrição | Aparece em User | Aparece em UserInput |
158
- | ---------------- | ------------------------------------------ | :-------------: | :------------------: |
159
- | read_only: true | Campos calculados ou gerados pelo sistema | SIM | NÃO |
160
- | read_only: false | Campos que o cliente pode enviar e receber | SIM | SIM |
161
-
162
160
  ```ruby
163
- attribute :full_name, type: :string, read_only: true # apenas resposta
164
- attribute :access_token, type: :string, read_only: true # apenas resposta
165
- attribute :nickname, type: :string # request e response
166
- ```
167
-
168
- ### Customizando operações
169
-
170
- ````ruby
171
161
  # app/openapi/user_openapi.rb
172
- class UserOpenapi < OpenapiBlocks::Base
173
- operation :index do
174
- summary "Listar todos os usuários"
175
- description "Retorna uma lista paginada de usuários ativos"
176
- parameter :page, in: :query, type: :integer, description: "Número da página"
177
- parameter :per_page, in: :query, type: :integer, description: "Itens por página"
178
- response 200, description: "Lista de usuários", schema: { type: :array, items: :User }
179
- response 401, description: "Não autorizado"
180
- end
181
-
182
- operation :show do
183
- summary "Buscar um usuário"
184
- response 200, description: "Usuário encontrado", schema: :User
185
- response 404, description: "Usuário não encontrado"
186
- end
187
-
188
- operation :create do
189
- # OpenapiBlocks
190
-
191
- OpenapiBlocks é uma gem Rails que gera automaticamente documentação OpenAPI 3.0/3.1 a partir dos seus modelos ActiveRecord, validações do ActiveModel e rotas do Rails, inspirada em ActiveModel::Serializer.
192
-
193
- Sem anotações manuais. Sem ruído de DSL nos controllers. Basta declarar o que deve ser exposto e o spec é gerado automaticamente.
194
-
195
- ---
196
-
197
- ## Instalação
198
-
199
- Adicione ao seu `Gemfile`:
200
-
201
- ```ruby
202
- gem "openapi_blocks"
203
- ```
204
-
205
- Depois execute:
206
-
207
- ```bash
208
- bundle install
209
- ```
162
+ class UserOpenapi < OpenapiBlocks::Controller
163
+ resource UserResource
210
164
 
211
- ---
165
+ tags "Users"
212
166
 
213
- ## Configuração
214
-
215
- ### 1. Monte a Engine
216
-
217
- ```ruby
218
- # config/routes.rb
219
- Rails.application.routes.draw do
220
- mount OpenapiBlocks::Engine => "/docs"
167
+ operation :index do
168
+ summary "List all users"
169
+ description "Returns a paginated list of active users"
221
170
 
222
- resources :users
223
- end
224
- ```
225
-
226
- Isso expõe:
227
-
228
- ```
229
- GET /docs -> Swagger UI
230
- GET /docs/openapi.json -> OpenAPI spec em JSON
231
- GET /docs/openapi.yaml -> OpenAPI spec em YAML
232
- ```
233
-
234
- ### 2. Configure o initializer
235
-
236
- ```ruby
237
- # config/initializers/openapi_blocks.rb
238
- OpenapiBlocks.configure do |config|
239
- # Versões suportadas: "3.1.0" e "3.0.3". O padrão desta gem é "3.1.0".
240
- config.openapi_version = "3.1.0"
241
-
242
- config.info do
243
- title "Minha API"
244
- version "1.0.0"
245
- description "Documentação da API gerada automaticamente"
246
-
247
- contact do
248
- name "Minha equipe"
249
- email "api@mycompany.com"
250
- url "https://mycompany.com"
251
- end
252
-
253
- license do
254
- name "MIT"
255
- url "https://opensource.org/licenses/MIT"
256
- end
257
- end
258
-
259
- config.servers do
260
- server do
261
- url "https://api.mycompany.com"
262
- description "Produção"
263
- end
264
-
265
- server do
266
- url "http://localhost:3000"
267
- description "Desenvolvimento"
268
- end
269
- end
270
-
271
- config.watch = :development # recarrega automaticamente em mudanças de arquivo
272
-
273
- # opcional: esquemas de segurança
274
- config.security do
275
- bearer_token format: "JWT"
276
- api_key name: "X-API-Key", in: :header
277
- end
278
- end
279
- ```
171
+ parameter :page, in: :query, type: :integer, description: "Page number"
172
+ parameter :per_page, in: :query, type: :integer, description: "Items per page"
280
173
 
281
- Observações:
282
- - A interface Swagger UI fornecida pela engine prioriza a origem atual da requisição (same-origin) como servidor primário para evitar problemas de CORS ao usar o recurso "Try it out". Você ainda pode listar outros servidores em `config.servers` apenas para fins informacionais; a UI buscará o documento OpenAPI a partir da URL do spec na mesma origem, construída automaticamente com o prefixo usado ao montar a engine.
174
+ response 200, description: "List of users", schema: { type: :array, items: :User }
175
+ response 401, description: "Unauthorized"
176
+ end
283
177
 
284
- ---
178
+ operation :show do
179
+ summary "Get a user"
285
180
 
286
- ## Uso
181
+ response 200, description: "User found", schema: :User
182
+ response 404, description: "User not found"
287
183
 
288
- ### Criando uma classe OpenAPI
184
+ no_security!
185
+ end
186
+ end
187
+ ```
289
188
 
290
- Crie um arquivo em `app/openapi/` seguindo a mesma convenção de nomes do ActiveModel::Serializer:
189
+ ```ruby
190
+ # app/controllers/users_controller.rb
191
+ def index
192
+ render json: UserResource.serialize(User.includes(:posts))
193
+ end
291
194
 
292
- ```
293
- app/
294
- openapi/
295
- user_openapi.rb -> User model
296
- post_openapi.rb -> Post model
297
- order_openapi.rb -> Order model
298
- ```
195
+ def show
196
+ render json: UserResource.serialize(User.find(params[:id]))
197
+ end
198
+ ```
299
199
 
300
- ```ruby
301
- # app/openapi/user_openapi.rb
302
- class UserOpenapi < OpenapiBlocks::Base
303
- # model User é inferido automaticamente a partir do nome da classe
200
+ ### Base (legado, classe única)
304
201
 
305
- # tags customizadas (padrão: inferido a partir do nome do controller/schema)
306
- tags "Users"
202
+ ```ruby
203
+ # app/openapi/user_openapi.rb
204
+ class UserOpenapi < OpenapiBlocks::Base
205
+ tags "Users"
307
206
 
308
- # ignora campos sensíveis ou desnecessários
309
- ignore :password_digest, :reset_password_token
207
+ ignore :password_digest
310
208
 
311
- # associações opt-in
312
- association :company
313
- association :posts, type: :array, input: false # excluído do UserInput
209
+ association :posts, type: :array, read_only: true
314
210
 
315
- # atributos virtuais (não existem no banco de dados)
316
- # read_only: true -> exposto apenas na resposta (User)
317
- # read_only: false -> exposto em User e UserInput
318
- attribute :full_name, type: :string, read_only: true
319
- attribute :access_token, type: :string, read_only: true
320
- attribute :nickname, type: :string
321
- end
322
- ```
211
+ attribute :full_name, type: :string, read_only: true
323
212
 
324
- ### O que é gerado automaticamente
213
+ operation :index do
214
+ summary "List all users"
215
+ response 200, description: "List of users", schema: { type: :array, items: :User }
216
+ end
217
+ end
218
+ ```
325
219
 
326
- Dado este model:
220
+ ```ruby
221
+ # app/controllers/users_controller.rb
222
+ def index
223
+ render json: UserOpenapi.serialize(User.includes(:posts))
224
+ end
225
+ ```
327
226
 
328
- ```ruby
329
- class User < ApplicationRecord
330
- validates :name, presence: true, length: { minimum: 2, maximum: 100 }
331
- validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
332
- validates :age, numericality: { greater_than: 0 }
333
- validates :role, inclusion: { in: %w[admin user guest] }
334
- end
335
- ```
227
+ ---
336
228
 
337
- OpenapiBlocks gera:
229
+ ## Serializer
338
230
 
339
- - `User` schema a partir das colunas e tipos de `db/schema.rb`
340
- - `UserInput` schema para bodies de request de `POST`, `PUT` e `PATCH` (sem `id`, `created_at`, `updated_at` e atributos virtuais marcados como `read_only`)
341
- - campos `required` a partir de validações `presence: true`
342
- - `minLength` e `maxLength` a partir de validações `length`
343
- - `minimum` e `maximum` a partir de validações `numericality`
344
- - `enum` a partir de validações `inclusion`
345
- - `format: "email"` a partir de validações de formato
346
- - todos os paths a partir de `config/routes.rb`
231
+ O serializer interno compila um método extrator monolítico por classe em tempo de boot usando `class_eval`. Não loops, nem indirection por lambda e nem ramificações em tempo de execução por objeto.
347
232
 
348
- ### Customizando operações
233
+ ### Performance (200 registros, arm64, Ruby 4.0)
349
234
 
350
- ```ruby
351
- # app/openapi/user_openapi.rb
352
- class UserOpenapi < OpenapiBlocks::Base
353
- tags "Users"
235
+ | Método | i/s | μs/i | vs serialize |
236
+ |---|---:|---:|---:|
237
+ | serialize | 4 239 | 235 | — |
238
+ | to_json | 1 444 | 692 | 2.94× mais lento |
239
+ | as_json | 1 186 | 843 | 3.58× mais lento |
240
+ | oj+as_json | 1 126 | 888 | 3.77× mais lento |
354
241
 
355
- operation :index do
356
- summary "List all users"
357
- description "Returns a paginated list of active users"
358
- tags "Users", "Admin" # sobrescreve tags em nível de operação
242
+ Escalamento é linear — a vantagem ~3.6× em relação a `as_json` se mantém de 10 a 5000 registros.
359
243
 
360
- parameter :page, in: :query, type: :integer, description: "Page number"
361
- parameter :per_page, in: :query, type: :integer, description: "Items per page"
244
+ ### Atributos virtuais e resolução de método
362
245
 
363
- response 200, description: "List of users", schema: { type: :array, items: :User }
364
- response 401, description: "Unauthorized"
365
- end
246
+ | Declarado com | Método no resource? | Chamada |
247
+ |---|---:|---|
248
+ | `attribute :full_name` | sim | `resource_instance.full_name` |
249
+ | `attribute :full_name` | não | `object.full_name` (delegado ao model) |
250
+ | coluna no db | — | `object.full_name` (direto) |
366
251
 
367
- operation :show do
368
- summary "Get a user"
252
+ ### Resolução de serializer de associação
369
253
 
370
- response 200, description: "User found", schema: :User
371
- response 404, description: "User not found"
372
- end
254
+ Para cada associação, a resolução procura na ordem:
373
255
 
374
- operation :create do
375
- summary "Create a user"
256
+ 1. `PostResource` — se existir `serialize`, é usado diretamente.
257
+ 2. `PostOpenapi` se for um `Controller`, delega ao seu `_resource`.
258
+ 3. Fallback — chama `as_json` no valor da associação.
376
259
 
377
- response 201, description: "User created", schema: :User
378
- response 422, description: "Invalid data"
379
- end
260
+ ---
380
261
 
381
- operation :update do
382
- summary "Update a user"
262
+ ## O que é gerado automaticamente
383
263
 
384
- response 200, description: "User updated", schema: :User
385
- response 404, description: "User not found"
386
- response 422, description: "Invalid data"
387
- end
264
+ Dado este model:
388
265
 
389
- operation :destroy do
390
- summary "Delete a user"
266
+ ```ruby
267
+ class User < ApplicationRecord
268
+ validates :name, presence: true, length: { minimum: 2, maximum: 100 }
269
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
270
+ validates :age, numericality: { greater_than: 0 }
271
+ validates :role, inclusion: { in: %w[admin user guest] }
272
+ end
273
+ ```
391
274
 
392
- response 200, description: "User deleted"
393
- response 404, description: "User not found"
394
- end
395
- end
396
- ```
275
+ OpenapiBlocks gera:
397
276
 
398
- ---
277
+ - `User` schema a partir de `db/schema.rb` (colunas e tipos)
278
+ - `UserInput` schema para bodies de `POST`, `PUT` e `PATCH` (sem `id`, `created_at`, `updated_at` e campos `read_only`)
279
+ - `required` a partir de validações `presence: true`
280
+ - `minLength` e `maxLength` a partir de validações `length`
281
+ - `minimum` e `maximum` a partir de validações `numericality`
282
+ - `enum` a partir de validações `inclusion`
283
+ - `format: "email"` a partir de validações de formato
284
+ - Todos os paths a partir de `config/routes.rb`
399
285
 
400
- ## Segurança
286
+ ---
401
287
 
402
- Configure esquemas de segurança globais no initializer:
288
+ ## Segurança
403
289
 
404
- ```ruby
405
- config.security do
406
- bearer_token format: "JWT" # Authorization: Bearer <token>
407
- api_key name: "X-API-Key", in: :header # X-API-Key: <key>
408
- end
409
- ```
290
+ Configure esquemas de segurança globais no initializer:
410
291
 
411
- Substitua a segurança por operação:
292
+ ```ruby
293
+ config.security do
294
+ bearer_token format: "JWT" # Authorization: Bearer <token>
295
+ api_key name: "X-API-Key", in: :header # X-API-Key: <key>
296
+ end
297
+ ```
412
298
 
413
- ```ruby
414
- operation :index do
415
- security :bearerAuth # apenas bearer nesta operação
416
- end
299
+ Substitua a segurança por operação:
417
300
 
418
- operation :show do
419
- no_security! # endpoint público — sem autenticação
420
- end
421
- ```
301
+ ```ruby
302
+ operation :index do
303
+ security :bearerAuth # apenas bearer nesta operação
304
+ end
422
305
 
423
- ---
306
+ operation :show do
307
+ no_security! # endpoint público — sem autenticação
308
+ end
309
+ ```
424
310
 
425
- ## Associações
311
+ ---
426
312
 
427
- ```ruby
428
- association :company # belongs_to — $ref para Company schema
429
- association :posts, type: :array # has_many — array de $ref para Post schema
430
- association :posts, type: :array, input: false # excluído do UserInput (response only)
431
- ```
313
+ ## Associações
432
314
 
433
- ---
315
+ ```ruby
316
+ association :company # belongs_to — $ref para Company schema
317
+ association :posts, type: :array # has_many — array de $ref para Post schema
318
+ association :posts, type: :array, read_only: true # excluído do UserInput (response only)
319
+ ```
434
320
 
435
- ## Atributos Virtuais
321
+ ---
436
322
 
437
- Atributos virtuais são campos que existem apenas na resposta da API e não no banco de dados.
323
+ ## Atributos Virtuais
438
324
 
439
- | Opção | Descrição | Aparece em User | Aparece em UserInput |
440
- | ---------------- | -------------------------------------- | :-------------: | :------------------: |
441
- | read_only: true | Campos calculados ou gerados pelo sistema | SIM | NÃO |
442
- | read_only: false | Campos que o cliente pode enviar e receber | SIM | SIM |
325
+ Atributos virtuais são campos que existem apenas na resposta da API e não no banco de dados.
443
326
 
444
- ```ruby
445
- attribute :full_name, type: :string, read_only: true # response only
446
- attribute :access_token, type: :string, read_only: true # response only
447
- attribute :nickname, type: :string # request and response
448
- ```
327
+ | Opção | Descrição | Aparece em User | Aparece em UserInput |
328
+ |---|---|:---:|:---:|
329
+ | `read_only: true` | Campos calculados ou gerados pelo sistema | SIM | NÃO |
330
+ | `read_only: false` | Campos que o cliente pode enviar e receber | SIM | SIM |
449
331
 
450
- ---
332
+ ```ruby
333
+ attribute :full_name, type: :string, read_only: true # response only
334
+ attribute :access_token, type: :string, read_only: true # response only
335
+ attribute :nickname, type: :string # request and response
336
+ ```
451
337
 
452
- ## Mapeamento de tipos
338
+ ---
453
339
 
454
- | Tipo do ActiveRecord | Tipo OpenAPI |
455
- |----------------------|-------------------------|
456
- | integer | integer / int32 |
457
- | bigint | integer / int64 |
458
- | float | number / float |
459
- | decimal | number / double |
460
- | string | string |
461
- | text | string |
462
- | boolean | boolean |
463
- | date | string / date |
464
- | datetime | string / date-time |
465
- | uuid | string / uuid |
466
- | json / jsonb | object |
340
+ ## Mapeamento de tipos
341
+
342
+ | Tipo do ActiveRecord | Tipo OpenAPI |
343
+ |---|---|
344
+ | integer | integer / int32 |
345
+ | bigint | integer / int64 |
346
+ | float | number / float |
347
+ | decimal | number / double |
348
+ | string | string |
349
+ | text | string |
350
+ | boolean | boolean |
351
+ | date | string / date |
352
+ | datetime | string / date-time |
353
+ | uuid | string / uuid |
354
+ | json / jsonb | object |
467
355
 
468
- ---
356
+ ---
469
357
 
470
- ## Auto-reload em desenvolvimento
358
+ ## Auto-reload em desenvolvimento
471
359
 
472
- OpenapiBlocks observa mudanças em:
360
+ OpenapiBlocks observa mudanças em:
473
361
 
474
- ```
475
- app/openapi/**/*.rb
476
- app/models/**/*.rb
477
- config/routes.rb
478
- db/schema.rb
479
- ```
362
+ ```
363
+ app/openapi/**/*.rb
364
+ app/models/**/*.rb
365
+ config/routes.rb
366
+ db/schema.rb
367
+ ```
480
368
 
481
- O spec é regenerado automaticamente na próxima requisição para `/docs/openapi.json` sempre que qualquer um desses arquivos muda. Não é necessário reiniciar o servidor.
369
+ O spec é regenerado automaticamente na próxima requisição para `/docs/openapi.json` sempre que qualquer um desses arquivos muda. Não é necessário reiniciar o servidor.
482
370
 
483
- ---
371
+ ---
484
372
 
485
- ## Requisitos
373
+ ## Requisitos
486
374
 
487
- - Ruby >= 3.2
488
- - Rails >= 7.0
375
+ - Ruby >= 3.2
376
+ - Rails >= 7.0
489
377
 
490
- ---
378
+ ---
491
379
 
492
- ## Licença
380
+ ## Licença
493
381
 
494
- [MIT](LICENSE.txt)
495
- ````
382
+ MIT (LICENSE.txt)