sober_swag 0.3.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c404cf1bad19f06280ce4c8a2002899ed37e9e68e0fc910d270b4e14dc38dab6
4
- data.tar.gz: 29333042d89f5f3de762143ef60566bbbb45640a948c8b5720c4563b236a1d64
3
+ metadata.gz: ad48275d462cfbd18f8bec906c7dbbf4999a360dca0417e3697370bdf43482d6
4
+ data.tar.gz: a5e6a560fa783c21604917402b97638315d874e96be67679bce49a003f2b0654
5
5
  SHA512:
6
- metadata.gz: 1b84c508452f4dfd1250e0b6255d11173e2eb8382669adb75d3356f842a9ce7e6155410f5c233c6a8eae00697db0a561b3e466cfa9704996fb8de76c164f9c6b
7
- data.tar.gz: 45f28285e80a666f45f6b8ef3b598fb68c321da83022ee3d13b8532bf97cc9b2fe3eaed36ca3d670fe75b44a37263d349ce76dde44f4ed80bdb5583bb4e7f342
6
+ metadata.gz: 5a52d5e36c080525ae5dd3ec1d44b054916b9e926b7342b41c4448a3d6e4aa500e2b544ec71d83ce7dde1927fdfef27b77612e1206fed7db18b5300fd712358c
7
+ data.tar.gz: 67d15424d01ef63ed06125b2270232c5d9c95d5615eaa155ce40902c09e14224ad5745dbbd92f3e8fee9053d6b4ea1357156e2e868a5ede4c4d2e546c9bb1e68
@@ -2,4 +2,3 @@ check_name: 'Rubocop Lint'
2
2
  versions:
3
3
  rubocop: 'latest'
4
4
  rubocop-rspec: 'latest'
5
-
@@ -17,6 +17,9 @@ jobs:
17
17
  test:
18
18
 
19
19
  runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby: [ '2.6', '2.7' ]
20
23
 
21
24
  steps:
22
25
  - uses: actions/checkout@v2
@@ -26,13 +29,13 @@ jobs:
26
29
  # uses: ruby/setup-ruby@v1
27
30
  uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
28
31
  with:
29
- ruby-version: 2.7
32
+ ruby-version: ${{ matrix.ruby }}
30
33
  - uses: actions/cache@v2
31
34
  with:
32
35
  path: vendor/bundle
33
- key: ${{ runner.os }}-gem-deps-${{ hashFiles('**/Gemfile.lock') }}
36
+ key: ${{ runner.os }}-${{ matrix.ruby }}-gem-deps-${{ hashFiles('**/Gemfile.lock') }}
34
37
  restore-keys: |
35
- ${{ runner.os }}-gem-deps-
38
+ ${{ runner.os }}-${{ matrix.ruby }}-gem-deps-
36
39
  - name: Install dependencies
37
40
  run: |
38
41
  bundle config path vendor/bundle
@@ -48,9 +51,9 @@ jobs:
48
51
  - uses: actions/cache@v2
49
52
  with:
50
53
  path: example/vendor/bundle
51
- key: ${{ runner.os }}-example-deps-${{ hashFiles('example/**/Gemfile.lock') }}
54
+ key: ${{ runner.os }}-${{ matrix.ruby }}-example-deps-${{ hashFiles('example/**/Gemfile.lock') }}
52
55
  restore-keys: |
53
- ${{ runner.os }}-example-deps-
56
+ ${{ runner.os }}-${{ matrix.ruby }}-example-deps-
54
57
  - name: Install example dependencies for example
55
58
  working-directory: example
56
59
  run: |
@@ -58,4 +61,4 @@ jobs:
58
61
  bundle install
59
62
  - name: Run specs for example
60
63
  working-directory: example
61
- run: rake
64
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -10,5 +10,6 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  .ruby-version
13
+ Gemfile.lock
13
14
 
14
15
  *.gem
@@ -3,7 +3,7 @@ Style/FrozenStringLiteralComment:
3
3
  Style/BlockDelimiters:
4
4
  EnforcedStyle: braces_for_chaining
5
5
  AllCops:
6
- TargetRubyVersion: 2.7.1
6
+ TargetRubyVersion: 2.6.0
7
7
  Exclude:
8
8
  - 'bin/bundle'
9
9
  - 'example/bin/bundle'
@@ -31,6 +31,8 @@ Metrics/AbcSize:
31
31
  Style/Documentation:
32
32
  Exclude:
33
33
  - 'example/db/migrate/**/*'
34
+ Metrics/PerceivedComplexity:
35
+ Enabled: false
34
36
  Layout/EmptyLinesAroundAttributeAccessor:
35
37
  Enabled: true
36
38
  Layout/SpaceAroundMethodCallOperator:
data/README.md CHANGED
@@ -4,13 +4,11 @@
4
4
  ![Linters Status](https://github.com/SonderMindOrg/sober_swag/workflows/Linters/badge.svg?branch=master)
5
5
 
6
6
  SoberSwag is a combination of [Dry-Types](https://dry-rb.org/gems/dry-types/1.2/) and [Swagger](https://swagger.io/) that makes your Rails APIs more awesome.
7
- Other tools generate documenation from a DSL.
7
+ Other tools generate documentation from a DSL.
8
8
  This generates documentation from *types*, which (conveniently) also lets you get supercharged strong-params-on-steroids.
9
9
 
10
10
  An introductory presentation is available [here](https://www.icloud.com/keynote/0bxP3Dn8ETNO0lpsSQSVfEL6Q#SoberSwagPresentation).
11
11
 
12
- This gem uses pattern matching, and is thus only compatible with Ruby 2.7 or later.
13
-
14
12
  ## Types for a fully-automated API
15
13
 
16
14
  SoberSwag lets you type your API using describe blocks.
@@ -18,22 +16,22 @@ In any controller that includes `SoberSwag::Controller`, you get access to the s
18
16
  This lets you type your API endpoint:
19
17
 
20
18
  ```ruby
21
- class PeopleController < ApplicationController
22
- include SoberSwag::Controller
23
- define :patch, :update, '/people/{id}' do
24
- query_params do
25
- attribute? :include_extra_info, Types::Params::Bool
26
- end
27
- request_body do
28
- attribute? :name, Types::Params::String
29
- attribute? :age, Types::Params::Integer
30
- end
31
- path_params { attribute :id, Types::Params::Integer }
19
+ class PeopleController < ApplicationController
20
+ include SoberSwag::Controller
21
+ define :patch, :update, '/people/{id}' do
22
+ query_params do
23
+ attribute? :include_extra_info, Types::Params::Bool
24
+ end
25
+ request_body do
26
+ attribute? :name, Types::Params::String
27
+ attribute? :age, Types::Params::Integer
32
28
  end
29
+ path_params { attribute :id, Types::Params::Integer }
33
30
  end
31
+ end
34
32
  ```
35
33
 
36
- We can now us this information to generate swagger documentation, available at the `swagger` action on this controller.
34
+ We can now use this information to generate swagger documentation, available at the `swagger` action on this controller.
37
35
  More than that, we can use this information *inside* our controller methods:
38
36
 
39
37
  ```ruby
@@ -87,27 +85,75 @@ end
87
85
 
88
86
  Support for easily typing "render the activerecord errors for me please" is (unfortunately) under development.
89
87
 
90
- ### SoberSwag Structs
88
+ ### SoberSwag Input Objects
91
89
 
92
90
  Input parameters (including path, query, and request body) are typed using [dry-struct](https://dry-rb.org/gems/dry-struct/1.0/).
93
- You don't have to do them inline: you can define them in another file, like so:
91
+ You don't have to do them inline. You can define them in another file, like so:
94
92
 
95
93
  ```ruby
96
- User = SoberSwag.struct do
94
+ User = SoberSwag.input_object do
97
95
  attribute :name, SoberSwag::Types::String
98
96
  # use ? if attributes are not required
99
97
  attribute? :favorite_movie, SoberSwag::Types::String
100
98
  # use .optional if attributes may be null
101
- attribute :age, SoberSwag::Types::Params::::Integer.optional
99
+ attribute :age, SoberSwag::Types::Params::Integer.optional
100
+ end
101
+ ```
102
+
103
+ Then, in your controller, just do:
104
+
105
+ ```ruby
106
+ class PeopleController < ApplicationController
107
+ include SoberSwag::Controller
108
+
109
+ define :path, :update, '/people/{id}' do
110
+ request_body(User)
111
+ path_params { attribute :id, Types::Params::Integer }
112
+ response(:ok, 'the updated person', PersonOutputObject)
113
+ end
114
+ def update
115
+ # same as above!
116
+ end
102
117
  end
103
118
  ```
104
119
 
105
120
  Under the hood, this literally just generates a subclass of `Dry::Struct`.
106
121
  We use the DSL-like method just to make working with Rails' reloading less annoying.
107
122
 
123
+ #### Adding additional documentation
124
+
125
+ You can use the `.meta` attribute on a type to add additional documentation.
126
+ Some keys are considered "well-known" and will be present on the swagger output.
127
+ For example:
128
+
129
+
130
+ ```ruby
131
+ User = SoberSwag.input_object do
132
+ attribute? :name, SoberSwag::Types::String.meta(description: <<~MARKDOWN, deprecated: true)
133
+ The given name of the students, with strings encoded as escaped-ASCII.
134
+ This is used by an internal Cobol microservice from 1968.
135
+ Please use unicode_name instead unless you are that microservice.
136
+ MARKDOWN
137
+ attribute? :unicode_name, SoberSwag::Types::String
138
+ end
139
+ ```
140
+
141
+ This will output the swagger you expect, with a description and a deprecated flag.
142
+
143
+ #### Adding Default Values
144
+
145
+ Sometimes it makes sense to specify a default value.
146
+ Don't worry, we've got you covered:
147
+
148
+ ```ruby
149
+ QueryInput = SoberSwag.input_object do
150
+ attribute :allow_first, SoberSwag::Types::Params::Bool.default(false) # smartly alters type-definition to establish that passing this is not required.
151
+ end
152
+ ```
153
+
108
154
  ## Special Thanks
109
155
 
110
- This gem is a mismatch of ideas from various sources.
156
+ This gem is a mishmash of ideas from various sources.
111
157
  The biggest thanks is owed to the [dry-rb](https://github.com/dry-rb) project, upon which the typing of SoberSwag is based.
112
158
  On an API design level, much is owed to [blueprinter](https://github.com/procore/blueprinter) for the serializers.
113
159
  The idea of a strongly-typed API came from the Haskell framework [servant](https://www.servant.dev/).
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby -W:no-experimental
1
+ #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'bundler/setup'
@@ -34,6 +34,15 @@ class LinkedList < SoberSwag::InputObject
34
34
  attribute? :next, LinkedList
35
35
  end
36
36
 
37
+ Foo = SoberSwag::OutputObject.define do
38
+ field :name, primitive(:String)
39
+ field :age, primitive(:String)
40
+
41
+ view :foo do
42
+ field :bar, primitive(:String).optional
43
+ end
44
+ end
45
+
37
46
  # (If you use this, don't forget to add pry to your Gemfile!)
38
47
  require 'pry'
39
48
  Pry.start
@@ -4,11 +4,11 @@ Serializers are a way to transform from one type to another.
4
4
  For example, you might want to change an ActiveRecord object to a JSON struct.
5
5
  You might also want to change an internal date-interval into a two-element array of dates, or some custom text format.
6
6
  You can do all of these things with SoberSwag serializers.
7
- Furthermore, Serializers document the *type* that they serialize, so you can use it to degenerate documentation.
7
+ Furthermore, Serializers document the *type* that they serialize, so you can use it to generate documentation.
8
8
 
9
9
  ## The Basics
10
10
 
11
- All serializers are inherted from [`SoberSwag::Serializer::Base`](../lib/sober_swag/serializer/base.rb).
11
+ All serializers are inherited from [`SoberSwag::Serializer::Base`](../lib/sober_swag/serializer/base.rb).
12
12
  This is an abstract class that implements several methods, most of which will be documented later.
13
13
  The two that are most interesting, however, are `#type` and `#serialize`.
14
14
 
@@ -55,12 +55,12 @@ In the future, we might add some "debug mode" sorta thing that will do type-chec
55
55
 
56
56
  ### Mapped
57
57
 
58
- Sometimes, you can create a serilaizer via a *proc*.
58
+ Sometimes, you can create a serializer via a *proc*.
59
59
  For example, let's say that I want a serializer that takes a `Date` and returns a string.
60
60
  I can do this:
61
61
 
62
62
  ```ruby
63
- date_string = SoberSwag::Serializer.Primitive(:String).via_map { |d| d.to_s }
63
+ date_string = SoberSwag::Serializer.primitive(:String).via_map { |d| d.to_s }
64
64
  ```
65
65
 
66
66
  This is implemented via [`SoberSwag::Serializer::Mapped`](../lib/sober_swag/serializer/mapped.rb).
@@ -87,7 +87,7 @@ my_serializer.optional.serialize(nil) # => nil
87
87
  # ^ nils become nil
88
88
  ```
89
89
 
90
- This properly changes the `type` to be a nillable type, as well.
90
+ This properly changes the `type` to be a nilable type, as well.
91
91
 
92
92
  ### Array
93
93
 
@@ -121,9 +121,9 @@ Let's define an output object:
121
121
 
122
122
  ```ruby
123
123
  StudentOutputObject = SoberSwag::OutputObject.define do
124
- field :first_name, Primitive(:String)
125
- field :last_name, Primitive(:String)
126
- field :recent_grades, Primitive(:Integer).array do |student|
124
+ field :first_name, primitive(:String)
125
+ field :last_name, primitive(:String)
126
+ field :recent_grades, primitive(:Integer).array do |student|
127
127
  student.graded_assignments.limit(100).pluck(:grade)
128
128
  end
129
129
  end
@@ -143,10 +143,10 @@ Let's take a look at their use:
143
143
 
144
144
  ```ruby
145
145
  StudentOutputObject = SoberSwag::OutputObject.define do
146
- field :first_name, Primitive(:String)
147
- field :last_name, Primitive(:String)
146
+ field :first_name, primitive(:String)
147
+ field :last_name, primitive(:String)
148
148
  view :detail do
149
- field :recent_grades, Primitive(:Integer).array do |student|
149
+ field :recent_grades, primitive(:Integer).array do |student|
150
150
  student.graded_assignments.limit(100).pluck(:grade)
151
151
  end
152
152
  end
@@ -189,7 +189,7 @@ StudentOutputObject = SoberSwag::OutputObject.define do
189
189
  end
190
190
  ```
191
191
 
192
- This can cause a circular dependecy.
192
+ This can cause a circular dependency.
193
193
  To break this, you can use a lambda:
194
194
 
195
195
  ```ruby
@@ -200,4 +200,4 @@ StudentOutputObject = SoberSwag::OutputObject.define do
200
200
  end
201
201
  ```
202
202
 
203
- For clarity (and to prevent infinitely-looping serializers on accident, we reccomend you *always* use an explicit view for dependent output objects.
203
+ For clarity (and to prevent infinitely-looping serializers on accident, we recommend you *always* use an explicit view for dependent output objects.
@@ -1,8 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
- ruby '2.7.1'
5
-
6
4
  # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7
5
  gem 'rails', '~> 6.0.2', '>= 6.0.2.2'
8
6
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- sober_swag (0.2.0)
4
+ sober_swag (0.5.0)
5
5
  activesupport
6
6
  dry-struct (~> 1.0)
7
7
  dry-types (~> 1.2)
@@ -225,8 +225,5 @@ DEPENDENCIES
225
225
  sqlite3 (~> 1.4)
226
226
  tzinfo-data
227
227
 
228
- RUBY VERSION
229
- ruby 2.7.1p83
230
-
231
228
  BUNDLED WITH
232
229
  2.1.4
@@ -61,16 +61,18 @@ class PeopleController < ApplicationController
61
61
 
62
62
  define :get, :index, '/people/' do
63
63
  query_params do
64
- attribute? :first_name, Types::String
65
- attribute? :last_name, Types::String
66
- attribute? :view, Types::String.enum('base', 'detail')
64
+ attribute? :filters do
65
+ attribute? :first_name, Types::String
66
+ attribute? :last_name, Types::String
67
+ end
68
+ attribute :view, Types::String.default('base'.freeze).enum('base', 'detail')
67
69
  end
68
70
  response(:ok, 'all the people', PersonOutputObject.array)
69
71
  end
70
72
  def index
71
73
  @people = Person.all
72
- @people = @people.where('UPPER(first_name) LIKE UPPER(?)', "%#{parsed_query.first_name}%") if parsed_query.first_name
73
- @people = @people.where('UPPER(last_name) LIKE UPPER(?)', "%#{parsed_query.last_name}%") if parsed_query.last_name
74
+ @people = @people.where('UPPER(first_name) LIKE UPPER(?)', "%#{parsed_query.filters.first_name}%") if parsed_query.filters&.first_name
75
+ @people = @people.where('UPPER(last_name) LIKE UPPER(?)', "%#{parsed_query.filters.last_name}%") if parsed_query.filters&.last_name
74
76
  respond!(:ok, @people.includes(:posts), serializer_opts: { view: parsed_query.view })
75
77
  end
76
78
 
@@ -39,12 +39,20 @@ class PostsController < ApplicationController
39
39
  define :get, :index, '/posts/' do
40
40
  query_params do
41
41
  attribute? :view, ViewTypes
42
+ attribute :include_first, SoberSwag::Types::Params::Bool.default(false).meta(description: <<~MARKDOWN)
43
+ For historical reasons the first-ever post is the entire text of *Finnegan's Wake.*
44
+ Unfortunately, our contractors wound up depending on this quirk to complete dark arcane ceremonies,
45
+ so we can't remove it. Thus, by default, we don't include the first post unless you explicitly ask us to
46
+ (maybe you feel like some classic literature?).
47
+ MARKDOWN
42
48
  end
43
49
  response(:ok, 'all the posts', PostOutputObject.array)
44
50
  end
45
51
  def index
46
52
  @posts = Post.all
47
53
 
54
+ @posts = @posts.where('id > 1') unless parsed_query.include_first
55
+
48
56
  respond!(:ok, @posts.includes(:person), serializer_opts: { view: parsed_query.view })
49
57
  end
50
58
 
@@ -35,13 +35,13 @@ RSpec.describe 'Index action for people' do
35
35
  end
36
36
 
37
37
  context 'with a good first-name search' do
38
- let(:request) { get '/people', params: { first_name: 'A' } }
38
+ let(:request) { get '/people', params: { filters: { first_name: 'A' } } }
39
39
 
40
40
  it_behaves_like 'a request with the person'
41
41
  end
42
42
 
43
43
  context 'with a good last-name search' do
44
- let(:request) { get '/people', params: { last_name: 'G' } }
44
+ let(:request) { get '/people', params: { filters: { last_name: 'G' } } }
45
45
 
46
46
  it_behaves_like 'a request with the person'
47
47
  end
@@ -73,13 +73,16 @@ module SoberSwag
73
73
  end
74
74
 
75
75
  def path_schema
76
- path_schema_stub.map { |e| e.merge(in: :path) }
76
+ path_schema_stub.map do |e|
77
+ ensure_uncomplicated(e[:name], e[:schema])
78
+ e.merge(in: :path)
79
+ end
77
80
  rescue TooComplicatedError => e
78
81
  raise TooComplicatedForPathError, e.message
79
82
  end
80
83
 
81
84
  def query_schema
82
- path_schema_stub.map { |e| e.merge(in: :query) }
85
+ path_schema_stub.map { |e| e.merge(in: :query, style: :deepObject, explode: true) }
83
86
  rescue TooComplicatedError => e
84
87
  raise TooComplicatedForQueryError, e.message
85
88
  end
@@ -150,14 +153,17 @@ module SoberSwag
150
153
 
151
154
  def rewrite_sums(object) # rubocop:disable Metrics/MethodLength
152
155
  case object
153
- in Nodes::Sum[Nodes::OneOf[*lhs], Nodes::OneOf[*rhs]]
154
- Nodes::OneOf.new(lhs + rhs)
155
- in Nodes::Sum[Nodes::OneOf[*args], rhs]
156
- Nodes::OneOf.new(args + [rhs])
157
- in Nodes::Sum[lhs, Nodes::OneOf[*args]]
158
- Nodes::OneOf.new([lhs] + args)
159
- in Nodes::Sum[lhs, rhs]
160
- Nodes::OneOf.new([lhs, rhs])
156
+ when Nodes::Sum
157
+ lhs, rhs = object.deconstruct
158
+ if lhs.is_a?(Nodes::OneOf) && rhs.is_a?(Nodes::OneOf)
159
+ Nodes::OneOf.new(lhs.deconstruct + rhs.deconstruct)
160
+ elsif lhs.is_a?(Nodes::OneOf)
161
+ Nodes::OneOf.new([*lhs.deconstruct, rhs])
162
+ elsif rhs.is_a?(Nodes::OneOf)
163
+ Nodes::OneOf.new([lhs, *rhs.deconstruct])
164
+ else
165
+ Nodes::OneOf.new([lhs, rhs])
166
+ end
161
167
  else
162
168
  object
163
169
  end
@@ -165,59 +171,69 @@ module SoberSwag
165
171
 
166
172
  def flatten_one_ofs(object)
167
173
  case object
168
- in Nodes::OneOf[*args]
169
- Nodes::OneOf.new(args.uniq)
170
- else
174
+ when Nodes::OneOf
175
+ Nodes::OneOf.new(object.deconstruct.uniq)
176
+ else
171
177
  object
172
178
  end
173
179
  end
174
180
 
175
181
  def to_object_schema(object) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
176
182
  case object
177
- in Nodes::List[element]
183
+ when Nodes::List
178
184
  {
179
185
  type: :array,
180
- items: element
186
+ items: object.deconstruct.first
181
187
  }
182
- in Nodes::Enum[values]
188
+ when Nodes::Enum
183
189
  {
184
190
  type: :string,
185
- enum: values
191
+ enum: object.deconstruct.first
192
+ }
193
+ when Nodes::OneOf
194
+ if object.deconstruct.include?({ type: 'null' })
195
+ rejected = object.deconstruct.reject { |e| e[:type] == 'null' }
196
+ if rejected.length == 1
197
+ rejected.first.merge(nullable: true)
198
+ else
199
+ { oneOf: rejected, nullable: true }
200
+ end
201
+ else
202
+ { oneOf: object.deconstruct }
203
+ end
204
+ when Nodes::Object
205
+ # openAPI requires that you give a list of required attributes
206
+ # (which IMO is the *totally* wrong thing to do but whatever)
207
+ # so we must do this garbage
208
+ required = object.deconstruct.filter { |(_, b)| b[:required] }.map(&:first)
209
+ {
210
+ type: :object,
211
+ properties: object.deconstruct.map { |(a, b)|
212
+ [a, b.reject { |k, _| k == :required }]
213
+ }.to_h,
214
+ required: required
186
215
  }
187
- in Nodes::OneOf[{ type: 'null' }, b]
188
- b.merge(nullable: true)
189
- in Nodes::OneOf[a, { type: 'null' }]
190
- a.merge(nullable: true)
191
- in Nodes::OneOf[*attrs] if attrs.include?(type: 'null')
192
- { oneOf: attrs.reject { |e| e[:type] == 'null' }, nullable: true }
193
- in Nodes::OneOf[*cases]
194
- { oneOf: cases }
195
- in Nodes::Object[*attrs]
196
- # openAPI requires that you give a list of required attributes
197
- # (which IMO is the *totally* wrong thing to do but whatever)
198
- # so we must do this garbage
199
- required = attrs.filter { |(_, b)| b[:required] }.map(&:first)
200
- {
201
- type: :object,
202
- properties: attrs.map { |(a, b)|
203
- [a, b.reject { |k, _| k == :required }]
204
- }.to_h,
205
- required: required
206
- }
207
- in Nodes::Attribute[name, true, value]
208
- [name, value.merge(required: true)]
209
- in Nodes::Attribute[name, false, value]
210
- [name, value]
216
+ when Nodes::Attribute
217
+ name, req, value = object.deconstruct
218
+ if req
219
+ [name, value.merge(required: true)]
220
+ else
221
+ [name, value]
222
+ end
211
223
  # can't match on value directly as ruby uses `===` to match,
212
224
  # and classes use `===` to mean `is an instance of`, as
213
225
  # opposed to direct equality lmao
214
- in Nodes::Primitive[value:, metadata:] if self.class.primitive?(value)
215
- md = self.class.primitive_def(value)
216
- METADATA_KEYS.select(&metadata.method(:key?)).reduce(md) do |definition, key|
217
- definition.merge(key => metadata[key])
218
- end
219
- in Nodes::Primitive[value:]
220
- { '$ref': self.class.get_ref(value) }
226
+ when Nodes::Primitive
227
+ value = object.value
228
+ metadata = object.metadata
229
+ if self.class.primitive?(value)
230
+ md = self.class.primitive_def(value)
231
+ METADATA_KEYS.select(&metadata.method(:key?)).reduce(md) do |definition, key|
232
+ definition.merge(key => metadata[key])
233
+ end
234
+ else
235
+ { '$ref': self.class.get_ref(value) }
236
+ end
221
237
  else
222
238
  raise ArgumentError, "Got confusing node #{object} (#{object.class})"
223
239
  end
@@ -226,13 +242,10 @@ module SoberSwag
226
242
  def path_schema_stub
227
243
  @path_schema_stub ||=
228
244
  object_schema[:properties].map do |k, v|
229
- ensure_uncomplicated(k, v)
245
+ # ensure_uncomplicated(k, v)
230
246
  {
231
247
  name: k,
232
248
  schema: v.reject { |key, _| %i[required nullable].include?(key) },
233
- # rubocop:disable Style/DoubleNegation
234
- allowEmptyValue: !object_schema[:required].include?(k) || !!v[:nullable], # if it's required, no empties, but if *nullabe*, empties are okay
235
- # rubocop:enable Style/DoubleNegation
236
249
  required: object_schema[:required].include?(k) || false
237
250
  }
238
251
  end
@@ -44,6 +44,10 @@ module SoberSwag
44
44
  def map
45
45
  raise ArgumentError, 'Base is abstract'
46
46
  end
47
+
48
+ def flatten_one_ofs
49
+ raise ArgumentError, 'Base is abstract'
50
+ end
47
51
  end
48
52
  end
49
53
  end
@@ -10,7 +10,7 @@ module SoberSwag
10
10
  ##
11
11
  # Given a symbol to this, we will use a primitive name
12
12
  def primitive(name)
13
- SoberSwag::Serializer.Primitive(SoberSwag::Types.const_get(name))
13
+ SoberSwag::Serializer.primitive(SoberSwag::Types.const_get(name))
14
14
  end
15
15
  end
16
16
  end
@@ -10,6 +10,9 @@ module SoberSwag
10
10
 
11
11
  def to_syntax # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
12
12
  case @node
13
+ when Dry::Types::Default
14
+ # we handle this elsewhere, so
15
+ bind(Parser.new(@node.type))
13
16
  when Dry::Types::Array::Member
14
17
  Nodes::List.new(bind(Parser.new(@node.member)))
15
18
  when Dry::Types::Enum
@@ -21,7 +24,7 @@ module SoberSwag
21
24
  when Dry::Types::Schema::Key
22
25
  Nodes::Attribute.new(
23
26
  @node.name,
24
- @node.required?,
27
+ @node.required? && !@node.type.default?,
25
28
  bind(Parser.new(@node.type))
26
29
  )
27
30
  when Dry::Types::Sum
@@ -18,7 +18,7 @@ module SoberSwag
18
18
  # in values raw.
19
19
  #
20
20
  # @param contained {Class} Dry::Type to use
21
- def Primitive(contained) # rubocop:disable Naming/MethodName
21
+ def primitive(contained)
22
22
  SoberSwag::Serializer::Primitive.new(contained)
23
23
  end
24
24
  end
@@ -29,13 +29,6 @@ module SoberSwag
29
29
  def type
30
30
  @base.type
31
31
  end
32
-
33
- ##
34
- # I have no freaking clue if ruby optimizes proc composition,
35
- # but we at least save some node traversals here
36
- def via_map(&block)
37
- SoberSwag::Serializer::Mapped.new(@base, @map_f >> block)
38
- end
39
32
  end
40
33
  end
41
34
  end
@@ -9,8 +9,12 @@ module SoberSwag
9
9
  Rails.application.routes.routes.map { |route|
10
10
  route.defaults[:controller]
11
11
  }.to_set.reject(&:nil?).map { |controller|
12
- "#{controller}_controller".classify.constantize
13
- }.filter { |controller| controller.ancestors.include?(SoberSwag::Controller) }
12
+ begin
13
+ "#{controller}_controller".classify.constantize
14
+ rescue StandardError
15
+ nil
16
+ end
17
+ }.compact.filter { |controller| controller.ancestors.include?(SoberSwag::Controller) }
14
18
  end
15
19
 
16
20
  ##
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SoberSwag
4
- VERSION = '0.3.0'
4
+ VERSION = '0.8.0'
5
5
  end
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.metadata['homepage_uri'] = spec.homepage
21
21
 
22
+ spec.required_ruby_version = '>= 2.6.0'
23
+
22
24
  # Specify which files should be added to the gem when it is released.
23
25
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
26
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
@@ -34,6 +36,7 @@ Gem::Specification.new do |spec|
34
36
 
35
37
  spec.add_development_dependency 'bundler', '~> 2.0'
36
38
  spec.add_development_dependency 'pry'
39
+ spec.add_development_dependency 'pry-byebug'
37
40
  spec.add_development_dependency 'rake', '~> 13.0'
38
41
  spec.add_development_dependency 'rspec', '~> 3.0'
39
42
  spec.add_development_dependency 'rubocop'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sober_swag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anthony Super
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-17 00:00:00.000000000 Z
11
+ date: 2020-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rake
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -166,7 +180,6 @@ files:
166
180
  - ".ruby-version"
167
181
  - ".travis.yml"
168
182
  - Gemfile
169
- - Gemfile.lock
170
183
  - LICENSE.txt
171
184
  - README.md
172
185
  - Rakefile
@@ -288,14 +301,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
288
301
  requirements:
289
302
  - - ">="
290
303
  - !ruby/object:Gem::Version
291
- version: '0'
304
+ version: 2.6.0
292
305
  required_rubygems_version: !ruby/object:Gem::Requirement
293
306
  requirements:
294
307
  - - ">="
295
308
  - !ruby/object:Gem::Version
296
309
  version: '0'
297
310
  requirements: []
298
- rubygems_version: 3.1.2
311
+ rubygems_version: 3.0.3
299
312
  signing_key:
300
313
  specification_version: 4
301
314
  summary: Generate swagger types from dry-types
@@ -1,116 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- sober_swag (0.2.0)
5
- activesupport
6
- dry-struct (~> 1.0)
7
- dry-types (~> 1.2)
8
-
9
- GEM
10
- remote: https://rubygems.org/
11
- specs:
12
- activesupport (6.0.3.2)
13
- concurrent-ruby (~> 1.0, >= 1.0.2)
14
- i18n (>= 0.7, < 2)
15
- minitest (~> 5.1)
16
- tzinfo (~> 1.1)
17
- zeitwerk (~> 2.2, >= 2.2.2)
18
- ast (2.4.1)
19
- coderay (1.1.2)
20
- concurrent-ruby (1.1.6)
21
- diff-lcs (1.4.4)
22
- docile (1.3.2)
23
- dry-configurable (0.11.6)
24
- concurrent-ruby (~> 1.0)
25
- dry-core (~> 0.4, >= 0.4.7)
26
- dry-equalizer (~> 0.2)
27
- dry-container (0.7.2)
28
- concurrent-ruby (~> 1.0)
29
- dry-configurable (~> 0.1, >= 0.1.3)
30
- dry-core (0.4.9)
31
- concurrent-ruby (~> 1.0)
32
- dry-equalizer (0.3.0)
33
- dry-inflector (0.2.0)
34
- dry-logic (1.0.6)
35
- concurrent-ruby (~> 1.0)
36
- dry-core (~> 0.2)
37
- dry-equalizer (~> 0.2)
38
- dry-struct (1.3.0)
39
- dry-core (~> 0.4, >= 0.4.4)
40
- dry-equalizer (~> 0.3)
41
- dry-types (~> 1.3)
42
- ice_nine (~> 0.11)
43
- dry-types (1.4.0)
44
- concurrent-ruby (~> 1.0)
45
- dry-container (~> 0.3)
46
- dry-core (~> 0.4, >= 0.4.4)
47
- dry-equalizer (~> 0.3)
48
- dry-inflector (~> 0.1, >= 0.1.2)
49
- dry-logic (~> 1.0, >= 1.0.2)
50
- i18n (1.8.3)
51
- concurrent-ruby (~> 1.0)
52
- ice_nine (0.11.2)
53
- method_source (0.9.2)
54
- minitest (5.14.1)
55
- parallel (1.19.2)
56
- parser (2.7.1.4)
57
- ast (~> 2.4.1)
58
- pry (0.12.2)
59
- coderay (~> 1.1.0)
60
- method_source (~> 0.9.0)
61
- rainbow (3.0.0)
62
- rake (13.0.1)
63
- regexp_parser (1.7.1)
64
- rexml (3.2.4)
65
- rspec (3.9.0)
66
- rspec-core (~> 3.9.0)
67
- rspec-expectations (~> 3.9.0)
68
- rspec-mocks (~> 3.9.0)
69
- rspec-core (3.9.2)
70
- rspec-support (~> 3.9.3)
71
- rspec-expectations (3.9.2)
72
- diff-lcs (>= 1.2.0, < 2.0)
73
- rspec-support (~> 3.9.0)
74
- rspec-mocks (3.9.1)
75
- diff-lcs (>= 1.2.0, < 2.0)
76
- rspec-support (~> 3.9.0)
77
- rspec-support (3.9.3)
78
- rubocop (0.88.0)
79
- parallel (~> 1.10)
80
- parser (>= 2.7.1.1)
81
- rainbow (>= 2.2.2, < 4.0)
82
- regexp_parser (>= 1.7)
83
- rexml
84
- rubocop-ast (>= 0.1.0, < 1.0)
85
- ruby-progressbar (~> 1.7)
86
- unicode-display_width (>= 1.4.0, < 2.0)
87
- rubocop-ast (0.1.0)
88
- parser (>= 2.7.0.1)
89
- rubocop-rspec (1.42.0)
90
- rubocop (>= 0.87.0)
91
- ruby-progressbar (1.10.1)
92
- simplecov (0.18.5)
93
- docile (~> 1.1)
94
- simplecov-html (~> 0.11)
95
- simplecov-html (0.12.2)
96
- thread_safe (0.3.6)
97
- tzinfo (1.2.7)
98
- thread_safe (~> 0.1)
99
- unicode-display_width (1.7.0)
100
- zeitwerk (2.4.0)
101
-
102
- PLATFORMS
103
- ruby
104
-
105
- DEPENDENCIES
106
- bundler (~> 2.0)
107
- pry
108
- rake (~> 13.0)
109
- rspec (~> 3.0)
110
- rubocop
111
- rubocop-rspec
112
- simplecov
113
- sober_swag!
114
-
115
- BUNDLED WITH
116
- 2.1.4