sober_swag 0.11.0 → 0.16.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.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +44 -9
- data/.gitignore +2 -0
- data/.rubocop.yml +50 -5
- data/CHANGELOG.md +22 -0
- data/README.md +141 -4
- data/bin/console +18 -30
- data/bin/rspec +29 -0
- data/docs/serializers.md +74 -9
- data/example/app/controllers/application_controller.rb +5 -0
- data/lib/sober_swag.rb +1 -0
- data/lib/sober_swag/compiler.rb +1 -0
- data/lib/sober_swag/compiler/primitive.rb +75 -0
- data/lib/sober_swag/compiler/type.rb +56 -92
- data/lib/sober_swag/controller.rb +3 -9
- data/lib/sober_swag/controller/route.rb +21 -8
- data/lib/sober_swag/input_object.rb +42 -2
- data/lib/sober_swag/nodes/attribute.rb +8 -7
- data/lib/sober_swag/nodes/enum.rb +2 -2
- data/lib/sober_swag/nodes/primitive.rb +1 -1
- data/lib/sober_swag/output_object/definition.rb +14 -2
- data/lib/sober_swag/output_object/field_syntax.rb +16 -0
- data/lib/sober_swag/parser.rb +10 -4
- data/lib/sober_swag/serializer/base.rb +2 -0
- data/lib/sober_swag/serializer/meta.rb +10 -6
- data/lib/sober_swag/type.rb +7 -0
- data/lib/sober_swag/type/named.rb +35 -0
- data/lib/sober_swag/types.rb +2 -0
- data/lib/sober_swag/types/comma_array.rb +17 -0
- data/lib/sober_swag/version.rb +1 -1
- data/sober_swag.gemspec +2 -2
- metadata +16 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad86b8f4701d16ec53f0fc998bb2a881cdc448acfbb509bbfeb7821cc7142d1b
|
4
|
+
data.tar.gz: ce70ffa29ca02b8fc70e7f78c0b41bf56e8e3cdffbcf7282d8dad1cf16717b23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '081ef6717877b1c40682fbcbf1436a7e44fd51307a03650edc9a6d4a594a99bc4e9f07adbb73a9073f784aa75dc6a5463f0aa1d77b77fefc976c0531273ced22'
|
7
|
+
data.tar.gz: c1fb5da8a19b9d6ee1324badbbff4caac4db49349efd301b9daaa74097372f2241e174617af5b59970b467610145419b6e392b579fc4a3457073439e5ec6824e
|
data/.github/workflows/lint.yml
CHANGED
@@ -1,15 +1,50 @@
|
|
1
|
-
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
2
7
|
|
3
|
-
|
8
|
+
name: Ruby Lint
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ master ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ master ]
|
4
15
|
|
5
16
|
jobs:
|
6
|
-
|
17
|
+
test:
|
18
|
+
|
7
19
|
runs-on: ubuntu-latest
|
20
|
+
strategy:
|
21
|
+
matrix:
|
22
|
+
ruby: [ '2.6', '2.7' ]
|
23
|
+
|
8
24
|
steps:
|
9
|
-
- uses: actions/checkout@
|
10
|
-
- name:
|
11
|
-
|
25
|
+
- uses: actions/checkout@v2
|
26
|
+
- name: Set up Ruby
|
27
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
28
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
29
|
+
# uses: ruby/setup-ruby@v1
|
30
|
+
uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby }}
|
33
|
+
- uses: actions/cache@v2
|
34
|
+
with:
|
35
|
+
path: vendor/bundle
|
36
|
+
key: ${{ runner.os }}-${{ matrix.ruby }}-gem-deps-${{ hashFiles('**/Gemfile.lock') }}
|
37
|
+
restore-keys: |
|
38
|
+
${{ runner.os }}-${{ matrix.ruby }}-gem-deps-
|
39
|
+
- name: Install dependencies
|
40
|
+
run: |
|
41
|
+
bundle config path vendor/bundle
|
42
|
+
bundle install
|
43
|
+
- name: Run Lints
|
44
|
+
run: bundle exec rubocop -P
|
45
|
+
- uses: actions/cache@v2
|
12
46
|
with:
|
13
|
-
|
14
|
-
|
15
|
-
|
47
|
+
path: example/vendor/bundle
|
48
|
+
key: ${{ runner.os }}-${{ matrix.ruby }}-example-deps-${{ hashFiles('example/**/Gemfile.lock') }}
|
49
|
+
restore-keys: |
|
50
|
+
${{ runner.os }}-${{ matrix.ruby }}-example-deps-
|
data/.rubocop.yml
CHANGED
@@ -1,81 +1,126 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
Style/BlockDelimiters:
|
4
|
-
EnforcedStyle: braces_for_chaining
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
5
3
|
AllCops:
|
6
4
|
TargetRubyVersion: 2.6.0
|
7
5
|
Exclude:
|
8
6
|
- 'bin/bundle'
|
9
7
|
- 'example/bin/bundle'
|
8
|
+
- 'vendor/bundle/**/*'
|
10
9
|
|
11
10
|
Layout/LineLength:
|
12
11
|
Max: 160
|
13
|
-
|
12
|
+
|
14
13
|
RSpec/NamedSubject:
|
15
14
|
Enabled: false
|
15
|
+
|
16
16
|
RSpec/DescribeClass:
|
17
17
|
Enabled: false
|
18
|
+
|
18
19
|
Metrics/BlockLength:
|
19
20
|
Exclude:
|
20
21
|
- 'spec/**/*.rb'
|
21
22
|
- 'sober_swag.gemspec'
|
22
23
|
- 'example/spec/**/*.rb'
|
24
|
+
|
23
25
|
RSpec/ImplicitBlockExpectation:
|
24
26
|
Enabled: false
|
27
|
+
|
25
28
|
RSpec/ImplicitExpect:
|
26
29
|
EnforcedStyle: should
|
30
|
+
|
31
|
+
RSpec/LeadingSubject:
|
32
|
+
Enabled: false
|
33
|
+
|
27
34
|
Style/MultilineBlockChain:
|
28
35
|
Enabled: false
|
36
|
+
|
29
37
|
Metrics/AbcSize:
|
30
38
|
Enabled: false
|
39
|
+
|
31
40
|
Style/Documentation:
|
32
41
|
Exclude:
|
33
42
|
- 'example/db/migrate/**/*'
|
43
|
+
|
34
44
|
Metrics/PerceivedComplexity:
|
35
45
|
Enabled: false
|
46
|
+
|
36
47
|
Layout/EmptyLinesAroundAttributeAccessor:
|
37
48
|
Enabled: true
|
49
|
+
|
38
50
|
Layout/SpaceAroundMethodCallOperator:
|
39
51
|
Enabled: true
|
52
|
+
|
40
53
|
Lint/DeprecatedOpenSSLConstant:
|
41
54
|
Enabled: true
|
55
|
+
|
42
56
|
Lint/DuplicateElsifCondition:
|
43
57
|
Enabled: true
|
58
|
+
|
44
59
|
Lint/MixedRegexpCaptureTypes:
|
45
60
|
Enabled: true
|
61
|
+
|
46
62
|
Lint/RaiseException:
|
47
63
|
Enabled: true
|
64
|
+
|
48
65
|
Lint/StructNewOverride:
|
49
66
|
Enabled: true
|
67
|
+
|
50
68
|
Style/AccessorGrouping:
|
51
69
|
Enabled: true
|
70
|
+
|
52
71
|
Style/ArrayCoercion:
|
53
72
|
Enabled: true
|
73
|
+
|
54
74
|
Style/BisectedAttrAccessor:
|
55
75
|
Enabled: true
|
76
|
+
|
56
77
|
Style/CaseLikeIf:
|
57
78
|
Enabled: true
|
79
|
+
|
58
80
|
Style/ExponentialNotation:
|
59
81
|
Enabled: true
|
82
|
+
|
60
83
|
Style/HashAsLastArrayItem:
|
61
84
|
Enabled: true
|
85
|
+
|
62
86
|
Style/HashEachMethods:
|
63
87
|
Enabled: true
|
88
|
+
|
64
89
|
Style/HashLikeCase:
|
65
90
|
Enabled: true
|
91
|
+
|
66
92
|
Style/HashTransformKeys:
|
67
93
|
Enabled: true
|
94
|
+
|
68
95
|
Style/HashTransformValues:
|
69
96
|
Enabled: true
|
97
|
+
|
70
98
|
Style/RedundantAssignment:
|
71
99
|
Enabled: true
|
100
|
+
|
72
101
|
Style/RedundantFetchBlock:
|
73
102
|
Enabled: true
|
103
|
+
|
74
104
|
Style/RedundantFileExtensionInRequire:
|
75
105
|
Enabled: true
|
106
|
+
|
76
107
|
Style/RedundantRegexpCharacterClass:
|
77
108
|
Enabled: true
|
109
|
+
|
78
110
|
Style/RedundantRegexpEscape:
|
79
111
|
Enabled: true
|
112
|
+
|
80
113
|
Style/SlicingWithRange:
|
81
114
|
Enabled: true
|
115
|
+
|
116
|
+
RSpec/NestedGroups:
|
117
|
+
Max: 5
|
118
|
+
|
119
|
+
RSpec/ExampleLength:
|
120
|
+
Max: 10
|
121
|
+
|
122
|
+
Style/FrozenStringLiteralComment:
|
123
|
+
Enabled: false
|
124
|
+
|
125
|
+
Style/BlockDelimiters:
|
126
|
+
EnforcedStyle: braces_for_chaining
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [v0.16.0]: 2020-10-23
|
4
|
+
|
5
|
+
- Allow non-class types to be used as the inputs to controllers
|
6
|
+
|
7
|
+
## [v0.15.0]: 2020-09-02
|
8
|
+
|
9
|
+
### Added
|
10
|
+
- Add a new `#merge` method to output objects, which will merge fields from another output object into the given output object.
|
11
|
+
- Add `multi` to Output Objects, as a way to define more than one field of the same type at once.
|
12
|
+
- Add an `inherits:` key to output objects, for view inheritance.
|
13
|
+
- Add `SoberSwag::Types::CommaArray`, which parses comma-separated strings into arrays.
|
14
|
+
This also sets `style` to `form` and `explode` to `false` when generating Swagger docs.
|
15
|
+
This class is mostly useful for query parameters where you want a simpler format: `tag=foo,bar` instead of `tag[]=foo,tag[]=bar`.
|
16
|
+
- Add support for using `meta` to specify alternative `style` and `explode` keys for query and path params.
|
17
|
+
Note that this support *does not* extend to parsing: If you modify the `style` or `explode` keywords, you will need to make those input formats work with the actual type yourself.
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
- No longer swallow `Dry::Struct` errors, instead let them surface to the user.
|
21
|
+
|
22
|
+
[v0.15.0]: https://github.com/SonderMindOrg/sober_swag/releases/tag/v0.15.0
|
data/README.md
CHANGED
@@ -9,6 +9,10 @@ This generates documentation from *types*, which (conveniently) also lets you ge
|
|
9
9
|
|
10
10
|
An introductory presentation is available [here](https://www.icloud.com/keynote/0bxP3Dn8ETNO0lpsSQSVfEL6Q#SoberSwagPresentation).
|
11
11
|
|
12
|
+
Further documentation on using the gem is available in the `docs/` directory:
|
13
|
+
|
14
|
+
- [Serializers](docs/serializers.md)
|
15
|
+
|
12
16
|
## Types for a fully-automated API
|
13
17
|
|
14
18
|
SoberSwag lets you type your API using describe blocks.
|
@@ -18,7 +22,14 @@ This lets you type your API endpoint:
|
|
18
22
|
```ruby
|
19
23
|
class PeopleController < ApplicationController
|
20
24
|
include SoberSwag::Controller
|
25
|
+
|
21
26
|
define :patch, :update, '/people/{id}' do
|
27
|
+
summary 'Update a Person record.'
|
28
|
+
description <<~MARKDOWN
|
29
|
+
You can use this endpoint to update a Person record. Note that age cannot
|
30
|
+
be a negative integer.
|
31
|
+
MARKDOWN
|
32
|
+
|
22
33
|
query_params do
|
23
34
|
attribute? :include_extra_info, Types::Params::Bool
|
24
35
|
end
|
@@ -28,11 +39,13 @@ class PeopleController < ApplicationController
|
|
28
39
|
end
|
29
40
|
path_params { attribute :id, Types::Params::Integer }
|
30
41
|
end
|
42
|
+
def update
|
43
|
+
# update action here
|
44
|
+
end
|
31
45
|
end
|
32
46
|
```
|
33
47
|
|
34
|
-
|
35
|
-
More than that, we can use this information *inside* our controller methods:
|
48
|
+
Then we can use the information from our SoberSwag definition *inside* the controller method:
|
36
49
|
|
37
50
|
```ruby
|
38
51
|
def update
|
@@ -44,6 +57,51 @@ end
|
|
44
57
|
No need for `params.require` or anything like that.
|
45
58
|
You define the type of parameters you accept, and we reject anything that doesn't fit.
|
46
59
|
|
60
|
+
### Rendering Swagger documentation from SoberSwag
|
61
|
+
|
62
|
+
We can also use the information from SoberSwag objects to generate Swagger
|
63
|
+
documentation, available at the `swagger` action on this controller.
|
64
|
+
|
65
|
+
You can create the `swagger` action for a controller as follows:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# config/routes.rb
|
69
|
+
Rails.application.routes.draw do
|
70
|
+
# Add a `swagger` GET endpoint to render the Swagger documentation created
|
71
|
+
# by SoberSwag.
|
72
|
+
resources :people do
|
73
|
+
get :swagger, on: :collection
|
74
|
+
end
|
75
|
+
|
76
|
+
# Or use a concern to make it easier to enable swagger endpoints for a number
|
77
|
+
# of controllers at once.
|
78
|
+
concern :swaggerable do
|
79
|
+
get :swagger, on: :collection
|
80
|
+
end
|
81
|
+
|
82
|
+
resources :people, concerns: :swaggerable do
|
83
|
+
get :search, on: :collection
|
84
|
+
end
|
85
|
+
|
86
|
+
resources :places, only: [:index], concerns: :swaggerable
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
If you don't want the API documentation to show up in certain cases, you can
|
91
|
+
use an environment variable or a check on the current Rails environment.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
# config/routes.rb
|
95
|
+
Rails.application.routes.draw do
|
96
|
+
resources :people do
|
97
|
+
# Enable based on environment variable.
|
98
|
+
get :swagger, on: :collection if ENV['ENABLE_SWAGGER']
|
99
|
+
# Or just disable in production.
|
100
|
+
get :swagger, on: :collection unless Rails.env.production?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
47
105
|
### Typed Responses
|
48
106
|
|
49
107
|
Want to go further and type your responses too?
|
@@ -53,6 +111,8 @@ Use SoberSwag output objects, a serializer library heavily inspired by [Blueprin
|
|
53
111
|
PersonOutputObject = SoberSwag::OutputObject.define do
|
54
112
|
field :id, primitive(:Integer)
|
55
113
|
field :name, primitive(:String).optional
|
114
|
+
# For fields that don't map to a simple attribute on your model, you can
|
115
|
+
# use a block.
|
56
116
|
field :is_registered, primitive(:Bool) do |person|
|
57
117
|
person.registered?
|
58
118
|
end
|
@@ -95,7 +155,7 @@ User = SoberSwag.input_object do
|
|
95
155
|
attribute :name, SoberSwag::Types::String
|
96
156
|
# use ? if attributes are not required
|
97
157
|
attribute? :favorite_movie, SoberSwag::Types::String
|
98
|
-
# use .optional if attributes may be
|
158
|
+
# use .optional if attributes may be nil
|
99
159
|
attribute :age, SoberSwag::Types::Params::Integer.optional
|
100
160
|
end
|
101
161
|
```
|
@@ -120,6 +180,63 @@ end
|
|
120
180
|
Under the hood, this literally just generates a subclass of `Dry::Struct`.
|
121
181
|
We use the DSL-like method just to make working with Rails' reloading less annoying.
|
122
182
|
|
183
|
+
#### Nested object attributes
|
184
|
+
|
185
|
+
You can nest attributes using a block. They'll return as nested JSON objects.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
User = SoberSwag.input_object do
|
189
|
+
attribute :user_notes do
|
190
|
+
attribute :note, SoberSwag::Types::String
|
191
|
+
end
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
If you want to use a specific type of object within an input object, you can
|
196
|
+
nest them by setting the other input object as the type of an attribute. For
|
197
|
+
example, if you had a UserGroup object with various Users, you could write
|
198
|
+
them like this:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
User = SoberSwag.input_object do
|
202
|
+
attribute :name, SoberSwag::Types::String
|
203
|
+
attribute :age, SoberSwag::Types::Params::Integer.optional
|
204
|
+
end
|
205
|
+
|
206
|
+
UserGroup = SoberSwag.input_object do
|
207
|
+
attribute :name, SoberSwag::Types::String
|
208
|
+
attribute :users, SoberSwag::Types::Array.of(User)
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
#### Input and Output Object Identifiers
|
213
|
+
|
214
|
+
Both input objects and output objects accept an identifier, which is used in
|
215
|
+
the Swagger Documentation to disambiguate between SoberSwag types.
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
User = SoberSwag.input_object do
|
219
|
+
identifier 'User'
|
220
|
+
|
221
|
+
attribute? :name, SoberSwag::Types::String
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
PersonOutputObject = SoberSwag::OutputObject.define do
|
227
|
+
identifier 'PersonOutput'
|
228
|
+
|
229
|
+
field :id, primitive(:Integer)
|
230
|
+
field :name, primitive(:String).optional
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
You can use these to make your Swagger documentation a bit easier to follow,
|
235
|
+
and it can also be useful for 'namespacing' objects if you're developing in
|
236
|
+
a large application, e.g. if you had a pet store and for some reason users
|
237
|
+
with cats and users with dogs were different, you could namespace it with
|
238
|
+
`identifier 'Dogs.User'`.
|
239
|
+
|
123
240
|
#### Adding additional documentation
|
124
241
|
|
125
242
|
You can use the `.meta` attribute on a type to add additional documentation.
|
@@ -151,10 +268,30 @@ QueryInput = SoberSwag.input_object do
|
|
151
268
|
end
|
152
269
|
```
|
153
270
|
|
271
|
+
## Testing the validity of output objects
|
272
|
+
|
273
|
+
If you're using RSpec and want to test the validity of output objects, you can do so relatively easily.
|
274
|
+
|
275
|
+
For example, assuming that you have a `UserOutputObject` class for representing a User record, and you have a `:user` factory via FactoryBot, you can validate that the serialization works without error like so:
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
RSpec.describe UserOutputObject do
|
279
|
+
describe 'serialized result' do
|
280
|
+
subject do
|
281
|
+
described_class.type.new(described_class.serialize(create(:user)))
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'works with an object' do
|
285
|
+
expect { subject }.not_to raise_error
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
```
|
290
|
+
|
154
291
|
## Special Thanks
|
155
292
|
|
156
293
|
This gem is a mishmash of ideas from various sources.
|
157
294
|
The biggest thanks is owed to the [dry-rb](https://github.com/dry-rb) project, upon which the typing of SoberSwag is based.
|
158
295
|
On an API design level, much is owed to [blueprinter](https://github.com/procore/blueprinter) for the serializers.
|
159
296
|
The idea of a strongly-typed API came from the Haskell framework [servant](https://www.servant.dev/).
|
160
|
-
Generating the swagger
|
297
|
+
Generating the swagger documentation happens via the use of a catamorphism, which I believe I first really understood thanks to [this medium article by Jared Tobin](https://medium.com/@jaredtobin/practical-recursion-schemes-c10648ec1c29).
|
data/bin/console
CHANGED
@@ -1,48 +1,36 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require 'bundler/setup'
|
5
4
|
require 'sober_swag'
|
6
|
-
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
9
|
-
module Types
|
10
|
-
include Dry.Types()
|
11
|
-
end
|
5
|
+
require 'pry'
|
12
6
|
|
13
7
|
Bio = SoberSwag.input_object do
|
14
|
-
attribute :
|
8
|
+
attribute :description, SoberSwag::Types::String
|
9
|
+
attribute :gender, SoberSwag::Types::String.enum('male', 'female') | SoberSwag::Types::String
|
15
10
|
end
|
16
11
|
|
17
12
|
Person = SoberSwag.input_object do
|
18
|
-
attribute :name,
|
19
|
-
attribute :
|
20
|
-
attribute? :mood, Types::String
|
13
|
+
attribute :name, SoberSwag::Types::String
|
14
|
+
attribute? :bio, Bio.optional
|
21
15
|
end
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
attribute
|
27
|
-
attribute? :age, Types::Integer
|
17
|
+
MultiFloorLocation = SoberSwag.input_object do
|
18
|
+
attribute :building, SoberSwag::Types::String.enum('science', 'mathematics', 'literature')
|
19
|
+
attribute :floor, SoberSwag::Types::String
|
20
|
+
attribute :room, SoberSwag::Types::Integer
|
28
21
|
end
|
29
22
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
attribute :value, Types::String
|
34
|
-
attribute? :next, LinkedList
|
23
|
+
SingleFloorLocation = SoberSwag.input_object do
|
24
|
+
attribute :building, SoberSwag::Types::String.enum('philosophy', 'computer science')
|
25
|
+
attribute :room, SoberSwag::Types::Integer
|
35
26
|
end
|
36
27
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
view :foo do
|
42
|
-
field :bar, primitive(:String).optional
|
43
|
-
end
|
28
|
+
SchoolClass = SoberSwag.input_object do
|
29
|
+
attribute :prof, Person.meta(description: 'The person who teaches this class.')
|
30
|
+
attribute :students, SoberSwag::Types::Array.of(Person)
|
31
|
+
attribute :location, (SingleFloorLocation | MultiFloorLocation).meta(description: 'What building and room this is in')
|
44
32
|
end
|
45
33
|
|
46
|
-
|
47
|
-
|
34
|
+
SortDirections = SoberSwag::Types::CommaArray.of(SoberSwag::Types::String.enum('created_at', 'updated_at', '-created_at', '-updated_at'))
|
35
|
+
|
48
36
|
Pry.start
|