openapi_blocks 0.2.1 → 0.3.1

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,332 @@ 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
164
+ controller UsersController
210
165
 
211
- ---
166
+ tags "Users"
212
167
 
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"
168
+ operation :index do
169
+ summary "List all users"
170
+ description "Returns a paginated list of active users"
221
171
 
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
- ```
172
+ parameter :page, in: :query, type: :integer, description: "Page number"
173
+ parameter :per_page, in: :query, type: :integer, description: "Items per page"
280
174
 
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.
175
+ response 200, description: "List of users", schema: { type: :array, items: :User }
176
+ response 401, description: "Unauthorized"
177
+ end
283
178
 
284
- ---
179
+ operation :show do
180
+ summary "Get a user"
285
181
 
286
- ## Uso
182
+ response 200, description: "User found", schema: :User
183
+ response 404, description: "User not found"
287
184
 
288
- ### Criando uma classe OpenAPI
185
+ no_security!
186
+ end
187
+ end
188
+ ```
289
189
 
290
- Crie um arquivo em `app/openapi/` seguindo a mesma convenção de nomes do ActiveModel::Serializer:
190
+ ```ruby
191
+ # app/controllers/users_controller.rb
192
+ def index
193
+ render json: UserResource.serialize(User.includes(:posts))
194
+ end
291
195
 
292
- ```
293
- app/
294
- openapi/
295
- user_openapi.rb -> User model
296
- post_openapi.rb -> Post model
297
- order_openapi.rb -> Order model
298
- ```
196
+ def show
197
+ render json: UserResource.serialize(User.find(params[:id]))
198
+ end
199
+ ```
299
200
 
300
- ```ruby
301
- # app/openapi/user_openapi.rb
302
- class UserOpenapi < OpenapiBlocks::Base
303
- # model User é inferido automaticamente a partir do nome da classe
201
+ ### Base (legado, classe única)
304
202
 
305
- # tags customizadas (padrão: inferido a partir do nome do controller/schema)
306
- tags "Users"
203
+ ```ruby
204
+ # app/openapi/user_openapi.rb
205
+ class UserOpenapi < OpenapiBlocks::Base
206
+ tags "Users"
307
207
 
308
- # ignora campos sensíveis ou desnecessários
309
- ignore :password_digest, :reset_password_token
208
+ ignore :password_digest
310
209
 
311
- # associações opt-in
312
- association :company
313
- association :posts, type: :array, read_only: true # excluído do UserInput
210
+ association :posts, type: :array, read_only: true
314
211
 
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
- ```
212
+ attribute :full_name, type: :string, read_only: true
323
213
 
324
- ### O que é gerado automaticamente
214
+ operation :index do
215
+ summary "List all users"
216
+ response 200, description: "List of users", schema: { type: :array, items: :User }
217
+ end
218
+ end
219
+ ```
325
220
 
326
- Dado este model:
221
+ ```ruby
222
+ # app/controllers/users_controller.rb
223
+ def index
224
+ render json: UserOpenapi.serialize(User.includes(:posts))
225
+ end
226
+ ```
327
227
 
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
- ```
228
+ ---
336
229
 
337
- OpenapiBlocks gera:
230
+ ## Serializer
338
231
 
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`
232
+ 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
233
 
348
- ### Customizando operações
234
+ ### Performance (200 registros, arm64, Ruby 4.0)
349
235
 
350
- ```ruby
351
- # app/openapi/user_openapi.rb
352
- class UserOpenapi < OpenapiBlocks::Base
353
- tags "Users"
236
+ | Método | i/s | μs/i | vs serialize |
237
+ |---|---:|---:|---:|
238
+ | serialize | 4 239 | 235 | — |
239
+ | to_json | 1 444 | 692 | 2.94× mais lento |
240
+ | as_json | 1 186 | 843 | 3.58× mais lento |
241
+ | oj+as_json | 1 126 | 888 | 3.77× mais lento |
354
242
 
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
243
+ Escalamento é linear — a vantagem ~3.6× em relação a `as_json` se mantém de 10 a 5000 registros.
359
244
 
360
- parameter :page, in: :query, type: :integer, description: "Page number"
361
- parameter :per_page, in: :query, type: :integer, description: "Items per page"
245
+ ### Atributos virtuais e resolução de método
362
246
 
363
- response 200, description: "List of users", schema: { type: :array, items: :User }
364
- response 401, description: "Unauthorized"
365
- end
247
+ | Declarado com | Método no resource? | Chamada |
248
+ |---|---:|---|
249
+ | `attribute :full_name` | sim | `resource_instance.full_name` |
250
+ | `attribute :full_name` | não | `object.full_name` (delegado ao model) |
251
+ | coluna no db | — | `object.full_name` (direto) |
366
252
 
367
- operation :show do
368
- summary "Get a user"
253
+ ### Resolução de serializer de associação
369
254
 
370
- response 200, description: "User found", schema: :User
371
- response 404, description: "User not found"
372
- end
255
+ Para cada associação, a resolução procura na ordem:
373
256
 
374
- operation :create do
375
- summary "Create a user"
257
+ 1. `PostResource` — se existir `serialize`, é usado diretamente.
258
+ 2. `PostOpenapi` se for um `Controller`, delega ao seu `_resource`.
259
+ 3. Fallback — chama `as_json` no valor da associação.
376
260
 
377
- response 201, description: "User created", schema: :User
378
- response 422, description: "Invalid data"
379
- end
261
+ ---
380
262
 
381
- operation :update do
382
- summary "Update a user"
263
+ ## O que é gerado automaticamente
383
264
 
384
- response 200, description: "User updated", schema: :User
385
- response 404, description: "User not found"
386
- response 422, description: "Invalid data"
387
- end
265
+ Dado este model:
388
266
 
389
- operation :destroy do
390
- summary "Delete a user"
267
+ ```ruby
268
+ class User < ApplicationRecord
269
+ validates :name, presence: true, length: { minimum: 2, maximum: 100 }
270
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
271
+ validates :age, numericality: { greater_than: 0 }
272
+ validates :role, inclusion: { in: %w[admin user guest] }
273
+ end
274
+ ```
391
275
 
392
- response 200, description: "User deleted"
393
- response 404, description: "User not found"
394
- end
395
- end
396
- ```
276
+ OpenapiBlocks gera:
397
277
 
398
- ---
278
+ - `User` schema a partir de `db/schema.rb` (colunas e tipos)
279
+ - `UserInput` schema para bodies de `POST`, `PUT` e `PATCH` (sem `id`, `created_at`, `updated_at` e campos `read_only`)
280
+ - `required` a partir de validações `presence: true`
281
+ - `minLength` e `maxLength` a partir de validações `length`
282
+ - `minimum` e `maximum` a partir de validações `numericality`
283
+ - `enum` a partir de validações `inclusion`
284
+ - `format: "email"` a partir de validações de formato
285
+ - Todos os paths a partir de `config/routes.rb`
399
286
 
400
- ## Segurança
287
+ ---
401
288
 
402
- Configure esquemas de segurança globais no initializer:
289
+ ## Segurança
403
290
 
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
- ```
291
+ Configure esquemas de segurança globais no initializer:
410
292
 
411
- Substitua a segurança por operação:
293
+ ```ruby
294
+ config.security do
295
+ bearer_token format: "JWT" # Authorization: Bearer <token>
296
+ api_key name: "X-API-Key", in: :header # X-API-Key: <key>
297
+ end
298
+ ```
412
299
 
413
- ```ruby
414
- operation :index do
415
- security :bearerAuth # apenas bearer nesta operação
416
- end
300
+ Substitua a segurança por operação:
417
301
 
418
- operation :show do
419
- no_security! # endpoint público — sem autenticação
420
- end
421
- ```
302
+ ```ruby
303
+ operation :index do
304
+ security :bearerAuth # apenas bearer nesta operação
305
+ end
422
306
 
423
- ---
307
+ operation :show do
308
+ no_security! # endpoint público — sem autenticação
309
+ end
310
+ ```
424
311
 
425
- ## Associações
312
+ ---
426
313
 
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, read_only: true # excluído do UserInput (response only)
431
- ```
314
+ ## Associações
432
315
 
433
- ---
316
+ ```ruby
317
+ association :company # belongs_to — $ref para Company schema
318
+ association :posts, type: :array # has_many — array de $ref para Post schema
319
+ association :posts, type: :array, read_only: true # excluído do UserInput (response only)
320
+ ```
434
321
 
435
- ## Atributos Virtuais
322
+ ---
436
323
 
437
- Atributos virtuais são campos que existem apenas na resposta da API e não no banco de dados.
324
+ ## Atributos Virtuais
438
325
 
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 |
326
+ Atributos virtuais são campos que existem apenas na resposta da API e não no banco de dados.
443
327
 
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
- ```
328
+ | Opção | Descrição | Aparece em User | Aparece em UserInput |
329
+ |---|---|:---:|:---:|
330
+ | `read_only: true` | Campos calculados ou gerados pelo sistema | SIM | NÃO |
331
+ | `read_only: false` | Campos que o cliente pode enviar e receber | SIM | SIM |
449
332
 
450
- ---
333
+ ```ruby
334
+ attribute :full_name, type: :string, read_only: true # response only
335
+ attribute :access_token, type: :string, read_only: true # response only
336
+ attribute :nickname, type: :string # request and response
337
+ ```
451
338
 
452
- ## Mapeamento de tipos
339
+ ---
453
340
 
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 |
341
+ ## Mapeamento de tipos
342
+
343
+ | Tipo do ActiveRecord | Tipo OpenAPI |
344
+ |---|---|
345
+ | integer | integer / int32 |
346
+ | bigint | integer / int64 |
347
+ | float | number / float |
348
+ | decimal | number / double |
349
+ | string | string |
350
+ | text | string |
351
+ | boolean | boolean |
352
+ | date | string / date |
353
+ | datetime | string / date-time |
354
+ | uuid | string / uuid |
355
+ | json / jsonb | object |
467
356
 
468
- ---
357
+ ---
469
358
 
470
- ## Auto-reload em desenvolvimento
359
+ ## Auto-reload em desenvolvimento
471
360
 
472
- OpenapiBlocks observa mudanças em:
361
+ OpenapiBlocks observa mudanças em:
473
362
 
474
- ```
475
- app/openapi/**/*.rb
476
- app/models/**/*.rb
477
- config/routes.rb
478
- db/schema.rb
479
- ```
363
+ ```
364
+ app/openapi/**/*.rb
365
+ app/models/**/*.rb
366
+ config/routes.rb
367
+ db/schema.rb
368
+ ```
480
369
 
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.
370
+ 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
371
 
483
- ---
372
+ ---
484
373
 
485
- ## Requisitos
374
+ ## Requisitos
486
375
 
487
- - Ruby >= 3.2
488
- - Rails >= 7.0
376
+ - Ruby >= 3.2
377
+ - Rails >= 7.0
489
378
 
490
- ---
379
+ ---
491
380
 
492
- ## Licença
381
+ ## Licença
493
382
 
494
- [MIT](LICENSE.txt)
495
- ````
383
+ MIT (LICENSE.txt)