sober_swag 0.17.0 → 0.21.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/benchmark.yml +39 -0
- data/.github/workflows/lint.yml +2 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +5 -1
- data/.yardopts +7 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +12 -0
- data/README.md +1 -1
- data/bench/benchmark.rb +34 -0
- data/bench/benchmarks/basic_field_serializer.rb +21 -0
- data/bench/benchmarks/view_selection.rb +47 -0
- data/docs/serializers.md +4 -1
- data/example/Gemfile +2 -2
- data/example/Gemfile.lock +46 -44
- data/example/config/environments/production.rb +1 -1
- data/lib/sober_swag/compiler/path.rb +42 -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/compiler.rb +29 -3
- data/lib/sober_swag/controller/route.rb +103 -20
- data/lib/sober_swag/controller.rb +39 -12
- data/lib/sober_swag/input_object.rb +124 -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/definition.rb +57 -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/output_object.rb +40 -19
- 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 +23 -5
- data/lib/sober_swag/serializer/hash.rb +53 -0
- 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/serializer.rb +1 -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/lib/sober_swag.rb +6 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9dce6daadaff9f7ddb1530ecf5fa69693454a29f04da849eec9bea8969166bc
|
4
|
+
data.tar.gz: a6cd16e93640b2d9c27fab081c3551d8498643044a6ebf9cab8100b51d8d916d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb2dcb7bee3b89e643b3e5d2d1e3d5b7034161ae563de59909120a3f4c817a8f14ab88d5d1968efb2b914b068cf0370d41f9094bbec7ae8ef96a87dfe3ad64fd
|
7
|
+
data.tar.gz: bfc9bfe3b4e93dca2a774e416496d430dfa63185acc82db780cc4f26d7eec1ff2a6db0f10103fd962243ab46b9799b8e6536dd94cc07758b5f52ea07af71df67
|
@@ -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"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: Ruby Benchmark
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
benchmark:
|
11
|
+
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby: [ '2.6', '2.7', '3.0' ]
|
16
|
+
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby }}
|
23
|
+
- uses: actions/cache@v2
|
24
|
+
with:
|
25
|
+
path: vendor/bundle
|
26
|
+
key: ${{ runner.os }}-${{ matrix.ruby }}-gem-deps-${{ hashFiles('**/Gemfile.lock') }}
|
27
|
+
restore-keys: |
|
28
|
+
${{ runner.os }}-${{ matrix.ruby }}-gem-deps-
|
29
|
+
- name: Install dependencies
|
30
|
+
run: |
|
31
|
+
bundle config path vendor/bundle
|
32
|
+
bundle install
|
33
|
+
- name: Run Benchmark
|
34
|
+
run: bundle exec ruby bench/benchmark.rb
|
35
|
+
- uses: actions/upload-artifact@v2
|
36
|
+
with:
|
37
|
+
name: benchmark-result
|
38
|
+
path: benchmark_results.yaml
|
39
|
+
if-no-files-found: error
|
data/.github/workflows/lint.yml
CHANGED
data/.github/workflows/ruby.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -2,6 +2,7 @@ require: rubocop-rspec
|
|
2
2
|
|
3
3
|
AllCops:
|
4
4
|
TargetRubyVersion: 2.6.0
|
5
|
+
NewCops: enable
|
5
6
|
Exclude:
|
6
7
|
- 'bin/bundle'
|
7
8
|
- 'example/bin/bundle'
|
@@ -22,6 +23,9 @@ Metrics/BlockLength:
|
|
22
23
|
- 'sober_swag.gemspec'
|
23
24
|
- 'example/spec/**/*.rb'
|
24
25
|
|
26
|
+
Lint/MissingSuper:
|
27
|
+
Enabled: false
|
28
|
+
|
25
29
|
RSpec/ImplicitBlockExpectation:
|
26
30
|
Enabled: false
|
27
31
|
|
@@ -123,4 +127,4 @@ Style/FrozenStringLiteralComment:
|
|
123
127
|
Enabled: false
|
124
128
|
|
125
129
|
Style/BlockDelimiters:
|
126
|
-
EnforcedStyle: braces_for_chaining
|
130
|
+
EnforcedStyle: braces_for_chaining
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.21.0] 2021-09-02
|
4
|
+
|
5
|
+
- Added a new method of serializing views based on hash lookups, improving performance
|
6
|
+
- Added a benchmarking suite
|
7
|
+
- Added `except` parameter to the `merge` method, which allows a specified field to be excluded from the merge.
|
8
|
+
- Add `type_key` to output objects, for easily serializing out type fields with a constant string.
|
9
|
+
- Added `type_attribute` to `SoberSwag::InputObject` to add easy constant-value disambiguation.
|
10
|
+
|
11
|
+
## [v0.20.0] 2021-05-17
|
12
|
+
|
13
|
+
- Added YARD documentation to almost every method
|
14
|
+
|
15
|
+
|
16
|
+
## [v0.19.0] 2021-03-10
|
17
|
+
|
18
|
+
- Use [redoc](https://github.com/Redocly/redoc) for generated documentation UI
|
19
|
+
|
20
|
+
## [v0.18.0] 2021-03-02
|
21
|
+
|
22
|
+
- Add generic hash type for primitive types
|
23
|
+
|
3
24
|
## [v0.17.0]: 2020-11-30
|
4
25
|
|
5
26
|
- Allow tagging endpoints via the new `tags` method.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@ An introductory presentation is available [here](https://www.icloud.com/keynote/
|
|
11
11
|
|
12
12
|
Further documentation on using the gem is available in the `docs/` directory:
|
13
13
|
|
14
|
-
-
|
14
|
+
- {file:docs/serializers.md Serializers}
|
15
15
|
|
16
16
|
## Types for a fully-automated API
|
17
17
|
|
data/bench/benchmark.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'sober_swag'
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'benchmark/ips'
|
7
|
+
|
8
|
+
##
|
9
|
+
# Quick and dirty way to benchmark things.
|
10
|
+
class Bench
|
11
|
+
class << self
|
12
|
+
def report(name, &block)
|
13
|
+
puts name
|
14
|
+
|
15
|
+
data[name] ||= Benchmark.ips(&block).data
|
16
|
+
end
|
17
|
+
|
18
|
+
def data
|
19
|
+
@data ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def write!(filename)
|
23
|
+
File.open(filename, 'w') do |f|
|
24
|
+
f << YAML.dump(data)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Dir['bench/benchmarks/**/*.rb'].sort.each do |file|
|
31
|
+
require_relative file.gsub(%r{^bench/}, '')
|
32
|
+
end
|
33
|
+
|
34
|
+
Bench.write!('benchmark_results.yaml')
|
@@ -0,0 +1,21 @@
|
|
1
|
+
##
|
2
|
+
# Bench test for serializing multiple fields.
|
3
|
+
class BasicFieldSerializer
|
4
|
+
Idea = Struct.new(:name, :grade, :cool)
|
5
|
+
|
6
|
+
Output = SoberSwag::OutputObject.define do
|
7
|
+
field :name, primitive(:String)
|
8
|
+
field :grade, primitive(:Integer)
|
9
|
+
field :cool, primitive(:Bool)
|
10
|
+
end
|
11
|
+
|
12
|
+
OutputSerializer = Output.serializer
|
13
|
+
|
14
|
+
MyIdea = Idea.new('Bob', 12, false)
|
15
|
+
|
16
|
+
Bench.report 'Basic Field Serializers' do |bm|
|
17
|
+
bm.report('Output Object') { Output.serialize(MyIdea) }
|
18
|
+
bm.report('Serializer of Output Object') { OutputSerializer.serialize(MyIdea) }
|
19
|
+
bm.compare!
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
##
|
2
|
+
# Benchmark for speed of selecting what view to use.
|
3
|
+
class ViewSelection
|
4
|
+
Accomplishment = Struct.new(:name, :description)
|
5
|
+
Person = Struct.new(:first_name, :last_name, :accomplishments)
|
6
|
+
|
7
|
+
MyPerson = Person.new(
|
8
|
+
'Joeseph',
|
9
|
+
'Biden',
|
10
|
+
[
|
11
|
+
Accomplishment.new('Became President', 'Won a Presidential Election'),
|
12
|
+
Accomplishment.new('Oldest President', 'Oldest man to be elected president at time of election'),
|
13
|
+
Accomplishment.new('Became Senator', 'Got Elected to the Senate'),
|
14
|
+
Accomplishment.new('Youngest Senator', 'Youngest person elected Senator at time of election')
|
15
|
+
]
|
16
|
+
)
|
17
|
+
|
18
|
+
AccomplishmentSerializer = SoberSwag::OutputObject.define do
|
19
|
+
field :name, primitive(:String)
|
20
|
+
|
21
|
+
view :detail do
|
22
|
+
field :description, primitive(:String)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
PersonSerializer = SoberSwag::OutputObject.define do
|
27
|
+
field :first_name, primitive(:String)
|
28
|
+
field :last_name, primitive(:String)
|
29
|
+
|
30
|
+
# make a bunch of dummy views
|
31
|
+
1.upto(10).each { |n| view(:"view_#{n}") {} }
|
32
|
+
|
33
|
+
view :detail do
|
34
|
+
field :accomplishments, AccomplishmentSerializer.view(:detail)
|
35
|
+
end
|
36
|
+
|
37
|
+
1.upto(10).each { |n| view(:"view_after_#{n}") {} }
|
38
|
+
end
|
39
|
+
|
40
|
+
Bench.report 'View Selection' do |bm|
|
41
|
+
bm.report('With no view') { PersonSerializer.serialize(MyPerson) }
|
42
|
+
|
43
|
+
bm.report('With a view') { PersonSerializer.serialize(MyPerson, { view: :detail }) }
|
44
|
+
|
45
|
+
bm.compare!
|
46
|
+
end
|
47
|
+
end
|
data/docs/serializers.md
CHANGED
@@ -105,7 +105,7 @@ This changes the type properly too.
|
|
105
105
|
|
106
106
|
98% of the time, when we're writing web APIs, we want to transform our domain objects into JSON objects.
|
107
107
|
We often want different ways to do this, too.
|
108
|
-
Consider, for
|
108
|
+
Consider, for example, an API for a college.
|
109
109
|
We might want to provide one detailed way to serialize a student, which includes their full name, grade, student ID, GPA, and so on.
|
110
110
|
On another page, we might want to display a classroom with a list of students.
|
111
111
|
However, on the classroom page, we don't want to serialize a full student: that's sending too much data.
|
@@ -241,6 +241,9 @@ end
|
|
241
241
|
Using `#merge` lets you add in all the fields from one output object into another.
|
242
242
|
You can even use `merge` from within a view.
|
243
243
|
|
244
|
+
Exclude any unneeded fields from the merge by passing a hash:
|
245
|
+
`merge GenericBioOutput, { except: [:position] }`
|
246
|
+
|
244
247
|
Note that `merge` does *not* copy anything but fields.
|
245
248
|
Identifiers and views will not be copied over.
|
246
249
|
|
data/example/Gemfile
CHANGED
@@ -8,7 +8,7 @@ gem 'actionpack', '>= 6.0.3.2'
|
|
8
8
|
# Use sqlite3 as the database for Active Record
|
9
9
|
gem 'sqlite3', '~> 1.4'
|
10
10
|
# Use Puma as the app server
|
11
|
-
gem 'puma', '~>
|
11
|
+
gem 'puma', '~> 5.3'
|
12
12
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
13
13
|
# gem 'jbuilder', '~> 2.7'
|
14
14
|
# Use Active Model has_secure_password
|
@@ -34,7 +34,7 @@ group :development, :test do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
group :development do
|
37
|
-
gem 'listen', '>= 3.0.5', '< 3.
|
37
|
+
gem 'listen', '>= 3.0.5', '< 3.7'
|
38
38
|
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
39
39
|
gem 'spring'
|
40
40
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
data/example/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
sober_swag (0.
|
4
|
+
sober_swag (0.19.0)
|
5
5
|
activesupport
|
6
6
|
dry-struct (~> 1.0)
|
7
7
|
dry-types (~> 1.2)
|
@@ -64,12 +64,12 @@ GEM
|
|
64
64
|
minitest (~> 5.1)
|
65
65
|
tzinfo (~> 1.1)
|
66
66
|
zeitwerk (~> 2.2, >= 2.2.2)
|
67
|
-
bootsnap (1.
|
67
|
+
bootsnap (1.7.5)
|
68
68
|
msgpack (~> 1.0)
|
69
69
|
builder (3.2.4)
|
70
70
|
byebug (11.1.3)
|
71
71
|
coderay (1.1.3)
|
72
|
-
concurrent-ruby (1.1.
|
72
|
+
concurrent-ruby (1.1.8)
|
73
73
|
crass (1.0.6)
|
74
74
|
diff-lcs (1.4.4)
|
75
75
|
dry-configurable (0.11.6)
|
@@ -102,18 +102,17 @@ GEM
|
|
102
102
|
dry-types-rails (0.3.4)
|
103
103
|
dry-types (>= 0.8.1)
|
104
104
|
rails (>= 3)
|
105
|
-
erubi (1.
|
106
|
-
ffi (1.
|
105
|
+
erubi (1.10.0)
|
106
|
+
ffi (1.15.3)
|
107
107
|
globalid (0.4.2)
|
108
108
|
activesupport (>= 4.2.0)
|
109
|
-
i18n (1.8.
|
109
|
+
i18n (1.8.9)
|
110
110
|
concurrent-ruby (~> 1.0)
|
111
111
|
ice_nine (0.11.2)
|
112
|
-
listen (3.
|
113
|
-
rb-fsevent (~> 0.
|
114
|
-
rb-inotify (~> 0.9, >= 0.9.
|
115
|
-
|
116
|
-
loofah (2.6.0)
|
112
|
+
listen (3.6.0)
|
113
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
114
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
115
|
+
loofah (2.9.0)
|
117
116
|
crass (~> 1.0.2)
|
118
117
|
nokogiri (>= 1.5.9)
|
119
118
|
mail (2.7.1)
|
@@ -121,19 +120,23 @@ GEM
|
|
121
120
|
marcel (0.3.3)
|
122
121
|
mimemagic (~> 0.3.2)
|
123
122
|
method_source (1.0.0)
|
124
|
-
mimemagic (0.3.
|
123
|
+
mimemagic (0.3.10)
|
124
|
+
nokogiri (~> 1)
|
125
|
+
rake
|
125
126
|
mini_mime (1.0.2)
|
126
|
-
mini_portile2 (2.
|
127
|
-
minitest (5.14.
|
128
|
-
msgpack (1.
|
129
|
-
nio4r (2.5.
|
130
|
-
nokogiri (1.
|
131
|
-
mini_portile2 (~> 2.
|
132
|
-
|
127
|
+
mini_portile2 (2.5.1)
|
128
|
+
minitest (5.14.4)
|
129
|
+
msgpack (1.4.2)
|
130
|
+
nio4r (2.5.7)
|
131
|
+
nokogiri (1.11.5)
|
132
|
+
mini_portile2 (~> 2.5.0)
|
133
|
+
racc (~> 1.4)
|
134
|
+
pry (0.14.1)
|
133
135
|
coderay (~> 1.1)
|
134
136
|
method_source (~> 1.0)
|
135
|
-
puma (
|
137
|
+
puma (5.3.2)
|
136
138
|
nio4r (~> 2.0)
|
139
|
+
racc (1.5.2)
|
137
140
|
rack (2.2.3)
|
138
141
|
rack-test (1.1.0)
|
139
142
|
rack (>= 1.0, < 3)
|
@@ -163,29 +166,28 @@ GEM
|
|
163
166
|
method_source
|
164
167
|
rake (>= 0.8.7)
|
165
168
|
thor (>= 0.20.3, < 2.0)
|
166
|
-
rake (13.0.
|
167
|
-
rb-fsevent (0.
|
169
|
+
rake (13.0.3)
|
170
|
+
rb-fsevent (0.11.0)
|
168
171
|
rb-inotify (0.10.1)
|
169
172
|
ffi (~> 1.0)
|
170
|
-
rspec-core (3.
|
171
|
-
rspec-support (~> 3.
|
172
|
-
rspec-expectations (3.
|
173
|
+
rspec-core (3.10.1)
|
174
|
+
rspec-support (~> 3.10.0)
|
175
|
+
rspec-expectations (3.10.1)
|
173
176
|
diff-lcs (>= 1.2.0, < 2.0)
|
174
|
-
rspec-support (~> 3.
|
175
|
-
rspec-mocks (3.
|
177
|
+
rspec-support (~> 3.10.0)
|
178
|
+
rspec-mocks (3.10.2)
|
176
179
|
diff-lcs (>= 1.2.0, < 2.0)
|
177
|
-
rspec-support (~> 3.
|
178
|
-
rspec-rails (
|
179
|
-
actionpack (>=
|
180
|
-
activesupport (>=
|
181
|
-
railties (>=
|
182
|
-
rspec-core (~> 3.
|
183
|
-
rspec-expectations (~> 3.
|
184
|
-
rspec-mocks (~> 3.
|
185
|
-
rspec-support (~> 3.
|
186
|
-
rspec-support (3.
|
187
|
-
|
188
|
-
spring (2.1.0)
|
180
|
+
rspec-support (~> 3.10.0)
|
181
|
+
rspec-rails (5.0.1)
|
182
|
+
actionpack (>= 5.2)
|
183
|
+
activesupport (>= 5.2)
|
184
|
+
railties (>= 5.2)
|
185
|
+
rspec-core (~> 3.10)
|
186
|
+
rspec-expectations (~> 3.10)
|
187
|
+
rspec-mocks (~> 3.10)
|
188
|
+
rspec-support (~> 3.10)
|
189
|
+
rspec-support (3.10.2)
|
190
|
+
spring (2.1.1)
|
189
191
|
spring-watcher-listen (2.0.1)
|
190
192
|
listen (>= 2.7, < 4.0)
|
191
193
|
spring (>= 1.2, < 3.0)
|
@@ -197,14 +199,14 @@ GEM
|
|
197
199
|
activesupport (>= 4.0)
|
198
200
|
sprockets (>= 3.0.0)
|
199
201
|
sqlite3 (1.4.2)
|
200
|
-
thor (1.0
|
202
|
+
thor (1.1.0)
|
201
203
|
thread_safe (0.3.6)
|
202
|
-
tzinfo (1.2.
|
204
|
+
tzinfo (1.2.9)
|
203
205
|
thread_safe (~> 0.1)
|
204
206
|
websocket-driver (0.7.3)
|
205
207
|
websocket-extensions (>= 0.1.0)
|
206
208
|
websocket-extensions (0.1.5)
|
207
|
-
zeitwerk (2.4.
|
209
|
+
zeitwerk (2.4.2)
|
208
210
|
|
209
211
|
PLATFORMS
|
210
212
|
ruby
|
@@ -214,9 +216,9 @@ DEPENDENCIES
|
|
214
216
|
bootsnap (>= 1.4.2)
|
215
217
|
byebug
|
216
218
|
dry-types-rails
|
217
|
-
listen (>= 3.0.5, < 3.
|
219
|
+
listen (>= 3.0.5, < 3.7)
|
218
220
|
pry
|
219
|
-
puma (~>
|
221
|
+
puma (~> 5.3)
|
220
222
|
rails (~> 6.0.2, >= 6.0.2.2)
|
221
223
|
rspec-rails
|
222
224
|
sober_swag!
|