grape-oas 1.0.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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +82 -0
  3. data/CONTRIBUTING.md +87 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +184 -0
  6. data/RELEASING.md +109 -0
  7. data/grape-oas.gemspec +27 -0
  8. data/lib/grape-oas.rb +3 -0
  9. data/lib/grape_oas/api_model/api.rb +42 -0
  10. data/lib/grape_oas/api_model/media_type.rb +22 -0
  11. data/lib/grape_oas/api_model/node.rb +57 -0
  12. data/lib/grape_oas/api_model/operation.rb +55 -0
  13. data/lib/grape_oas/api_model/parameter.rb +24 -0
  14. data/lib/grape_oas/api_model/path.rb +29 -0
  15. data/lib/grape_oas/api_model/request_body.rb +27 -0
  16. data/lib/grape_oas/api_model/response.rb +28 -0
  17. data/lib/grape_oas/api_model/schema.rb +60 -0
  18. data/lib/grape_oas/api_model_builder.rb +63 -0
  19. data/lib/grape_oas/api_model_builders/concerns/content_type_resolver.rb +93 -0
  20. data/lib/grape_oas/api_model_builders/concerns/oas_utilities.rb +75 -0
  21. data/lib/grape_oas/api_model_builders/concerns/type_resolver.rb +142 -0
  22. data/lib/grape_oas/api_model_builders/operation.rb +168 -0
  23. data/lib/grape_oas/api_model_builders/path.rb +122 -0
  24. data/lib/grape_oas/api_model_builders/request.rb +304 -0
  25. data/lib/grape_oas/api_model_builders/request_params.rb +128 -0
  26. data/lib/grape_oas/api_model_builders/request_params_support/nested_params_builder.rb +155 -0
  27. data/lib/grape_oas/api_model_builders/request_params_support/param_location_resolver.rb +64 -0
  28. data/lib/grape_oas/api_model_builders/request_params_support/param_schema_builder.rb +163 -0
  29. data/lib/grape_oas/api_model_builders/request_params_support/schema_enhancer.rb +111 -0
  30. data/lib/grape_oas/api_model_builders/response.rb +241 -0
  31. data/lib/grape_oas/api_model_builders/response_parsers/base.rb +56 -0
  32. data/lib/grape_oas/api_model_builders/response_parsers/default_response_parser.rb +31 -0
  33. data/lib/grape_oas/api_model_builders/response_parsers/documentation_responses_parser.rb +35 -0
  34. data/lib/grape_oas/api_model_builders/response_parsers/http_codes_parser.rb +85 -0
  35. data/lib/grape_oas/constants.rb +81 -0
  36. data/lib/grape_oas/documentation_extension.rb +124 -0
  37. data/lib/grape_oas/exporter/base/operation.rb +88 -0
  38. data/lib/grape_oas/exporter/base/paths.rb +53 -0
  39. data/lib/grape_oas/exporter/concerns/schema_indexer.rb +93 -0
  40. data/lib/grape_oas/exporter/concerns/tag_builder.rb +55 -0
  41. data/lib/grape_oas/exporter/oas2/operation.rb +31 -0
  42. data/lib/grape_oas/exporter/oas2/parameter.rb +116 -0
  43. data/lib/grape_oas/exporter/oas2/paths.rb +19 -0
  44. data/lib/grape_oas/exporter/oas2/response.rb +74 -0
  45. data/lib/grape_oas/exporter/oas2/schema.rb +125 -0
  46. data/lib/grape_oas/exporter/oas2_schema.rb +133 -0
  47. data/lib/grape_oas/exporter/oas3/operation.rb +24 -0
  48. data/lib/grape_oas/exporter/oas3/parameter.rb +27 -0
  49. data/lib/grape_oas/exporter/oas3/paths.rb +21 -0
  50. data/lib/grape_oas/exporter/oas3/request_body.rb +54 -0
  51. data/lib/grape_oas/exporter/oas3/response.rb +85 -0
  52. data/lib/grape_oas/exporter/oas3/schema.rb +249 -0
  53. data/lib/grape_oas/exporter/oas30_schema.rb +13 -0
  54. data/lib/grape_oas/exporter/oas31/schema.rb +42 -0
  55. data/lib/grape_oas/exporter/oas31_schema.rb +34 -0
  56. data/lib/grape_oas/exporter/oas3_schema.rb +130 -0
  57. data/lib/grape_oas/exporter/registry.rb +82 -0
  58. data/lib/grape_oas/exporter.rb +16 -0
  59. data/lib/grape_oas/introspectors/base.rb +44 -0
  60. data/lib/grape_oas/introspectors/dry_introspector.rb +131 -0
  61. data/lib/grape_oas/introspectors/dry_introspector_support/argument_extractor.rb +51 -0
  62. data/lib/grape_oas/introspectors/dry_introspector_support/ast_walker.rb +125 -0
  63. data/lib/grape_oas/introspectors/dry_introspector_support/constraint_applier.rb +136 -0
  64. data/lib/grape_oas/introspectors/dry_introspector_support/constraint_extractor.rb +85 -0
  65. data/lib/grape_oas/introspectors/dry_introspector_support/constraint_merger.rb +47 -0
  66. data/lib/grape_oas/introspectors/dry_introspector_support/contract_resolver.rb +60 -0
  67. data/lib/grape_oas/introspectors/dry_introspector_support/inheritance_handler.rb +87 -0
  68. data/lib/grape_oas/introspectors/dry_introspector_support/predicate_handler.rb +131 -0
  69. data/lib/grape_oas/introspectors/dry_introspector_support/type_schema_builder.rb +143 -0
  70. data/lib/grape_oas/introspectors/dry_introspector_support/type_unwrapper.rb +143 -0
  71. data/lib/grape_oas/introspectors/entity_introspector.rb +165 -0
  72. data/lib/grape_oas/introspectors/entity_introspector_support/cycle_tracker.rb +42 -0
  73. data/lib/grape_oas/introspectors/entity_introspector_support/discriminator_handler.rb +83 -0
  74. data/lib/grape_oas/introspectors/entity_introspector_support/exposure_processor.rb +261 -0
  75. data/lib/grape_oas/introspectors/entity_introspector_support/inheritance_builder.rb +112 -0
  76. data/lib/grape_oas/introspectors/entity_introspector_support/property_extractor.rb +53 -0
  77. data/lib/grape_oas/introspectors/registry.rb +136 -0
  78. data/lib/grape_oas/rake/oas_tasks.rb +127 -0
  79. data/lib/grape_oas/version.rb +5 -0
  80. data/lib/grape_oas.rb +145 -0
  81. metadata +152 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1a578224c8df0f07ef5151db8496a084a64c6000f1f4c4158d7cc21efee1f91f
4
+ data.tar.gz: a1427ab277998cd7828e24c91f2555be6ea41b254312f14c4ad40b43204fa13c
5
+ SHA512:
6
+ metadata.gz: c03fa30c214a62b2f3164ced48ff16e6c8ba120126ac3d7397db0cd0a7e6ee191224b10bd59badb91f848d3bfcded18cc03e4da331f883c80203795dfa0a143b
7
+ data.tar.gz: b232aa12c2211dc35853933ad09e1d1d5451d1df9fc3c2e1cb2c3bc1d58f8769b892141381323669c3c158e6ca409bbab3358012f9be0360f2736cb6194b1ada
data/CHANGELOG.md ADDED
@@ -0,0 +1,82 @@
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
+ ## [1.0.0] - 2025-12-06
9
+
10
+ ### Added
11
+
12
+ #### Core Features
13
+ - OpenAPI specification generation for Grape APIs
14
+ - Support for OpenAPI 2.0 (Swagger), 3.0, and 3.1 specifications
15
+ - `GrapeOAS.generate(app:, schema_type:)` for programmatic generation
16
+ - `add_oas_documentation` DSL for mounting documentation endpoint
17
+ - `add_swagger_documentation` compatibility shim for grape-swagger migration
18
+ - Query parameter `?oas=2|3|3.1` for version selection at runtime
19
+
20
+ #### Entity Support
21
+ - Built-in Grape::Entity introspection (no separate gem needed)
22
+ - Dry::Validation::Contract and Dry::Struct support
23
+ - Entity inheritance with `allOf` composition
24
+ - Polymorphism support with `discriminator`
25
+ - Sum types (`|`) converted to `anyOf`
26
+ - Circular reference handling with `$ref`
27
+
28
+ #### Parameter Documentation
29
+ - All Grape parameter types (String, Integer, Float, Boolean, Date, DateTime, Array, Hash, File)
30
+ - Nested parameters (Hash with block)
31
+ - Array parameters with item types (`Array[String]`, `Array[Integer]`)
32
+ - Multi-type parameters (`types: [String, Integer]`)
33
+ - Parameter validation constraints (values, regexp, minimum, maximum, etc.)
34
+ - Parameter hiding (`documentation: { hidden: true }`)
35
+ - `collectionFormat` support for OAS2 arrays
36
+
37
+ #### Response Documentation
38
+ - `success` and `failure` response definitions
39
+ - Multiple success/failure status codes
40
+ - Response headers
41
+ - Response examples
42
+ - Multiple present responses with `as:` key combination
43
+ - Root element wrapping support
44
+ - `suppress_default_error_response` option
45
+
46
+ #### Endpoint Documentation
47
+ - `desc` block syntax with detail, tags, deprecated, consumes, produces
48
+ - Endpoint hiding (`hidden: true` or lambda)
49
+ - `body_name` for custom body parameter naming (OAS2)
50
+ - Request body for GET/HEAD/DELETE when explicitly enabled
51
+ - Operation extensions (`x-*` properties)
52
+
53
+ #### Configuration
54
+ - Global options: host, base_path, schemes, servers, consumes, produces
55
+ - Info object: title, version, description, contact, license, terms_of_service
56
+ - Security definitions (API key, OAuth2, Bearer)
57
+ - Tag definitions with descriptions
58
+ - `models` option to pre-register entities
59
+ - Namespace filtering for partial schema generation
60
+ - URL-based namespace filtering for mounted docs (`/swagger_doc/users`)
61
+ - Tag filtering to only include used tags
62
+
63
+ #### Rake Tasks
64
+ - `grape_oas:generate[API,schema_type,output_path]` for file generation
65
+ - `grape_oas:validate[file_path]` for spec validation
66
+
67
+ #### Migration Support
68
+ - Comprehensive migration guide from grape-swagger
69
+ - Feature parity documentation
70
+ - Compatibility shim for `add_swagger_documentation`
71
+
72
+ #### Extensibility
73
+ - Introspector registry - register custom introspectors via `GrapeOAS.introspectors.register()`
74
+ - Exporter registry - register custom exporters via `GrapeOAS.exporters.register(ExporterClass, as: :alias)`
75
+
76
+ ### Documentation
77
+ - README with full usage examples
78
+ - `docs/MIGRATING_FROM_GRAPE_SWAGGER.md` - detailed migration guide
79
+ - `docs/ARCHITECTURE.md` - system architecture overview
80
+ - `docs/INTROSPECTORS.md` - introspector system documentation
81
+ - `docs/EXPORTERS.md` - exporter system documentation
82
+ - `docs/API_MODEL.md` - internal API model reference
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,87 @@
1
+ # Contributing to Grape::OAS
2
+
3
+ We welcome contributions to Grape::OAS! This document provides guidelines for contributing.
4
+
5
+ ## Getting Started
6
+
7
+ 1. **Fork the repository** on GitHub
8
+
9
+ 2. **Clone your fork**:
10
+ ```bash
11
+ git clone https://github.com/YOUR_USERNAME/grape-oas.git
12
+ cd grape-oas
13
+ ```
14
+
15
+ 3. **Set up upstream remote**:
16
+ ```bash
17
+ git remote add upstream https://github.com/numbata/grape-oas.git
18
+ ```
19
+
20
+ 4. **Install dependencies**:
21
+ ```bash
22
+ bin/setup
23
+ ```
24
+
25
+ ## Development Workflow
26
+
27
+ 1. **Create a topic branch**:
28
+ ```bash
29
+ git checkout -b my-feature
30
+ ```
31
+
32
+ 2. **Write tests** for your changes in `test/`
33
+
34
+ 3. **Implement your feature or fix**
35
+
36
+ 4. **Run the test suite**:
37
+ ```bash
38
+ bundle exec rake test
39
+ ```
40
+
41
+ 5. **Run RuboCop**:
42
+ ```bash
43
+ bundle exec rubocop
44
+ ```
45
+
46
+ 6. **Run all checks**:
47
+ ```bash
48
+ bundle exec rake
49
+ ```
50
+
51
+ ## Submitting Changes
52
+
53
+ 1. **Update CHANGELOG.md** under "Unreleased" section
54
+
55
+ 2. **Write clear commit messages** describing what and why
56
+
57
+ 3. **Push to your fork**:
58
+ ```bash
59
+ git push origin my-feature
60
+ ```
61
+
62
+ 4. **Open a Pull Request** against the `main` branch
63
+
64
+ ## Pull Request Guidelines
65
+
66
+ - Keep PRs focused on a single change
67
+ - Include tests for new functionality
68
+ - Update documentation if needed
69
+ - Ensure CI passes before requesting review
70
+
71
+ ## Code Style
72
+
73
+ - Follow existing code conventions
74
+ - RuboCop enforces style - run it before committing
75
+ - Use descriptive variable and method names
76
+
77
+ ## Testing
78
+
79
+ - Write tests for all new functionality
80
+ - Ensure existing tests pass
81
+ - Aim for good coverage of edge cases
82
+
83
+ ## Questions?
84
+
85
+ Open an issue for questions or discussions about potential changes.
86
+
87
+ Thank you for contributing!
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Andrei Subbota
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,184 @@
1
+ # Grape::OAS
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/grape-oas.svg)](https://badge.fury.io/rb/grape-oas)
4
+ [![CI](https://github.com/numbata/grape-oas/actions/workflows/ci.yml/badge.svg)](https://github.com/numbata/grape-oas/actions)
5
+
6
+ OpenAPI Specification (OAS) documentation generator for [Grape](https://github.com/ruby-grape/grape) APIs. Supports OpenAPI 2.0 (Swagger), 3.0, and 3.1 specifications.
7
+
8
+ ## Why Grape::OAS?
9
+
10
+ Grape::OAS is built around a **DTO (Data Transfer Object) architecture** that separates collecting API metadata from generating schemas. This clean separation makes the codebase easier to reason about and enables support for multiple output formats (OAS 2.0, 3.0, 3.1) from the same API definition.
11
+
12
+ ## Features
13
+
14
+ - **Multi-version support**: Generate OAS 2.0, 3.0, or 3.1 from the same API
15
+ - **Entity integration**: Works with [grape-entity](https://github.com/ruby-grape/grape-entity) and [dry-struct](https://dry-rb.org/gems/dry-struct/)
16
+ - **Automatic type inference**: Derives OpenAPI types from Grape parameter definitions
17
+ - **Flexible output**: Mount as an endpoint or generate programmatically
18
+
19
+ ## Compatibility
20
+
21
+ | grape-oas | grape | grape-entity | dry-struct | Ruby |
22
+ |-----------|-------|--------------|------------|------|
23
+ | 0.1.x | >= 3.0 | >= 0.7 | >= 1.0 | >= 3.2 |
24
+
25
+ ## Installation
26
+
27
+ ```ruby
28
+ gem 'grape-oas'
29
+ ```
30
+
31
+ For entity support:
32
+
33
+ ```ruby
34
+ gem 'grape-entity' # For grape-entity support
35
+ gem 'dry-struct' # For dry-struct contract support
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### Mount Documentation Endpoint
41
+
42
+ ```ruby
43
+ class API < Grape::API
44
+ format :json
45
+
46
+ add_oas_documentation(
47
+ info: {
48
+ title: 'My API',
49
+ version: '1.0.0'
50
+ }
51
+ )
52
+
53
+ resource :users do
54
+ desc 'List users', entity: Entity::User
55
+ get { User.all }
56
+ end
57
+ end
58
+ ```
59
+
60
+ Documentation available at:
61
+ - `/swagger_doc` - OpenAPI 3.0 (default)
62
+ - `/swagger_doc?oas=2` - OpenAPI 2.0
63
+ - `/swagger_doc?oas=3.1` - OpenAPI 3.1
64
+
65
+ ### Manual Generation
66
+
67
+ ```ruby
68
+ # Generate OpenAPI 3.0 spec
69
+ spec = GrapeOAS.generate(app: API, schema_type: :oas3)
70
+ puts JSON.pretty_generate(spec)
71
+ ```
72
+
73
+ ### Rake Tasks
74
+
75
+ ```ruby
76
+ # In Rakefile
77
+ require 'grape_oas/tasks'
78
+ ```
79
+
80
+ ```bash
81
+ rake grape_oas:generate[MyAPI,oas31,spec/openapi.json]
82
+ ```
83
+
84
+ ## Documentation
85
+
86
+ | Document | Description |
87
+ |----------|-------------|
88
+ | [Configuration](docs/CONFIGURATION.md) | All configuration options |
89
+ | [Usage Guide](docs/USAGE.md) | Detailed usage examples |
90
+ | [Architecture](docs/ARCHITECTURE.md) | System architecture overview |
91
+ | [Introspectors](docs/INTROSPECTORS.md) | Custom introspector development |
92
+ | [Exporters](docs/EXPORTERS.md) | Custom exporter development |
93
+ | [API Model](docs/API_MODEL.md) | Internal API model reference |
94
+
95
+ ## Basic Usage
96
+
97
+ ### Documenting Endpoints
98
+
99
+ ```ruby
100
+ desc 'Get a user by ID',
101
+ detail: 'Returns detailed user information',
102
+ tags: ['users']
103
+
104
+ params do
105
+ requires :id, type: Integer, desc: 'User ID'
106
+ end
107
+
108
+ get ':id' do
109
+ User.find(params[:id])
110
+ end
111
+ ```
112
+
113
+ ### Response Documentation
114
+
115
+ ```ruby
116
+ desc 'Get user' do
117
+ success Entity::User
118
+ failure [[404, 'Not Found'], [500, 'Server Error']]
119
+ end
120
+ ```
121
+
122
+ ### Entity Definition
123
+
124
+ ```ruby
125
+ class Entity::User < Grape::Entity
126
+ expose :id, documentation: { type: Integer }
127
+ expose :name, documentation: { type: String }
128
+ expose :posts, using: Entity::Post, documentation: { is_array: true }
129
+ end
130
+ ```
131
+
132
+ ## Extensibility
133
+
134
+ ### Custom Introspectors
135
+
136
+ ```ruby
137
+ class MyModelIntrospector
138
+ extend GrapeOAS::Introspectors::Base
139
+
140
+ def self.handles?(subject)
141
+ subject.is_a?(Class) && subject < MyBaseModel
142
+ end
143
+
144
+ def self.build_schema(subject, stack: [], registry: {})
145
+ GrapeOAS::ApiModel::Schema.new(type: "object", canonical_name: subject.name)
146
+ end
147
+ end
148
+
149
+ GrapeOAS.introspectors.register(MyModelIntrospector)
150
+ ```
151
+
152
+ ### Custom Exporters
153
+
154
+ ```ruby
155
+ GrapeOAS.exporters.register(MyCustomExporter, as: :custom)
156
+ schema = GrapeOAS.generate(app: API, schema_type: :custom)
157
+ ```
158
+
159
+ ## Related Projects
160
+
161
+ | Project | Description |
162
+ |---------|-------------|
163
+ | [grape](https://github.com/ruby-grape/grape) | REST-like API framework for Ruby |
164
+ | [grape-entity](https://github.com/ruby-grape/grape-entity) | Entity exposure for Grape APIs |
165
+ | [grape-swagger](https://github.com/ruby-grape/grape-swagger) | OpenAPI documentation for Grape APIs |
166
+ | [grape-swagger-entity](https://github.com/ruby-grape/grape-swagger-entity) | grape-swagger adapter for grape-entity |
167
+ | [oas_grape](https://github.com/a-chacon/oas_grape) | Another OpenAPI 3.1 generator for Grape |
168
+
169
+ ## Development
170
+
171
+ ```bash
172
+ git clone https://github.com/numbata/grape-oas.git
173
+ cd grape-oas
174
+ bin/setup
175
+ bundle exec rake test
176
+ ```
177
+
178
+ ## Contributing
179
+
180
+ Bug reports and pull requests are welcome on GitHub at https://github.com/numbata/grape-oas. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
181
+
182
+ ## License
183
+
184
+ MIT License. Copyright (c) Andrei Subbota.
data/RELEASING.md ADDED
@@ -0,0 +1,109 @@
1
+ # Releasing Grape::OAS
2
+
3
+ This document describes the release process for the grape-oas gem.
4
+
5
+ ## Pre-Release Checks
6
+
7
+ Before releasing, ensure:
8
+
9
+ 1. All tests pass locally and in CI:
10
+ ```bash
11
+ bundle install
12
+ bundle exec rake
13
+ ```
14
+
15
+ 2. The CHANGELOG.md is up to date with all changes since the last release.
16
+
17
+ 3. The version number in `lib/grape_oas/version.rb` is correct.
18
+
19
+ ## Release Steps
20
+
21
+ ### Automated Release Process
22
+
23
+ 1. **Update the version** in `lib/grape_oas/version.rb` to the release version
24
+
25
+ 2. **Prepare the CHANGELOG**:
26
+ ```bash
27
+ bundle exec rake release:prerelease
28
+ ```
29
+ This task will:
30
+ - Replace "Unreleased" with the version number and today's date
31
+ - Remove any "Your contribution here" placeholder lines
32
+ - Commit with message "Preparing for release vX.X.X"
33
+
34
+ **Note**: The task is idempotent - safe to run multiple times.
35
+
36
+ 3. **Push and create the release**:
37
+ ```bash
38
+ git push origin main
39
+ bundle exec rake release
40
+ ```
41
+ This will:
42
+ - Build the gem
43
+ - Create a git tag
44
+ - Push the tag to GitHub
45
+ - Push the gem to RubyGems.org
46
+
47
+ <details>
48
+ <summary>Manual Release Steps (if needed)</summary>
49
+
50
+ 1. **Update CHANGELOG.md**
51
+ - Change "Unreleased" to the version number and date
52
+ - Remove any "Your contribution here" placeholder lines
53
+
54
+ 2. **Commit the release preparation**:
55
+ ```bash
56
+ git add CHANGELOG.md
57
+ git commit -m "Preparing for release v0.x.x"
58
+ git push origin main
59
+ ```
60
+
61
+ 3. **Create and push the release**:
62
+ ```bash
63
+ bundle exec rake release
64
+ ```
65
+ This will:
66
+ - Build the gem
67
+ - Create a git tag
68
+ - Push the tag to GitHub
69
+ - Push the gem to RubyGems.org
70
+ </details>
71
+
72
+ ## Post-Release
73
+
74
+ You can automate the post-release preparation with:
75
+
76
+ ```bash
77
+ bundle exec rake release:postrelease
78
+ git push origin main
79
+ ```
80
+
81
+ This task will:
82
+ - Bump the patch version in `lib/grape_oas/version.rb`
83
+ - Ensure CHANGELOG.md has an "Unreleased" section
84
+ - Commit the changes with message "Prepare for next development iteration"
85
+
86
+ **Note**: The task is idempotent - safe to run multiple times without duplicating work.
87
+
88
+ <details>
89
+ <summary>Manual Post-Release Steps (if needed)</summary>
90
+
91
+ 1. **Prepare for next development cycle**:
92
+ - Add "Unreleased" section to CHANGELOG.md
93
+ - Bump version in `lib/grape_oas/version.rb`
94
+
95
+ 2. **Commit post-release changes**:
96
+ ```bash
97
+ git add CHANGELOG.md lib/grape_oas/version.rb
98
+ git commit -m "Prepare for next development iteration"
99
+ git push origin main
100
+ ```
101
+ </details>
102
+
103
+ ## Versioning
104
+
105
+ This project follows [Semantic Versioning](https://semver.org/):
106
+
107
+ - **MAJOR**: Incompatible API changes
108
+ - **MINOR**: New functionality in a backward compatible manner
109
+ - **PATCH**: Backward compatible bug fixes
data/grape-oas.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/grape_oas/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "grape-oas"
7
+ spec.version = GrapeOAS::VERSION
8
+ spec.authors = ["Andrei Subbota"]
9
+ spec.email = ["subbota@gmail.com"]
10
+
11
+ spec.summary = "OpenAPI (Swagger) v2 and v3 documentation for Grape APIs"
12
+ spec.description = "A Grape extension that provides OpenAPI (Swagger) v2 and v3 documentation support"
13
+ spec.homepage = "https://github.com/numbata/grape-oas"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
+
21
+ spec.files = Dir["lib/**/*", "*.md", "LICENSE.txt", "grape-oas.gemspec"]
22
+
23
+ spec.add_dependency "grape", ">= 3.0"
24
+ spec.add_dependency "zeitwerk"
25
+
26
+ spec.metadata["rubygems_mfa_required"] = "true"
27
+ end
data/lib/grape-oas.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "grape_oas"
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeOAS
4
+ module ApiModel
5
+ # Represents the root API object in the DTO model for OpenAPI v2/v3.
6
+ # Contains metadata, paths, servers, tags, and components.
7
+ # Used as the entry point for building OpenAPIv2 and OpenAPIv3 documents.
8
+ #
9
+ # @see https://swagger.io/specification/
10
+ # @see GrapeOAS::ApiModel::Path
11
+ class API < Node
12
+ attr_accessor :title, :version, :paths, :servers, :tag_defs, :components,
13
+ :host, :base_path, :schemes, :security_definitions, :security,
14
+ :registered_schemas, :suppress_default_error_response
15
+
16
+ def initialize(title:, version:)
17
+ super()
18
+ @title = title
19
+ @version = version
20
+ @paths = Set.new
21
+ @servers = []
22
+ @tag_defs = Set.new
23
+ @components = {}
24
+ @host = nil
25
+ @base_path = nil
26
+ @schemes = []
27
+ @security_definitions = {}
28
+ @security = []
29
+ @registered_schemas = []
30
+ @suppress_default_error_response = false
31
+ end
32
+
33
+ def add_path(path)
34
+ @paths << path
35
+ end
36
+
37
+ def add_tags(*tags)
38
+ @tag_defs.merge(tags)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeOAS
4
+ module ApiModel
5
+ # Represents a media type (e.g., application/json) in the DTO model for OpenAPI v2/v3.
6
+ # Used for request bodies and responses to specify content type and schema.
7
+ #
8
+ # @see https://swagger.io/specification/
9
+ # @see GrapeOAS::ApiModel::RequestBody, GrapeOAS::ApiModel::Response
10
+ class MediaType < Node
11
+ attr_accessor :mime_type, :schema, :examples, :extensions
12
+
13
+ def initialize(mime_type:, schema:, examples: nil, extensions: nil)
14
+ super()
15
+ @mime_type = mime_type
16
+ @schema = schema
17
+ @examples = examples
18
+ @extensions = extensions
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module GrapeOAS
6
+ module ApiModel
7
+ # Base class for all DTO (intermediate) nodes used in OpenAPI v2/v3 conversion.
8
+ # Provides a unique ID and helper methods for referencing and bucketing.
9
+ # All DTO classes for OpenAPIv2 and OpenAPIv3 inherit from Node.
10
+ #
11
+ # @abstract
12
+ # @see GrapeOAS::ApiModel::Schema, GrapeOAS::ApiModel::Parameter, etc.
13
+ class Node
14
+ # OpenAPI component bucket names per spec (some are irregular plurals)
15
+ BUCKET_NAMES = {
16
+ "Schema" => "schemas",
17
+ "Parameter" => "parameters",
18
+ "RequestBody" => "requestBodies",
19
+ "Response" => "responses",
20
+ "Header" => "headers",
21
+ "SecurityScheme" => "securitySchemes",
22
+ "Link" => "links",
23
+ "Callback" => "callbacks",
24
+ "Example" => "examples",
25
+ "PathItem" => "pathItems"
26
+ }.freeze
27
+
28
+ class << self
29
+ # Returns the pluralized bucket name for this class (e.g., "schemas", "parameters").
30
+ # Uses OpenAPI spec-compliant names for irregular plurals (e.g., "requestBodies").
31
+ # Memoized at the class level to avoid repeated string manipulation.
32
+ def bucket
33
+ @bucket ||= begin
34
+ class_name = name.split("::").last
35
+ BUCKET_NAMES.fetch(class_name, "#{class_name.downcase}s")
36
+ end
37
+ end
38
+ end
39
+
40
+ attr_reader :id
41
+
42
+ def initialize(node_id: nil)
43
+ @id = node_id || generate_id
44
+ end
45
+
46
+ def ref
47
+ "#/components/#{self.class.bucket}/#{id}"
48
+ end
49
+
50
+ private
51
+
52
+ def generate_id
53
+ SecureRandom.uuid
54
+ end
55
+ end
56
+ end
57
+ end