sober_swag 0.3.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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