sober_swag 0.17.0 → 0.21.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/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!
|