grape-swagger-entity 0.7.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.github/workflows/danger-comment.yml +10 -0
- data/.github/workflows/danger.yml +8 -18
- data/.rspec +1 -0
- data/.rubocop_todo.yml +14 -5
- data/AGENTS.md +24 -0
- data/CHANGELOG.md +14 -0
- data/Dangerfile +4 -1
- data/Gemfile +4 -1
- data/README.md +102 -7
- data/docs/architecture.md +71 -0
- data/docs/contributing.md +57 -0
- data/docs/documentation-options.md +89 -0
- data/docs/testing.md +63 -0
- data/lib/grape-swagger/entity/attribute_parser.rb +23 -4
- data/lib/grape-swagger/entity/parser.rb +64 -26
- data/lib/grape-swagger/entity/version.rb +1 -1
- metadata +13 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30e014da1f2cee91b947f2028660fcad2f9d71102bbe30d4eff6369a48974949
|
|
4
|
+
data.tar.gz: 4e6d460b2a210c5356a99444b89ca8dec5ded34b1601a4200e01ac4e7d84d68e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4830b3a7de51a5391ff090594e70bdb492c722bbcdc6e88745f169b22dc417a64291facee0fb465b3488804a81c113beb6b0f8f5b84f4204b69c9c108b5e3b89
|
|
7
|
+
data.tar.gz: 0b029af3078ad8c0769177445a2ed698292ba7f77fd910134dd8274f8d4d5d0f0354d2130916a78cc687aa44b36af797a503055052c9ea30808eaf0a0fbec63e
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
name:
|
|
2
|
-
on:
|
|
1
|
+
name: Danger
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
types: [opened, reopened, edited, synchronize]
|
|
3
5
|
|
|
4
6
|
jobs:
|
|
5
7
|
danger:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
fetch-depth: 100
|
|
11
|
-
- name: Set up Ruby
|
|
12
|
-
uses: ruby/setup-ruby@v1
|
|
13
|
-
with:
|
|
14
|
-
ruby-version: 3.2
|
|
15
|
-
bundler-cache: true
|
|
16
|
-
rubygems: latest
|
|
17
|
-
- name: Run Danger
|
|
18
|
-
run: |
|
|
19
|
-
# the token is public, has public_repo scope and belongs to the grape-bot user owned by @dblock, this is ok
|
|
20
|
-
TOKEN=$(echo -n Z2hwX2lYb0dPNXNyejYzOFJyaTV3QUxUdkNiS1dtblFwZTFuRXpmMwo= | base64 --decode)
|
|
21
|
-
DANGER_GITHUB_API_TOKEN=$TOKEN bundle exec danger --verbose
|
|
8
|
+
uses: numbata/danger-pr-comment/.github/workflows/danger-run.yml@main
|
|
9
|
+
secrets: inherit
|
|
10
|
+
with:
|
|
11
|
+
ruby-version: "3.2"
|
data/.rspec
CHANGED
data/.rubocop_todo.yml
CHANGED
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
-
# Offense count:
|
|
9
|
+
# Offense count: 8
|
|
10
10
|
# Configuration parameters: AllowedMethods.
|
|
11
11
|
# AllowedMethods: enums
|
|
12
12
|
Lint/ConstantDefinitionInBlock:
|
|
13
13
|
Exclude:
|
|
14
14
|
- 'spec/grape-swagger/entities/response_model_spec.rb'
|
|
15
|
+
- 'spec/issues/44_desc_in_entity_type_spec.rb'
|
|
16
|
+
- 'spec/issues/962_polymorphic_entity_with_custom_documentation_spec.rb'
|
|
15
17
|
- 'spec/support/shared_contexts/inheritance_api.rb'
|
|
16
18
|
- 'spec/support/shared_contexts/this_api.rb'
|
|
17
19
|
|
|
@@ -21,6 +23,11 @@ Lint/EmptyBlock:
|
|
|
21
23
|
Exclude:
|
|
22
24
|
- 'spec/grape-swagger/entity/attribute_parser_spec.rb'
|
|
23
25
|
|
|
26
|
+
# Offense count: 1
|
|
27
|
+
Lint/EmptyClass:
|
|
28
|
+
Exclude:
|
|
29
|
+
- 'spec/support/shared_contexts/custom_type_parser.rb'
|
|
30
|
+
|
|
24
31
|
# Offense count: 2
|
|
25
32
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
26
33
|
# Configuration parameters: AllowedMethods.
|
|
@@ -37,7 +44,7 @@ Metrics/AbcSize:
|
|
|
37
44
|
# Offense count: 2
|
|
38
45
|
# Configuration parameters: CountComments, CountAsOne.
|
|
39
46
|
Metrics/ClassLength:
|
|
40
|
-
Max:
|
|
47
|
+
Max: 145
|
|
41
48
|
|
|
42
49
|
# Offense count: 2
|
|
43
50
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
@@ -79,11 +86,12 @@ RSpec/ContextWording:
|
|
|
79
86
|
- 'spec/support/shared_contexts/inheritance_api.rb'
|
|
80
87
|
- 'spec/support/shared_contexts/this_api.rb'
|
|
81
88
|
|
|
82
|
-
# Offense count:
|
|
89
|
+
# Offense count: 3
|
|
83
90
|
# Configuration parameters: IgnoredMetadata.
|
|
84
91
|
RSpec/DescribeClass:
|
|
85
92
|
Exclude:
|
|
86
93
|
- '**/spec/features/**/*'
|
|
94
|
+
- '**/spec/issues/**/*'
|
|
87
95
|
- '**/spec/requests/**/*'
|
|
88
96
|
- '**/spec/routing/**/*'
|
|
89
97
|
- '**/spec/system/**/*'
|
|
@@ -93,12 +101,13 @@ RSpec/DescribeClass:
|
|
|
93
101
|
# Offense count: 4
|
|
94
102
|
# Configuration parameters: CountAsOne.
|
|
95
103
|
RSpec/ExampleLength:
|
|
96
|
-
Max:
|
|
104
|
+
Max: 225
|
|
97
105
|
|
|
98
|
-
# Offense count:
|
|
106
|
+
# Offense count: 30
|
|
99
107
|
RSpec/LeakyConstantDeclaration:
|
|
100
108
|
Exclude:
|
|
101
109
|
- 'spec/grape-swagger/entities/response_model_spec.rb'
|
|
110
|
+
- '**/spec/issues/**/*'
|
|
102
111
|
- 'spec/support/shared_contexts/inheritance_api.rb'
|
|
103
112
|
- 'spec/support/shared_contexts/this_api.rb'
|
|
104
113
|
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# grape-swagger-entity
|
|
2
|
+
|
|
3
|
+
Adapter for grape-swagger that parses Grape::Entity classes into OpenAPI schema definitions.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bundle install # Install dependencies
|
|
9
|
+
bundle exec rspec # Run tests
|
|
10
|
+
bundle exec rubocop # Run linter
|
|
11
|
+
bundle exec rubocop -a # Auto-fix linter issues
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Key Constraints
|
|
15
|
+
|
|
16
|
+
- Ruby >= 3.0
|
|
17
|
+
- All files must start with `# frozen_string_literal: true`
|
|
18
|
+
- CHANGELOG.md entry required for every PR (danger enforces this)
|
|
19
|
+
|
|
20
|
+
## Documentation
|
|
21
|
+
|
|
22
|
+
- [Testing Patterns](docs/testing.md)
|
|
23
|
+
- [Contributing Guidelines](docs/contributing.md)
|
|
24
|
+
- [Architecture Overview](docs/architecture.md)
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
### 0.7.1 (2026/01/28)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* [#91](https://github.com/ruby-grape/grape-swagger-entity/pull/91): Improve README and add documentation structure - [@numbata](https://github.com/numbata).
|
|
6
|
+
|
|
7
|
+
#### Fixes
|
|
8
|
+
|
|
9
|
+
* [#90](https://github.com/ruby-grape/grape-swagger-entity/pull/90): Fix description not rendered for non-array entity references - [@numbata](https://github.com/numbata).
|
|
10
|
+
* [#86](https://github.com/ruby-grape/grape-swagger-entity/pull/86): Fix Array[Object] documentation type parsing - [@numbata](https://github.com/numbata).
|
|
11
|
+
* [#87](https://github.com/ruby-grape/grape-swagger-entity/pull/87): Remove hidden attributes from required - [@bogdan](https://github.com/bogdan).
|
|
12
|
+
* [#88](https://github.com/ruby-grape/grape-swagger-entity/pull/88): Update danger workflows - [@numbata](https://github.com/numbata).
|
|
13
|
+
* [#89](https://github.com/ruby-grape/grape-swagger-entity/pull/89): Remove ruby-grape-danger - [@dblock](https://github.com/dblock).
|
|
14
|
+
|
|
1
15
|
### 0.7.0 (2025/08/02)
|
|
2
16
|
|
|
3
17
|
#### Features
|
data/Dangerfile
CHANGED
data/Gemfile
CHANGED
|
@@ -43,7 +43,10 @@ group :development do
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
group :test do
|
|
46
|
+
gem 'danger', require: false
|
|
47
|
+
gem 'danger-changelog', require: false
|
|
48
|
+
gem 'danger-pr-comment', require: false
|
|
49
|
+
gem 'danger-toc', require: false
|
|
46
50
|
gem 'grape-entity', grape_entity_spec
|
|
47
|
-
gem 'ruby-grape-danger', '~> 0.2.1', require: false
|
|
48
51
|
gem 'simplecov', require: false
|
|
49
52
|
end
|
data/README.md
CHANGED
|
@@ -6,15 +6,64 @@
|
|
|
6
6
|
## Table of Contents
|
|
7
7
|
|
|
8
8
|
- [What is grape-swagger-entity?](#what-is-grape-swagger-entity)
|
|
9
|
+
- [What it does](#what-it-does)
|
|
10
|
+
- [Example Output](#example-output)
|
|
11
|
+
- [Related Projects](#related-projects)
|
|
12
|
+
- [Compatibility](#compatibility)
|
|
9
13
|
- [Installation](#installation)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [Basic Entity](#basic-entity)
|
|
16
|
+
- [Custom Model Description](#custom-model-description)
|
|
17
|
+
- [Entity References](#entity-references)
|
|
18
|
+
- [Documentation Options](#documentation-options)
|
|
10
19
|
- [Development](#development)
|
|
11
20
|
- [Contributing](#contributing)
|
|
12
21
|
- [License](#license)
|
|
13
22
|
|
|
14
|
-
|
|
15
23
|
## What is grape-swagger-entity?
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
This gem provides an adapter for [grape-swagger](https://github.com/ruby-grape/grape-swagger) that allows parsing [grape-entity](https://github.com/ruby-grape/grape-entity) classes to generate OpenAPI/Swagger model definitions automatically.
|
|
26
|
+
|
|
27
|
+
### What it does
|
|
28
|
+
|
|
29
|
+
- Generates `definitions` in your Swagger JSON from Grape::Entity exposures
|
|
30
|
+
- Maps entity properties to OpenAPI schema properties with types and descriptions
|
|
31
|
+
- Handles nested entities and entity references via `$ref`
|
|
32
|
+
- Supports arrays, required fields, and documentation options
|
|
33
|
+
|
|
34
|
+
### Example Output
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"definitions": {
|
|
39
|
+
"User": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"description": "User model",
|
|
42
|
+
"properties": {
|
|
43
|
+
"id": { "type": "integer", "description": "User ID" },
|
|
44
|
+
"name": { "type": "string", "description": "Full name" }
|
|
45
|
+
},
|
|
46
|
+
"required": ["id", "name"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Related Projects
|
|
53
|
+
|
|
54
|
+
- [Grape](https://github.com/ruby-grape/grape)
|
|
55
|
+
- [Grape Entity](https://github.com/ruby-grape/grape-entity)
|
|
56
|
+
- [Grape Swagger](https://github.com/ruby-grape/grape-swagger)
|
|
57
|
+
- [Grape Swagger Representable](https://github.com/ruby-grape/grape-swagger-representable)
|
|
58
|
+
|
|
59
|
+
## Compatibility
|
|
60
|
+
|
|
61
|
+
This gem is tested with the following versions:
|
|
62
|
+
|
|
63
|
+
| grape-swagger-entity | grape-swagger | grape-entity | grape |
|
|
64
|
+
|---------------------|---------------|--------------|---------|
|
|
65
|
+
| 0.7.x | >= 2.0.0 | >= 1.0.0 | >= 1.3 |
|
|
66
|
+
| 0.6.x | >= 1.2.0 | >= 0.6.0 | >= 1.3 |
|
|
18
67
|
|
|
19
68
|
## Installation
|
|
20
69
|
|
|
@@ -32,17 +81,63 @@ Or install it yourself as:
|
|
|
32
81
|
|
|
33
82
|
$ gem install grape-swagger-entity
|
|
34
83
|
|
|
35
|
-
##
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
### Basic Entity
|
|
87
|
+
|
|
88
|
+
Define your entities with `documentation` options to generate OpenAPI schema properties:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
class UserEntity < Grape::Entity
|
|
92
|
+
expose :id, documentation: { type: Integer, desc: 'User ID' }
|
|
93
|
+
expose :name, documentation: { type: String, desc: 'Full name' }
|
|
94
|
+
expose :email, documentation: { type: String, desc: 'Email address' }
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Custom Model Description
|
|
36
99
|
|
|
37
|
-
|
|
100
|
+
Override the default "{ModelName} model" description by defining a `self.documentation` method. This feature is handled by [grape-swagger](https://github.com/ruby-grape/grape-swagger) (requires grape-swagger >= 2.2.0):
|
|
38
101
|
|
|
39
|
-
|
|
102
|
+
```ruby
|
|
103
|
+
class UserEntity < Grape::Entity
|
|
104
|
+
def self.documentation
|
|
105
|
+
{ desc: 'Represents a user account with profile information' }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
expose :id, documentation: { type: Integer, desc: 'User ID' }
|
|
109
|
+
expose :name, documentation: { type: String, desc: 'Full name' }
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Entity References
|
|
114
|
+
|
|
115
|
+
Use `using:` to reference other entities and `is_array:` for collections:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
class OrderEntity < Grape::Entity
|
|
119
|
+
expose :id, documentation: { type: Integer, desc: 'Order ID' }
|
|
120
|
+
expose :user, using: UserEntity,
|
|
121
|
+
documentation: { desc: 'The customer who placed this order' }
|
|
122
|
+
expose :items, using: ItemEntity,
|
|
123
|
+
documentation: { desc: 'Line items', is_array: true }
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Documentation Options
|
|
128
|
+
|
|
129
|
+
Common options: `type`, `desc`, `required`, `is_array`, `values`, `example`.
|
|
130
|
+
|
|
131
|
+
See [full documentation options](docs/documentation-options.md) for all available options including validation constraints.
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
See [testing documentation](docs/testing.md) for development setup and running tests.
|
|
40
136
|
|
|
41
137
|
## Contributing
|
|
42
138
|
|
|
43
|
-
|
|
139
|
+
See [contributing guidelines](docs/contributing.md).
|
|
44
140
|
|
|
45
141
|
## License
|
|
46
142
|
|
|
47
143
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
48
|
-
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Architecture Overview
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This gem registers a model parser with grape-swagger that converts `Grape::Entity` classes into OpenAPI schema definitions.
|
|
6
|
+
|
|
7
|
+
## Core Components
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
lib/grape-swagger/entity/
|
|
11
|
+
├── parser.rb # Main parser - converts Entity to OpenAPI schema
|
|
12
|
+
├── attribute_parser.rb # Parses individual exposure attributes
|
|
13
|
+
├── helper.rb # Utility methods for model naming, discriminators
|
|
14
|
+
└── version.rb # Gem version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Parser (`parser.rb`)
|
|
18
|
+
|
|
19
|
+
Entry point for entity parsing. Responsibilities:
|
|
20
|
+
- Extract exposures from Grape::Entity
|
|
21
|
+
- Handle nested entities and `using:` references
|
|
22
|
+
- Process `merge: true` exposures
|
|
23
|
+
- Handle inheritance with `allOf` and discriminators
|
|
24
|
+
- Determine required fields
|
|
25
|
+
|
|
26
|
+
### AttributeParser (`attribute_parser.rb`)
|
|
27
|
+
|
|
28
|
+
Converts individual exposure options to OpenAPI property schema:
|
|
29
|
+
- Maps Ruby types to OpenAPI types
|
|
30
|
+
- Handles arrays (`is_array: true`)
|
|
31
|
+
- Processes enums (`values:`)
|
|
32
|
+
- Adds constraints (min/max, minLength/maxLength)
|
|
33
|
+
|
|
34
|
+
### Helper (`helper.rb`)
|
|
35
|
+
|
|
36
|
+
Utilities for:
|
|
37
|
+
- Model name resolution (strips Entity/Entities suffix)
|
|
38
|
+
- Discriminator detection for inheritance
|
|
39
|
+
- Root exposure extraction
|
|
40
|
+
|
|
41
|
+
## Data Flow
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Grape::Entity class
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
Parser.call
|
|
48
|
+
│
|
|
49
|
+
├── extract_params (get exposures)
|
|
50
|
+
│
|
|
51
|
+
├── parse_grape_entity_params
|
|
52
|
+
│ │
|
|
53
|
+
│ ├── AttributeParser (per exposure)
|
|
54
|
+
│ │
|
|
55
|
+
│ └── parse_nested (for nested blocks)
|
|
56
|
+
│
|
|
57
|
+
└── handle_discriminator (inheritance)
|
|
58
|
+
│
|
|
59
|
+
▼
|
|
60
|
+
[properties_hash, required_array]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Integration Point
|
|
64
|
+
|
|
65
|
+
Registered with grape-swagger in `lib/grape-swagger/entity.rb`:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
GrapeSwagger.model_parsers.register(GrapeSwagger::Entity::Parser, Grape::Entity)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
grape-swagger calls `Parser.new(model, endpoint).call` when it encounters a `Grape::Entity` subclass.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Contributing Guidelines
|
|
2
|
+
|
|
3
|
+
## PR Requirements
|
|
4
|
+
|
|
5
|
+
Every PR must include:
|
|
6
|
+
|
|
7
|
+
1. **CHANGELOG.md entry** - Add under `### X.X.X (Next)` in the appropriate section:
|
|
8
|
+
```markdown
|
|
9
|
+
#### Fixes
|
|
10
|
+
* [#123](https://github.com/ruby-grape/grape-swagger-entity/pull/123): Brief description - [@username](https://github.com/username).
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. **Passing CI** - RuboCop + RSpec must pass
|
|
14
|
+
|
|
15
|
+
3. **Tests** - New functionality needs specs; bug fixes need regression tests
|
|
16
|
+
|
|
17
|
+
## CHANGELOG Format
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
### 0.7.1 (Next)
|
|
21
|
+
|
|
22
|
+
#### Features
|
|
23
|
+
|
|
24
|
+
* Your contribution here.
|
|
25
|
+
|
|
26
|
+
#### Fixes
|
|
27
|
+
|
|
28
|
+
* [#PR](URL): Description - [@author](URL).
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- Use `Features` for new functionality
|
|
32
|
+
- Use `Fixes` for bug fixes and maintenance
|
|
33
|
+
|
|
34
|
+
## Commit Messages
|
|
35
|
+
|
|
36
|
+
Follow conventional style:
|
|
37
|
+
- `Fix #123: description` for bug fixes
|
|
38
|
+
- `Add feature description` for features
|
|
39
|
+
- `Update dependency/docs` for maintenance
|
|
40
|
+
|
|
41
|
+
## Code Style
|
|
42
|
+
|
|
43
|
+
RuboCop enforces style. Key rules:
|
|
44
|
+
- `frozen_string_literal: true` pragma required
|
|
45
|
+
- Consistent hash indentation
|
|
46
|
+
- No trailing whitespace
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bundle exec rubocop -a # Auto-fix most issues
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Danger Checks
|
|
53
|
+
|
|
54
|
+
CI runs danger which enforces:
|
|
55
|
+
- CHANGELOG entry present
|
|
56
|
+
- Table of Contents in README is current
|
|
57
|
+
- PR has description
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Documentation Options
|
|
2
|
+
|
|
3
|
+
The `documentation` hash in entity exposures supports the following options:
|
|
4
|
+
|
|
5
|
+
## Type Options
|
|
6
|
+
|
|
7
|
+
| Option | Description | Example |
|
|
8
|
+
|--------|-------------|---------|
|
|
9
|
+
| `type` | OpenAPI data type | `String`, `Integer`, `Boolean`, `Float`, `Date`, `DateTime` |
|
|
10
|
+
| `is_array` | Marks field as array type | `true` / `false` |
|
|
11
|
+
|
|
12
|
+
## Description Options
|
|
13
|
+
|
|
14
|
+
| Option | Description | Example |
|
|
15
|
+
|--------|-------------|---------|
|
|
16
|
+
| `desc` | Property description | `'User email address'` |
|
|
17
|
+
| `example` | Example value for documentation | `'user@example.com'` |
|
|
18
|
+
| `default` | Default value | `'pending'` |
|
|
19
|
+
|
|
20
|
+
## Validation Options
|
|
21
|
+
|
|
22
|
+
| Option | Description | Example |
|
|
23
|
+
|--------|-------------|---------|
|
|
24
|
+
| `required` | Whether field is required | `true` / `false` |
|
|
25
|
+
| `values` | Enum values (array or proc) | `%w[pending active]` or `-> { Status.all }` |
|
|
26
|
+
| `minimum` | Minimum value for numeric types | `0` |
|
|
27
|
+
| `maximum` | Maximum value for numeric types | `100` |
|
|
28
|
+
| `min_length` | Minimum length for strings | `1` |
|
|
29
|
+
| `max_length` | Maximum length for strings | `255` |
|
|
30
|
+
| `min_items` | Minimum items for arrays | `1` |
|
|
31
|
+
| `max_items` | Maximum items for arrays | `10` |
|
|
32
|
+
| `unique_items` | Array items must be unique | `true` |
|
|
33
|
+
|
|
34
|
+
## Display Options
|
|
35
|
+
|
|
36
|
+
| Option | Description | Example |
|
|
37
|
+
|--------|-------------|---------|
|
|
38
|
+
| `read_only` | Marks field as read-only | `true` |
|
|
39
|
+
| `hidden` | Hide field from documentation | `true` |
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Basic types
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
expose :id, documentation: { type: Integer, desc: 'Unique identifier' }
|
|
47
|
+
expose :name, documentation: { type: String, desc: 'Full name', required: true }
|
|
48
|
+
expose :active, documentation: { type: Boolean, default: true }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Enums
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
expose :status, documentation: {
|
|
55
|
+
type: String,
|
|
56
|
+
desc: 'Current status',
|
|
57
|
+
values: %w[pending active suspended]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Numeric constraints
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
expose :age, documentation: {
|
|
65
|
+
type: Integer,
|
|
66
|
+
minimum: 0,
|
|
67
|
+
maximum: 150
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### String constraints
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
expose :username, documentation: {
|
|
75
|
+
type: String,
|
|
76
|
+
min_length: 3,
|
|
77
|
+
max_length: 20
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Arrays
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
expose :tags, documentation: {
|
|
85
|
+
type: String,
|
|
86
|
+
is_array: true,
|
|
87
|
+
desc: 'Associated tags'
|
|
88
|
+
}
|
|
89
|
+
```
|
data/docs/testing.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Testing Patterns
|
|
2
|
+
|
|
3
|
+
## Running Tests
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bundle exec rspec # Run all tests
|
|
7
|
+
bundle exec rspec spec/path/file.rb # Run specific file
|
|
8
|
+
bundle exec rspec -e "description" # Run matching examples
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Test Structure
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
spec/
|
|
15
|
+
├── grape-swagger/
|
|
16
|
+
│ ├── entity_spec.rb # Main module specs
|
|
17
|
+
│ ├── entity/
|
|
18
|
+
│ │ ├── parser_spec.rb # Parser unit tests
|
|
19
|
+
│ │ └── attribute_parser_spec.rb
|
|
20
|
+
│ └── entities/
|
|
21
|
+
│ └── response_model_spec.rb # Integration tests
|
|
22
|
+
├── issues/ # Regression tests for GitHub issues
|
|
23
|
+
│ └── 962_polymorphic_entity_... # Named by issue number
|
|
24
|
+
└── support/
|
|
25
|
+
└── shared_contexts/ # Reusable test setup
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Shared Contexts
|
|
29
|
+
|
|
30
|
+
Use shared contexts for common API setups:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require 'spec_helper'
|
|
34
|
+
require_relative '../../support/shared_contexts/this_api'
|
|
35
|
+
|
|
36
|
+
describe SomeClass do
|
|
37
|
+
include_context 'this api'
|
|
38
|
+
# ...
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Issue Regression Tests
|
|
43
|
+
|
|
44
|
+
When fixing a bug, create a spec in `spec/issues/` named `{issue_number}_{brief_description}_spec.rb`:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
# spec/issues/123_some_bug_spec.rb
|
|
48
|
+
require 'spec_helper'
|
|
49
|
+
|
|
50
|
+
describe '#123 brief description of the issue' do
|
|
51
|
+
# Test that reproduces and verifies the fix
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Testing with Different Gem Versions
|
|
56
|
+
|
|
57
|
+
Environment variables control dependency versions:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
GRAPE_VERSION=2.0.0 bundle update grape
|
|
61
|
+
GRAPE_SWAGGER_VERSION=HEAD bundle update grape-swagger
|
|
62
|
+
GRAPE_ENTITY_VERSION=1.0.1 bundle update grape-entity
|
|
63
|
+
```
|
|
@@ -20,7 +20,7 @@ module GrapeSwagger
|
|
|
20
20
|
documentation = entity_options[:documentation]
|
|
21
21
|
return param if documentation.nil?
|
|
22
22
|
|
|
23
|
-
add_array_documentation(param, documentation) if documentation
|
|
23
|
+
add_array_documentation(param, documentation) if array_type?(documentation)
|
|
24
24
|
|
|
25
25
|
add_attribute_sample(param, documentation, :default)
|
|
26
26
|
add_attribute_sample(param, documentation, :example)
|
|
@@ -37,11 +37,26 @@ module GrapeSwagger
|
|
|
37
37
|
def model_from(entity_options)
|
|
38
38
|
model = entity_options[:using] if entity_options[:using].present?
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
documentation = entity_options[:documentation]
|
|
41
|
+
model ||= documentation[:type] if could_it_be_a_model?(documentation)
|
|
41
42
|
|
|
42
43
|
model
|
|
43
44
|
end
|
|
44
45
|
|
|
46
|
+
# Checks if documentation indicates an array type that needs items wrapping.
|
|
47
|
+
# Returns true for:
|
|
48
|
+
# - is_array: true (explicit flag)
|
|
49
|
+
# - type: 'Array[Something]' (array with element type)
|
|
50
|
+
# Returns false for:
|
|
51
|
+
# - type: 'Array' (bare array - already an array type, no wrapping needed)
|
|
52
|
+
def array_type?(documentation)
|
|
53
|
+
return false if documentation.nil?
|
|
54
|
+
return documentation[:is_array] if documentation.key?(:is_array)
|
|
55
|
+
|
|
56
|
+
# Only match Array[ElementType] syntax, not bare 'Array'
|
|
57
|
+
documentation[:type].to_s.match?(/\Aarray\[.+\]\z/i)
|
|
58
|
+
end
|
|
59
|
+
|
|
45
60
|
def could_it_be_a_model?(value)
|
|
46
61
|
return false if value.nil?
|
|
47
62
|
|
|
@@ -57,6 +72,8 @@ module GrapeSwagger
|
|
|
57
72
|
end
|
|
58
73
|
|
|
59
74
|
def primitive_type?(type)
|
|
75
|
+
return false if type.nil?
|
|
76
|
+
|
|
60
77
|
data_type = GrapeSwagger::DocMethods::DataType.call(type)
|
|
61
78
|
GrapeSwagger::DocMethods::DataType.request_primitive?(data_type)
|
|
62
79
|
end
|
|
@@ -69,7 +86,7 @@ module GrapeSwagger
|
|
|
69
86
|
|
|
70
87
|
documented_data_type = document_data_type(documentation, data_type)
|
|
71
88
|
|
|
72
|
-
if documentation
|
|
89
|
+
if array_type?(documentation)
|
|
73
90
|
{
|
|
74
91
|
type: :array,
|
|
75
92
|
items: documented_data_type
|
|
@@ -97,7 +114,9 @@ module GrapeSwagger
|
|
|
97
114
|
end
|
|
98
115
|
|
|
99
116
|
def entity_model_type(name, entity_options)
|
|
100
|
-
|
|
117
|
+
documentation = entity_options[:documentation]
|
|
118
|
+
|
|
119
|
+
if array_type?(documentation)
|
|
101
120
|
{
|
|
102
121
|
'type' => 'array',
|
|
103
122
|
'items' => {
|
|
@@ -43,37 +43,72 @@ module GrapeSwagger
|
|
|
43
43
|
def parse_grape_entity_params(params, parent_model = nil)
|
|
44
44
|
return unless params
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
46
|
+
required = required_params(params)
|
|
47
|
+
parsed_params = parse_params(params, parent_model)
|
|
48
|
+
|
|
49
|
+
handle_discriminator(parsed_params, required)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def parse_params(params, parent_model)
|
|
53
|
+
params.each_with_object({}) do |(entity_name, entity_options), memo|
|
|
54
|
+
next if skip_param?(entity_options)
|
|
55
|
+
|
|
56
|
+
original_entity_name = entity_name.is_a?(Alias) ? entity_name.original : entity_name
|
|
57
|
+
final_entity_name = entity_options.fetch(:as, original_entity_name)
|
|
58
|
+
|
|
59
|
+
memo[final_entity_name] = parse_entity_options(entity_options, original_entity_name, parent_model)
|
|
60
|
+
add_documentation_to_memo(memo[final_entity_name], entity_options[:documentation])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def skip_param?(entity_options)
|
|
65
|
+
documentation_options = entity_options.fetch(:documentation, {})
|
|
66
|
+
in_option = documentation_options.fetch(:in, nil).to_s
|
|
67
|
+
hidden_option = documentation_options.fetch(:hidden, nil)
|
|
68
|
+
|
|
69
|
+
in_option == 'header' || hidden_option == true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def parse_entity_options(entity_options, entity_name, parent_model)
|
|
73
|
+
if entity_options[:nesting]
|
|
74
|
+
parse_nested(entity_name, entity_options, parent_model)
|
|
75
|
+
else
|
|
76
|
+
attribute_parser.call(entity_options)
|
|
66
77
|
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def add_documentation_to_memo(memo_entry, documentation)
|
|
81
|
+
return unless documentation
|
|
82
|
+
|
|
83
|
+
has_read_only = documentation[:read_only]
|
|
84
|
+
has_description = documentation[:desc]
|
|
85
|
+
|
|
86
|
+
return unless has_read_only || has_description
|
|
87
|
+
|
|
88
|
+
# In OpenAPI/Swagger 2.0 and 3.0.x, $ref cannot have sibling properties - they are ignored.
|
|
89
|
+
# To add description or readOnly to a $ref, wrap it in allOf.
|
|
90
|
+
# See: https://swagger.io/docs/specification/using-ref/
|
|
91
|
+
wrap_ref_in_all_of(memo_entry) if memo_entry.key?('$ref')
|
|
67
92
|
|
|
93
|
+
memo_entry[:readOnly] = documentation[:read_only].to_s == 'true' if has_read_only
|
|
94
|
+
memo_entry[:description] = documentation[:desc] if has_description
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def wrap_ref_in_all_of(memo_entry)
|
|
98
|
+
ref = memo_entry.delete('$ref')
|
|
99
|
+
memo_entry['allOf'] = [{ '$ref' => ref }]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def handle_discriminator(parsed, required)
|
|
68
103
|
discriminator = GrapeSwagger::Entity::Helper.discriminator(model)
|
|
69
104
|
if discriminator
|
|
70
|
-
respond_with_all_of(parsed,
|
|
105
|
+
respond_with_all_of(parsed, required, discriminator)
|
|
71
106
|
else
|
|
72
|
-
[parsed,
|
|
107
|
+
[parsed, required]
|
|
73
108
|
end
|
|
74
109
|
end
|
|
75
110
|
|
|
76
|
-
def respond_with_all_of(parsed,
|
|
111
|
+
def respond_with_all_of(parsed, required, discriminator)
|
|
77
112
|
parent_name = GrapeSwagger::Entity::Helper.model_name(model.superclass, endpoint)
|
|
78
113
|
|
|
79
114
|
{
|
|
@@ -83,7 +118,7 @@ module GrapeSwagger
|
|
|
83
118
|
},
|
|
84
119
|
[
|
|
85
120
|
add_discriminator(parsed, discriminator),
|
|
86
|
-
|
|
121
|
+
required.push(discriminator.attribute)
|
|
87
122
|
]
|
|
88
123
|
]
|
|
89
124
|
}
|
|
@@ -136,8 +171,11 @@ module GrapeSwagger
|
|
|
136
171
|
|
|
137
172
|
def required_params(params)
|
|
138
173
|
params.each_with_object(Set.new) do |(key, options), accum|
|
|
139
|
-
|
|
140
|
-
|
|
174
|
+
documentation = options.fetch(:documentation, {})
|
|
175
|
+
next if documentation[:hidden]
|
|
176
|
+
|
|
177
|
+
required = if documentation.key?(:required)
|
|
178
|
+
documentation[:required]
|
|
141
179
|
else
|
|
142
180
|
!options.key?(:if) && !options.key?(:unless) && options[:expose_nil] != false
|
|
143
181
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: grape-swagger-entity
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kirill Zaitsev
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-01-28 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: grape-entity
|
|
@@ -37,18 +38,21 @@ dependencies:
|
|
|
37
38
|
- - "~>"
|
|
38
39
|
- !ruby/object:Gem::Version
|
|
39
40
|
version: '2'
|
|
41
|
+
description:
|
|
40
42
|
email:
|
|
41
43
|
- kirik910@gmail.com
|
|
42
44
|
executables: []
|
|
43
45
|
extensions: []
|
|
44
46
|
extra_rdoc_files: []
|
|
45
47
|
files:
|
|
48
|
+
- ".github/workflows/danger-comment.yml"
|
|
46
49
|
- ".github/workflows/danger.yml"
|
|
47
50
|
- ".github/workflows/test.yml"
|
|
48
51
|
- ".gitignore"
|
|
49
52
|
- ".rspec"
|
|
50
53
|
- ".rubocop.yml"
|
|
51
54
|
- ".rubocop_todo.yml"
|
|
55
|
+
- AGENTS.md
|
|
52
56
|
- CHANGELOG.md
|
|
53
57
|
- Dangerfile
|
|
54
58
|
- Gemfile
|
|
@@ -59,6 +63,10 @@ files:
|
|
|
59
63
|
- UPGRADING.md
|
|
60
64
|
- bin/pry
|
|
61
65
|
- bin/setup
|
|
66
|
+
- docs/architecture.md
|
|
67
|
+
- docs/contributing.md
|
|
68
|
+
- docs/documentation-options.md
|
|
69
|
+
- docs/testing.md
|
|
62
70
|
- grape-swagger-entity.gemspec
|
|
63
71
|
- lib/grape-swagger-entity.rb
|
|
64
72
|
- lib/grape-swagger/entity.rb
|
|
@@ -71,6 +79,7 @@ licenses:
|
|
|
71
79
|
- MIT
|
|
72
80
|
metadata:
|
|
73
81
|
rubygems_mfa_required: 'true'
|
|
82
|
+
post_install_message:
|
|
74
83
|
rdoc_options: []
|
|
75
84
|
require_paths:
|
|
76
85
|
- lib
|
|
@@ -85,7 +94,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
85
94
|
- !ruby/object:Gem::Version
|
|
86
95
|
version: '0'
|
|
87
96
|
requirements: []
|
|
88
|
-
rubygems_version: 3.
|
|
97
|
+
rubygems_version: 3.5.22
|
|
98
|
+
signing_key:
|
|
89
99
|
specification_version: 4
|
|
90
100
|
summary: Grape swagger adapter to support grape-entity object parsing
|
|
91
101
|
test_files: []
|