openapi_blocks 0.2.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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +86 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +304 -0
  6. data/README.pt-BR.md +495 -0
  7. data/Rakefile +12 -0
  8. data/app/controllers/openapi_blocks/spec_controller.rb +110 -0
  9. data/config/routes.rb +7 -0
  10. data/lib/openapi_blocks/base.rb +52 -0
  11. data/lib/openapi_blocks/builder.rb +23 -0
  12. data/lib/openapi_blocks/cache.rb +28 -0
  13. data/lib/openapi_blocks/configuration/contact_builder.rb +23 -0
  14. data/lib/openapi_blocks/configuration/info_builder.rb +42 -0
  15. data/lib/openapi_blocks/configuration/license_builder.rb +19 -0
  16. data/lib/openapi_blocks/configuration/security_builder.rb +33 -0
  17. data/lib/openapi_blocks/configuration/server_builder.rb +19 -0
  18. data/lib/openapi_blocks/configuration/servers_builder.rb +21 -0
  19. data/lib/openapi_blocks/configuration.rb +55 -0
  20. data/lib/openapi_blocks/engine.rb +13 -0
  21. data/lib/openapi_blocks/file_watcher.rb +42 -0
  22. data/lib/openapi_blocks/middleware.rb +54 -0
  23. data/lib/openapi_blocks/operation_builder.rb +57 -0
  24. data/lib/openapi_blocks/railtie.rb +19 -0
  25. data/lib/openapi_blocks/routing/extractor.rb +187 -0
  26. data/lib/openapi_blocks/routing/operation.rb +45 -0
  27. data/lib/openapi_blocks/schema/extractor.rb +103 -0
  28. data/lib/openapi_blocks/schema/types.rb +52 -0
  29. data/lib/openapi_blocks/schema/validator.rb +86 -0
  30. data/lib/openapi_blocks/spec/components.rb +67 -0
  31. data/lib/openapi_blocks/spec/document.rb +47 -0
  32. data/lib/openapi_blocks/spec/paths.rb +17 -0
  33. data/lib/openapi_blocks/version.rb +5 -0
  34. data/lib/openapi_blocks.rb +35 -0
  35. data/sig/openapi_blocks.rbs +4 -0
  36. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 955300fe20fc1f9dfe45283d0f4608886592feceb45db0cb7765e7eac1bda439
4
+ data.tar.gz: bcac14db5aae2164a71ec642cea311aeba135c3f7f43d3a173aea92579cedbcb
5
+ SHA512:
6
+ metadata.gz: 6be97c3ea397aafdc76b6f51f9222cc6bf2cfd0b8ef76982ce7cc73a79687f095110f99902cb8be787c3b1603bc9bdc47eb7ae14a2fab7fbfe24fd4fb71db4ad
7
+ data.tar.gz: e2b44b0e135fefd0c1af80a86c76dd2cfbc7cc69d9c74de0821808cf75ce4945cdf5e18ca4c1a55e95a096ab404255a0e4c6213e0227b5aac2be0a9777e9745c
data/CHANGELOG.md ADDED
@@ -0,0 +1,86 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.0] - 2026-06-01
11
+
12
+ ### Added
13
+
14
+ - `OpenapiBlocks::OperationBuilder` DSL for customizing operations per action
15
+ - `summary` and `description` customization per operation
16
+ - `parameter` DSL for query parameters with `in:`, `type:`, `description:` and `required:` options
17
+ - `response` DSL with `description:` and `schema:` options
18
+ - `tags` DSL on `OpenapiBlocks::Base` for custom tags per OpenAPI class
19
+ - `tags` DSL on `OperationBuilder` for custom tags per operation
20
+ - Tag resolution priority: operation tags > class tags > inferred from controller name
21
+ - `OpenapiBlocks::Configuration::SecurityBuilder` with `bearer_token` and `api_key` DSL
22
+ - Global `security` configuration in initializer
23
+ - Per-operation `security` and `no_security!` DSL in `OperationBuilder`
24
+ - `securitySchemes` generated automatically in `components`
25
+ - `tags` array generated at document root level from paths
26
+ - Swagger UI served at root of mounted engine (`/docs`)
27
+ - `SpecController#ui` action serving Swagger UI with JSON/YAML spec switcher
28
+ - `association` DSL now supports `input: false` to exclude from input schema
29
+ - Associations excluded from `required` fields automatically
30
+ - `resolve_schema` classifies symbol references (`:user` → `User`)
31
+ - Automatic `UserInput` schema excludes `read_only: true` virtual attributes
32
+ - Validation for `openapi_version` — raises `ArgumentError` for unsupported versions
33
+ - RSpec coverage for `OperationBuilder`, `SecurityBuilder`, tags and security
34
+
35
+ ### Fixed
36
+
37
+ - `find_openapi_class` uses `Object.const_get` instead of `ObjectSpace` for reliability
38
+ - `openapi` version field correctly outputs `3.1.0` instead of `3.1`
39
+
40
+ ## [0.1.0] - 2026-06-01
41
+
42
+ ### Added
43
+
44
+ - Initial project structure with gemspec, RSpec and RuboCop setup (`71be639`)
45
+ - `OpenapiBlocks::Configuration` with DSL for `info`, `servers` and `openapi_version` (3.0 / 3.1)
46
+ - `OpenapiBlocks::Cache` thread-safe in-memory cache with `Mutex`
47
+ - `OpenapiBlocks::FileWatcher` monitoring `app/openapi`, `app/models`, `config/routes.rb` and `db/schema.rb`
48
+ - `OpenapiBlocks::Middleware` with automatic cache invalidation on file changes
49
+ - `OpenapiBlocks::Railtie` for automatic middleware injection and eager loading of `app/openapi` (`28d9cab`)
50
+ - `OpenapiBlocks::Schema::Extractor` for automatic schema generation from ActiveRecord columns
51
+ - `OpenapiBlocks::Schema::Validator` for mapping ActiveModel validations to OpenAPI restrictions (`minLength`, `maxLength`, `minimum`, `maximum`, `enum`, `pattern`, `format`)
52
+ - `OpenapiBlocks::Schema::Types` mapping ActiveRecord types to OpenAPI types (`1256d31`)
53
+ - `OpenapiBlocks::Routing::Operation` and `OpenapiBlocks::Routing::Extractor` for automatic path generation from Rails routes
54
+ - `OpenapiBlocks::Spec::Document`, `OpenapiBlocks::Spec::Paths` and `OpenapiBlocks::Spec::Components` for OpenAPI document assembly
55
+ - `OpenapiBlocks::Builder` for orchestrating spec generation (`db5910a`)
56
+ - `OpenapiBlocks::Engine` mountable in `config/routes.rb` exposing `/openapi.json` and `/openapi.yaml`
57
+ - `OpenapiBlocks::SpecController` replacing previous implementation (`cbacd18`)
58
+ - Automatic `UserInput` schema generation for `POST`, `PUT` and `PATCH` request bodies
59
+ - Validation merging into schema properties
60
+ - Automatic filtering of internal Rails routes (`rails/`, `action_mailbox/`, `active_storage/`) (`35737ab`)
61
+ - `OpenapiBlocks::OperationBuilder` DSL for customizing operations per action (`operation :index`, `:show`, `:create`, `:update`, `:destroy`)
62
+ - `summary` and `description` customization per operation
63
+ - `parameter` DSL for query parameters with `in:`, `type:`, `description:` and `required:` options
64
+ - `response` DSL for custom responses with `description:` and `schema:` options
65
+ - Schema resolution supporting `Symbol` (`schema: :User`) and array (`schema: { type: :array, items: :User }`) references
66
+ - Fallback to auto-generated responses when no custom `operation` block is defined (`0055aa3`)
67
+ - RSpec coverage for `Schema::Types`, `Schema::Validator`, `Schema::Extractor`, `Routing::Extractor`, `Spec::Components` and `Configuration`
68
+ - Validation for `openapi_version` — raises `ArgumentError` for unsupported versions
69
+ - `read_only: true` virtual attributes excluded from `UserInput` schema
70
+ - `OpenapiBlocks::Configuration::SecurityBuilder` with `bearer_token` and `api_key` DSL
71
+ - Global `security` configuration in initializer
72
+ - Per-operation `security` and `no_security!` DSL in `OperationBuilder`
73
+ - `securitySchemes` generated automatically in `components`
74
+ - `tags` array generated at document root level from paths
75
+ - `association` DSL now supports `input: false` to exclude from `UserInput`
76
+ - Associations excluded from `required` fields automatically
77
+ - `resolve_schema` now classifies symbol references (`:user` → `User`)
78
+ - `tags` DSL on `OpenapiBlocks::Base` for custom tags per OpenAPI class
79
+ - `tags` DSL on `OperationBuilder` for custom tags per operation
80
+ - Tag resolution priority: operation tags > class tags > inferred from controller name
81
+ - Swagger UI served at root of mounted engine (`/docs`)
82
+ - `SpecController#ui` action serving Swagger UI with JSON/YAML spec switcher
83
+
84
+ [Unreleased]: https://github.com/evotechbuilder/openapi_blocks/compare/v0.2.0...HEAD
85
+ [0.2.0]: https://github.com/evotechbuilder/openapi_blocks/compare/v0.1.0...v0.2.0
86
+ [0.1.0]: https://github.com/evotechbuilder/openapi_blocks/releases/tag/v0.1.0
@@ -0,0 +1,10 @@
1
+ # Code of Conduct
2
+
3
+ "openapi_blocks" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
4
+
5
+ * Participants will be tolerant of opposing views.
6
+ * Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
7
+ * When interpreting the words and actions of others, participants should always assume good intentions.
8
+ * Behaviour which can be reasonably considered harassment will not be tolerated.
9
+
10
+ If you have any concerns about behaviour within this project, please contact us at ["caio.francelinosena@gmail.com"](mailto:"caio.francelinosena@gmail.com").
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Caio Santos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,304 @@
1
+ # OpenapiBlocks
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 ActiveModel::Serializer (https://github.com/rails-api/active_model_serializers).
4
+
5
+ Versão em português brasileiro: README.pt-BR.md
6
+
7
+ No manual annotation. No DSL noise in your controllers. Just declare what to expose and the spec is generated automatically.
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem "openapi_blocks"
17
+ ```
18
+
19
+ Then run:
20
+
21
+ ```bash
22
+ bundle install
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Setup
28
+
29
+ ### 1. Mount the Engine
30
+
31
+ ```ruby
32
+ # config/routes.rb
33
+ Rails.application.routes.draw do
34
+ mount OpenapiBlocks::Engine => "/docs"
35
+
36
+ resources :users
37
+ end
38
+ ```
39
+
40
+ This exposes:
41
+
42
+ ```
43
+ GET /docs -> Swagger UI
44
+ GET /docs/openapi.json -> OpenAPI spec in JSON
45
+ GET /docs/openapi.yaml -> OpenAPI spec in YAML
46
+ ```
47
+
48
+ ### 2. Configure the initializer
49
+
50
+ ```ruby
51
+ # config/initializers/openapi_blocks.rb
52
+ OpenapiBlocks.configure do |config|
53
+ config.openapi_version = "3.1" # "3.0" or "3.1"
54
+
55
+ config.info do
56
+ title "My API"
57
+ version "1.0.0"
58
+ description "API documentation generated automatically"
59
+
60
+ contact do
61
+ name "My Team"
62
+ email "api@mycompany.com"
63
+ url "https://mycompany.com"
64
+ end
65
+
66
+ license do
67
+ name "MIT"
68
+ url "https://opensource.org/licenses/MIT"
69
+ end
70
+ end
71
+
72
+ config.servers do
73
+ server do
74
+ url "https://api.mycompany.com"
75
+ description "Production"
76
+ end
77
+
78
+ server do
79
+ url "http://localhost:3000"
80
+ description "Development"
81
+ end
82
+ end
83
+
84
+ config.watch = :development # auto-reload on file changes
85
+
86
+ # optional: security schemes
87
+ config.security do
88
+ bearer_token format: "JWT"
89
+ api_key name: "X-API-Key", in: :header
90
+ end
91
+ end
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Usage
97
+
98
+ ### Creating an OpenAPI class
99
+
100
+ Create a file in app/openapi/ following the same naming convention as ActiveModel::Serializer:
101
+
102
+ ```
103
+ app/
104
+ openapi/
105
+ user_openapi.rb -> User model
106
+ post_openapi.rb -> Post model
107
+ order_openapi.rb -> Order model
108
+ ```
109
+
110
+ ```ruby
111
+ # app/openapi/user_openapi.rb
112
+ class UserOpenapi < OpenapiBlocks::Base
113
+ # model User is inferred automatically from the class name
114
+
115
+ # custom tags (default: inferred from controller name)
116
+ tags "Users"
117
+
118
+ # opt-out sensitive or unnecessary fields
119
+ ignore :password_digest, :reset_password_token
120
+
121
+ # opt-in associations
122
+ association :company
123
+ association :posts, type: :array, input: false # excluded from UserInput
124
+
125
+ # virtual attributes (not in the database)
126
+ # read_only: true -> exposed in response (User), excluded from request body (UserInput)
127
+ # read_only: false -> exposed in both User and UserInput
128
+ attribute :full_name, type: :string, read_only: true
129
+ attribute :access_token, type: :string, read_only: true
130
+ attribute :nickname, type: :string
131
+ end
132
+ ```
133
+
134
+ ### What is generated automatically
135
+
136
+ Given this model:
137
+
138
+ ```ruby
139
+ class User < ApplicationRecord
140
+ validates :name, presence: true, length: { minimum: 2, maximum: 100 }
141
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
142
+ validates :age, numericality: { greater_than: 0 }
143
+ validates :role, inclusion: { in: %w[admin user guest] }
144
+ end
145
+ ```
146
+
147
+ OpenapiBlocks generates:
148
+
149
+ - User schema from db/schema.rb columns and types
150
+ - UserInput schema for POST, PUT and PATCH request bodies (without id, created_at, updated_at and read_only virtual attributes)
151
+ - required fields from presence: true validations
152
+ - minLength, maxLength from length validations
153
+ - minimum, maximum from numericality validations
154
+ - enum from inclusion validations
155
+ - format: "email" from format validations
156
+ - All paths from config/routes.rb
157
+
158
+ ### Customizing operations
159
+
160
+ ```ruby
161
+ # app/openapi/user_openapi.rb
162
+ class UserOpenapi < OpenapiBlocks::Base
163
+ tags "Users"
164
+
165
+ operation :index do
166
+ summary "List all users"
167
+ description "Returns a paginated list of active users"
168
+ tags "Users", "Admin" # overrides class-level tags for this operation
169
+
170
+ parameter :page, in: :query, type: :integer, description: "Page number"
171
+ parameter :per_page, in: :query, type: :integer, description: "Items per page"
172
+
173
+ response 200, description: "List of users", schema: { type: :array, items: :User }
174
+ response 401, description: "Unauthorized"
175
+ end
176
+
177
+ operation :show do
178
+ summary "Get a user"
179
+
180
+ response 200, description: "User found", schema: :User
181
+ response 404, description: "User not found"
182
+ end
183
+
184
+ operation :create do
185
+ summary "Create a user"
186
+
187
+ response 201, description: "User created", schema: :User
188
+ response 422, description: "Invalid data"
189
+ end
190
+
191
+ operation :update do
192
+ summary "Update a user"
193
+
194
+ response 200, description: "User updated", schema: :User
195
+ response 404, description: "User not found"
196
+ response 422, description: "Invalid data"
197
+ end
198
+
199
+ operation :destroy do
200
+ summary "Delete a user"
201
+
202
+ response 200, description: "User deleted"
203
+ response 404, description: "User not found"
204
+ end
205
+ end
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Security
211
+
212
+ Configure global security schemes in the initializer:
213
+
214
+ ```ruby
215
+ config.security do
216
+ bearer_token format: "JWT" # Authorization: Bearer <token>
217
+ api_key name: "X-API-Key", in: :header # X-API-Key: <key>
218
+ end
219
+ ```
220
+
221
+ Override security per operation:
222
+
223
+ ```ruby
224
+ operation :index do
225
+ security :bearerAuth # only bearer on this operation
226
+ end
227
+
228
+ operation :show do
229
+ no_security! # public endpoint — no auth required
230
+ end
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Associations
236
+
237
+ ```ruby
238
+ association :company # belongs_to — $ref to Company schema
239
+ association :posts, type: :array # has_many — array of $ref to Post schema
240
+ association :posts, type: :array, input: false # excluded from UserInput (response only)
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Virtual Attributes
246
+
247
+ Virtual attributes are fields that exist in the API response but not in the database.
248
+
249
+ | Option | Description | Appears in User | Appears in UserInput |
250
+ | ---------------- | -------------------------------------- | :-------------: | :------------------: |
251
+ | read_only: true | Calculated or system-generated fields | YES | NO |
252
+ | read_only: false | Fields the client can send and receive | YES | YES |
253
+
254
+ ```ruby
255
+ attribute :full_name, type: :string, read_only: true # response only
256
+ attribute :access_token, type: :string, read_only: true # response only
257
+ attribute :nickname, type: :string # request and response
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Type Mapping
263
+
264
+ | ActiveRecord type | OpenAPI type |
265
+ | ----------------- | ------------------ |
266
+ | integer | integer / int32 |
267
+ | bigint | integer / int64 |
268
+ | float | number / float |
269
+ | decimal | number / double |
270
+ | string | string |
271
+ | text | string |
272
+ | boolean | boolean |
273
+ | date | string / date |
274
+ | datetime | string / date-time |
275
+ | uuid | string / uuid |
276
+ | json / jsonb | object |
277
+
278
+ ---
279
+
280
+ ## Auto-reload in Development
281
+
282
+ OpenapiBlocks watches for changes in:
283
+
284
+ ```
285
+ app/openapi/**/*.rb
286
+ app/models/**/*.rb
287
+ config/routes.rb
288
+ db/schema.rb
289
+ ```
290
+
291
+ The spec is automatically regenerated on the next request to /docs/openapi.json whenever any of these files change. No server restart needed.
292
+
293
+ ---
294
+
295
+ ## Requirements
296
+
297
+ - Ruby >= 3.2
298
+ - Rails >= 7.0
299
+
300
+ ---
301
+
302
+ ## License
303
+
304
+ MIT (LICENSE.txt)