sober_swag 0.15.0 → 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/lint.yml +4 -9
- data/.github/workflows/ruby.yml +2 -6
- data/.gitignore +4 -0
- data/.rubocop.yml +50 -5
- data/.yardopts +7 -0
- data/CHANGELOG.md +29 -1
- data/Gemfile +8 -0
- data/README.md +155 -4
- data/bin/rspec +29 -0
- data/docs/serializers.md +18 -13
- data/example/Gemfile +2 -2
- data/example/app/controllers/people_controller.rb +4 -0
- data/example/app/controllers/posts_controller.rb +5 -0
- data/example/config/environments/production.rb +1 -1
- data/lib/sober_swag.rb +6 -1
- data/lib/sober_swag/compiler.rb +29 -3
- data/lib/sober_swag/compiler/path.rb +49 -3
- data/lib/sober_swag/compiler/paths.rb +20 -0
- data/lib/sober_swag/compiler/primitive.rb +20 -1
- data/lib/sober_swag/compiler/type.rb +105 -22
- data/lib/sober_swag/controller.rb +42 -15
- data/lib/sober_swag/controller/route.rb +133 -28
- data/lib/sober_swag/input_object.rb +117 -7
- data/lib/sober_swag/nodes/array.rb +19 -0
- data/lib/sober_swag/nodes/attribute.rb +45 -4
- data/lib/sober_swag/nodes/base.rb +27 -7
- data/lib/sober_swag/nodes/binary.rb +30 -13
- data/lib/sober_swag/nodes/enum.rb +16 -1
- data/lib/sober_swag/nodes/list.rb +20 -0
- data/lib/sober_swag/nodes/nullable_primitive.rb +3 -0
- data/lib/sober_swag/nodes/object.rb +4 -1
- data/lib/sober_swag/nodes/one_of.rb +11 -3
- data/lib/sober_swag/nodes/primitive.rb +34 -2
- data/lib/sober_swag/nodes/sum.rb +8 -0
- data/lib/sober_swag/output_object.rb +35 -4
- data/lib/sober_swag/output_object/definition.rb +31 -1
- data/lib/sober_swag/output_object/field.rb +31 -11
- data/lib/sober_swag/output_object/field_syntax.rb +19 -3
- data/lib/sober_swag/output_object/view.rb +46 -1
- data/lib/sober_swag/parser.rb +7 -1
- data/lib/sober_swag/serializer/array.rb +27 -3
- data/lib/sober_swag/serializer/base.rb +75 -25
- data/lib/sober_swag/serializer/conditional.rb +33 -1
- data/lib/sober_swag/serializer/field_list.rb +18 -2
- data/lib/sober_swag/serializer/mapped.rb +10 -1
- data/lib/sober_swag/serializer/optional.rb +18 -1
- data/lib/sober_swag/serializer/primitive.rb +3 -0
- data/lib/sober_swag/server.rb +27 -11
- data/lib/sober_swag/type/named.rb +14 -0
- data/lib/sober_swag/types/comma_array.rb +4 -0
- data/lib/sober_swag/version.rb +1 -1
- data/sober_swag.gemspec +2 -2
- metadata +13 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97cb4bbfd84f28f6f79368c3f78182835217868a1d3142c38107ab3b03552952
|
4
|
+
data.tar.gz: 1cd8446250a8ddadc16eb114a773b9d317c2182e2bcb8c692c59681331ef972b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6b055ea451db16f12a02ebe2bc6da459aefcf13605a54f4f0b0cff8aa694b73e2c4cb2c2f759a89c859a820a74a37391728998e0939a770cda2783c2190586e
|
7
|
+
data.tar.gz: 3a49c153297750447c2776b086da135b81c80a1b628780d9a7ab6f68f204ddeaa3b21e08ffd02f8a49e92f1a7ac5942b0e42d295307299e8623ebb6a765731dd
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "bundler"
|
9
|
+
directory: "/"
|
10
|
+
schedule:
|
11
|
+
interval: "daily"
|
12
|
+
- package-ecosystem: "bundler"
|
13
|
+
directory: "/example"
|
14
|
+
schedule:
|
15
|
+
interval: "daily"
|
data/.github/workflows/lint.yml
CHANGED
@@ -19,17 +19,14 @@ jobs:
|
|
19
19
|
runs-on: ubuntu-latest
|
20
20
|
strategy:
|
21
21
|
matrix:
|
22
|
-
ruby: [ '2.6', '2.7' ]
|
22
|
+
ruby: [ '2.6', '2.7', '3.0' ]
|
23
23
|
|
24
24
|
steps:
|
25
25
|
- uses: actions/checkout@v2
|
26
26
|
- name: Set up Ruby
|
27
|
-
|
28
|
-
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
29
|
-
# uses: ruby/setup-ruby@v1
|
30
|
-
uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
|
27
|
+
uses: ruby/setup-ruby@v1
|
31
28
|
with:
|
32
|
-
ruby-version:
|
29
|
+
ruby-version: ${{ matrix.ruby }}
|
33
30
|
- uses: actions/cache@v2
|
34
31
|
with:
|
35
32
|
path: vendor/bundle
|
@@ -40,10 +37,8 @@ jobs:
|
|
40
37
|
run: |
|
41
38
|
bundle config path vendor/bundle
|
42
39
|
bundle install
|
43
|
-
gem install rubocop
|
44
|
-
gem install rubocop-rspec
|
45
40
|
- name: Run Lints
|
46
|
-
run:
|
41
|
+
run: bundle exec rubocop -P
|
47
42
|
- uses: actions/cache@v2
|
48
43
|
with:
|
49
44
|
path: example/vendor/bundle
|
data/.github/workflows/ruby.yml
CHANGED
@@ -19,15 +19,11 @@ jobs:
|
|
19
19
|
runs-on: ubuntu-latest
|
20
20
|
strategy:
|
21
21
|
matrix:
|
22
|
-
ruby: [ '2.6', '2.7' ]
|
23
|
-
|
22
|
+
ruby: [ '2.6', '2.7', '3.0' ]
|
24
23
|
steps:
|
25
24
|
- uses: actions/checkout@v2
|
26
25
|
- name: Set up Ruby
|
27
|
-
|
28
|
-
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
29
|
-
# uses: ruby/setup-ruby@v1
|
30
|
-
uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
|
26
|
+
uses: ruby/setup-ruby@v1
|
31
27
|
with:
|
32
28
|
ruby-version: ${{ matrix.ruby }}
|
33
29
|
- uses: actions/cache@v2
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,85 +1,130 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
Style/BlockDelimiters:
|
4
|
-
EnforcedStyle: braces_for_chaining
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
5
3
|
AllCops:
|
6
4
|
TargetRubyVersion: 2.6.0
|
5
|
+
NewCops: enable
|
7
6
|
Exclude:
|
8
7
|
- 'bin/bundle'
|
9
8
|
- 'example/bin/bundle'
|
9
|
+
- 'vendor/bundle/**/*'
|
10
10
|
|
11
11
|
Layout/LineLength:
|
12
12
|
Max: 160
|
13
|
-
|
13
|
+
|
14
14
|
RSpec/NamedSubject:
|
15
15
|
Enabled: false
|
16
|
+
|
16
17
|
RSpec/DescribeClass:
|
17
18
|
Enabled: false
|
19
|
+
|
18
20
|
Metrics/BlockLength:
|
19
21
|
Exclude:
|
20
22
|
- 'spec/**/*.rb'
|
21
23
|
- 'sober_swag.gemspec'
|
22
24
|
- 'example/spec/**/*.rb'
|
25
|
+
|
26
|
+
Lint/MissingSuper:
|
27
|
+
Enabled: false
|
28
|
+
|
23
29
|
RSpec/ImplicitBlockExpectation:
|
24
30
|
Enabled: false
|
31
|
+
|
25
32
|
RSpec/ImplicitExpect:
|
26
33
|
EnforcedStyle: should
|
34
|
+
|
27
35
|
RSpec/LeadingSubject:
|
28
36
|
Enabled: false
|
37
|
+
|
29
38
|
Style/MultilineBlockChain:
|
30
39
|
Enabled: false
|
40
|
+
|
31
41
|
Metrics/AbcSize:
|
32
42
|
Enabled: false
|
43
|
+
|
33
44
|
Style/Documentation:
|
34
45
|
Exclude:
|
35
46
|
- 'example/db/migrate/**/*'
|
47
|
+
|
36
48
|
Metrics/PerceivedComplexity:
|
37
49
|
Enabled: false
|
50
|
+
|
38
51
|
Layout/EmptyLinesAroundAttributeAccessor:
|
39
52
|
Enabled: true
|
53
|
+
|
40
54
|
Layout/SpaceAroundMethodCallOperator:
|
41
55
|
Enabled: true
|
56
|
+
|
42
57
|
Lint/DeprecatedOpenSSLConstant:
|
43
58
|
Enabled: true
|
59
|
+
|
44
60
|
Lint/DuplicateElsifCondition:
|
45
61
|
Enabled: true
|
62
|
+
|
46
63
|
Lint/MixedRegexpCaptureTypes:
|
47
64
|
Enabled: true
|
65
|
+
|
48
66
|
Lint/RaiseException:
|
49
67
|
Enabled: true
|
68
|
+
|
50
69
|
Lint/StructNewOverride:
|
51
70
|
Enabled: true
|
71
|
+
|
52
72
|
Style/AccessorGrouping:
|
53
73
|
Enabled: true
|
74
|
+
|
54
75
|
Style/ArrayCoercion:
|
55
76
|
Enabled: true
|
77
|
+
|
56
78
|
Style/BisectedAttrAccessor:
|
57
79
|
Enabled: true
|
80
|
+
|
58
81
|
Style/CaseLikeIf:
|
59
82
|
Enabled: true
|
83
|
+
|
60
84
|
Style/ExponentialNotation:
|
61
85
|
Enabled: true
|
86
|
+
|
62
87
|
Style/HashAsLastArrayItem:
|
63
88
|
Enabled: true
|
89
|
+
|
64
90
|
Style/HashEachMethods:
|
65
91
|
Enabled: true
|
92
|
+
|
66
93
|
Style/HashLikeCase:
|
67
94
|
Enabled: true
|
95
|
+
|
68
96
|
Style/HashTransformKeys:
|
69
97
|
Enabled: true
|
98
|
+
|
70
99
|
Style/HashTransformValues:
|
71
100
|
Enabled: true
|
101
|
+
|
72
102
|
Style/RedundantAssignment:
|
73
103
|
Enabled: true
|
104
|
+
|
74
105
|
Style/RedundantFetchBlock:
|
75
106
|
Enabled: true
|
107
|
+
|
76
108
|
Style/RedundantFileExtensionInRequire:
|
77
109
|
Enabled: true
|
110
|
+
|
78
111
|
Style/RedundantRegexpCharacterClass:
|
79
112
|
Enabled: true
|
113
|
+
|
80
114
|
Style/RedundantRegexpEscape:
|
81
115
|
Enabled: true
|
116
|
+
|
82
117
|
Style/SlicingWithRange:
|
83
118
|
Enabled: true
|
119
|
+
|
84
120
|
RSpec/NestedGroups:
|
85
121
|
Max: 5
|
122
|
+
|
123
|
+
RSpec/ExampleLength:
|
124
|
+
Max: 10
|
125
|
+
|
126
|
+
Style/FrozenStringLiteralComment:
|
127
|
+
Enabled: false
|
128
|
+
|
129
|
+
Style/BlockDelimiters:
|
130
|
+
EnforcedStyle: braces_for_chaining
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,30 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
##
|
3
|
+
## [v0.20.0] 2021-05-17
|
4
4
|
|
5
|
+
- Added YARD documentation to almost every method
|
6
|
+
- Added `except` parameter to the `merge` method, which allows a specified field to be excluded from the merge.
|
7
|
+
|
8
|
+
## [v0.19.0] 2021-03-10
|
9
|
+
|
10
|
+
- Use [redoc](https://github.com/Redocly/redoc) for generated documentation UI
|
11
|
+
|
12
|
+
## [v0.18.0] 2021-03-02
|
13
|
+
|
14
|
+
- Add generic hash type for primitive types
|
15
|
+
|
16
|
+
## [v0.17.0]: 2020-11-30
|
17
|
+
|
18
|
+
- Allow tagging endpoints via the new `tags` method.
|
19
|
+
|
20
|
+
## [v0.16.0]: 2020-10-23
|
21
|
+
|
22
|
+
- Allow non-class types to be used as the inputs to controllers
|
23
|
+
|
24
|
+
## [v0.15.0]: 2020-09-02
|
25
|
+
|
26
|
+
### Added
|
27
|
+
- Add a new `#merge` method to output objects, which will merge fields from another output object into the given output object.
|
5
28
|
- Add `multi` to Output Objects, as a way to define more than one field of the same type at once.
|
6
29
|
- Add an `inherits:` key to output objects, for view inheritance.
|
7
30
|
- Add `SoberSwag::Types::CommaArray`, which parses comma-separated strings into arrays.
|
@@ -9,3 +32,8 @@
|
|
9
32
|
This class is mostly useful for query parameters where you want a simpler format: `tag=foo,bar` instead of `tag[]=foo,tag[]=bar`.
|
10
33
|
- Add support for using `meta` to specify alternative `style` and `explode` keys for query and path params.
|
11
34
|
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.
|
35
|
+
|
36
|
+
### Fixed
|
37
|
+
- No longer swallow `Dry::Struct` errors, instead let them surface to the user.
|
38
|
+
|
39
|
+
[v0.15.0]: https://github.com/SonderMindOrg/sober_swag/releases/tag/v0.15.0
|
data/Gemfile
CHANGED
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
|
+
- {file:docs/serializers.md Serializers}
|
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,44 @@ QueryInput = SoberSwag.input_object do
|
|
151
268
|
end
|
152
269
|
```
|
153
270
|
|
271
|
+
## Tags
|
272
|
+
|
273
|
+
If you want to organize your API into sections, you can use `tags`.
|
274
|
+
It's quite simple:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
define :patch, :update, '/people/{id}' do
|
278
|
+
# other cool config
|
279
|
+
tags 'people', 'mutations', 'incurs_cost'
|
280
|
+
end
|
281
|
+
```
|
282
|
+
|
283
|
+
This will map to OpenAPI's `tags` field (naturally), and the UI codegen will automatically organize your endpoints by their tags.
|
284
|
+
|
285
|
+
## Testing the validity of output objects
|
286
|
+
|
287
|
+
If you're using RSpec and want to test the validity of output objects, you can do so relatively easily.
|
288
|
+
|
289
|
+
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:
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
RSpec.describe UserOutputObject do
|
293
|
+
describe 'serialized result' do
|
294
|
+
subject do
|
295
|
+
described_class.type.new(described_class.serialize(create(:user)))
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'works with an object' do
|
299
|
+
expect { subject }.not_to raise_error
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
```
|
304
|
+
|
154
305
|
## Special Thanks
|
155
306
|
|
156
307
|
This gem is a mishmash of ideas from various sources.
|
157
308
|
The biggest thanks is owed to the [dry-rb](https://github.com/dry-rb) project, upon which the typing of SoberSwag is based.
|
158
309
|
On an API design level, much is owed to [blueprinter](https://github.com/procore/blueprinter) for the serializers.
|
159
310
|
The idea of a strongly-typed API came from the Haskell framework [servant](https://www.servant.dev/).
|
160
|
-
Generating the swagger
|
311
|
+
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).
|