sober_swag 0.1.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/config/rubocop_linter_action.yml +4 -0
  3. data/.github/workflows/lint.yml +15 -0
  4. data/.github/workflows/ruby.yml +33 -2
  5. data/.gitignore +4 -0
  6. data/.rubocop.yml +75 -1
  7. data/.ruby-version +1 -1
  8. data/README.md +154 -1
  9. data/bin/console +16 -15
  10. data/docs/serializers.md +203 -0
  11. data/example/.rspec +1 -0
  12. data/example/.ruby-version +1 -1
  13. data/example/Gemfile +9 -7
  14. data/example/Gemfile.lock +96 -79
  15. data/example/app/controllers/people_controller.rb +41 -23
  16. data/example/app/controllers/posts_controller.rb +110 -0
  17. data/example/app/models/application_record.rb +3 -0
  18. data/example/app/models/person.rb +6 -0
  19. data/example/app/models/post.rb +9 -0
  20. data/example/app/output_objects/person_errors_output_object.rb +5 -0
  21. data/example/app/output_objects/person_output_object.rb +15 -0
  22. data/example/app/output_objects/post_output_object.rb +10 -0
  23. data/example/bin/bundle +24 -20
  24. data/example/bin/rails +1 -1
  25. data/example/bin/rake +1 -1
  26. data/example/config/application.rb +11 -7
  27. data/example/config/environments/development.rb +0 -1
  28. data/example/config/environments/production.rb +3 -3
  29. data/example/config/puma.rb +5 -5
  30. data/example/config/routes.rb +3 -0
  31. data/example/config/spring.rb +4 -4
  32. data/example/db/migrate/20200311152021_create_people.rb +0 -1
  33. data/example/db/migrate/20200603172347_create_posts.rb +11 -0
  34. data/example/db/schema.rb +16 -7
  35. data/example/spec/rails_helper.rb +64 -0
  36. data/example/spec/requests/people/create_spec.rb +52 -0
  37. data/example/spec/requests/people/get_spec.rb +35 -0
  38. data/example/spec/requests/people/index_spec.rb +69 -0
  39. data/example/spec/spec_helper.rb +94 -0
  40. data/lib/sober_swag.rb +6 -3
  41. data/lib/sober_swag/compiler/error.rb +2 -0
  42. data/lib/sober_swag/compiler/path.rb +2 -5
  43. data/lib/sober_swag/compiler/paths.rb +0 -1
  44. data/lib/sober_swag/compiler/type.rb +86 -56
  45. data/lib/sober_swag/controller.rb +16 -11
  46. data/lib/sober_swag/controller/route.rb +18 -21
  47. data/lib/sober_swag/controller/undefined_body_error.rb +3 -0
  48. data/lib/sober_swag/controller/undefined_path_error.rb +3 -0
  49. data/lib/sober_swag/controller/undefined_query_error.rb +3 -0
  50. data/lib/sober_swag/input_object.rb +28 -0
  51. data/lib/sober_swag/nodes/array.rb +1 -1
  52. data/lib/sober_swag/nodes/base.rb +5 -3
  53. data/lib/sober_swag/nodes/binary.rb +2 -1
  54. data/lib/sober_swag/nodes/enum.rb +4 -2
  55. data/lib/sober_swag/nodes/list.rb +0 -1
  56. data/lib/sober_swag/nodes/primitive.rb +6 -5
  57. data/lib/sober_swag/output_object.rb +102 -0
  58. data/lib/sober_swag/output_object/definition.rb +30 -0
  59. data/lib/sober_swag/{blueprint → output_object}/field.rb +14 -4
  60. data/lib/sober_swag/{blueprint → output_object}/field_syntax.rb +2 -2
  61. data/lib/sober_swag/{blueprint → output_object}/view.rb +15 -6
  62. data/lib/sober_swag/parser.rb +9 -4
  63. data/lib/sober_swag/serializer.rb +5 -2
  64. data/lib/sober_swag/serializer/array.rb +12 -0
  65. data/lib/sober_swag/serializer/base.rb +50 -1
  66. data/lib/sober_swag/serializer/conditional.rb +19 -2
  67. data/lib/sober_swag/serializer/field_list.rb +29 -6
  68. data/lib/sober_swag/serializer/mapped.rb +15 -3
  69. data/lib/sober_swag/serializer/meta.rb +35 -0
  70. data/lib/sober_swag/serializer/optional.rb +17 -2
  71. data/lib/sober_swag/serializer/primitive.rb +4 -1
  72. data/lib/sober_swag/server.rb +83 -0
  73. data/lib/sober_swag/types.rb +3 -0
  74. data/lib/sober_swag/version.rb +1 -1
  75. data/sober_swag.gemspec +8 -4
  76. metadata +79 -47
  77. data/Gemfile.lock +0 -92
  78. data/example/person.json +0 -4
  79. data/example/test/controllers/.keep +0 -0
  80. data/example/test/fixtures/.keep +0 -0
  81. data/example/test/fixtures/files/.keep +0 -0
  82. data/example/test/fixtures/people.yml +0 -11
  83. data/example/test/integration/.keep +0 -0
  84. data/example/test/models/.keep +0 -0
  85. data/example/test/models/person_test.rb +0 -7
  86. data/example/test/test_helper.rb +0 -13
  87. data/lib/sober_swag/blueprint.rb +0 -113
  88. data/lib/sober_swag/path.rb +0 -8
  89. data/lib/sober_swag/path/integer.rb +0 -21
  90. data/lib/sober_swag/path/lit.rb +0 -41
  91. data/lib/sober_swag/path/literal.rb +0 -29
  92. data/lib/sober_swag/path/param.rb +0 -33
@@ -1,13 +1,29 @@
1
1
  module SoberSwag
2
2
  module Serializer
3
+ ##
4
+ # Given something that serializes a type 'A',
5
+ # this can be used to make a serializer of type 'A | nil'.
6
+ #
7
+ # Or, put another way, makes serializers not crash on nil values.
3
8
  class Optional < Base
4
-
5
9
  def initialize(inner)
6
10
  @inner = inner
7
11
  end
8
12
 
9
13
  attr_reader :inner
10
14
 
15
+ def lazy_type?
16
+ @inner.lazy_type?
17
+ end
18
+
19
+ def lazy_type
20
+ @inner.lazy_type.optional
21
+ end
22
+
23
+ def finalize_lazy_type!
24
+ @inner.finalize_lazy_type!
25
+ end
26
+
11
27
  def serialize(object, options = {})
12
28
  if object.nil?
13
29
  object
@@ -23,7 +39,6 @@ module SoberSwag
23
39
  def optional(*)
24
40
  raise ArgumentError, 'no nesting optionals please'
25
41
  end
26
-
27
42
  end
28
43
  end
29
44
  end
@@ -1,5 +1,8 @@
1
1
  module SoberSwag
2
2
  module Serializer
3
+ ##
4
+ # A class that does *no* serialization: you give it a type,
5
+ # and it will pass any serialized input on verbatim.
3
6
  class Primitive < Base
4
7
  def initialize(type)
5
8
  @type = type
@@ -7,7 +10,7 @@ module SoberSwag
7
10
 
8
11
  attr_reader :type
9
12
 
10
- def serialize(object, options = {})
13
+ def serialize(object, _options = {})
11
14
  object
12
15
  end
13
16
  end
@@ -0,0 +1,83 @@
1
+ require 'set'
2
+
3
+ module SoberSwag
4
+ ##
5
+ # A basic, rack-only server to serve up swagger definitions.
6
+ # By default it is configured to work with rails, but you can pass stuff to initialize.
7
+ class Server
8
+ RAILS_CONTROLLER_PROC = proc do
9
+ Rails.application.routes.routes.map { |route|
10
+ route.defaults[:controller]
11
+ }.to_set.reject(&:nil?).map { |controller|
12
+ "#{controller}_controller".classify.constantize
13
+ }.filter { |controller| controller.ancestors.include?(SoberSwag::Controller) }
14
+ end
15
+
16
+ ##
17
+ # Start up.
18
+ #
19
+ # @param controller_proc [Proc] a proc that, when called, gives a list of {SoberSwag::Controller}s to document
20
+ # @param cache [Bool | Proc] if we should cache our defintions (default false)
21
+ def initialize(
22
+ controller_proc: RAILS_CONTROLLER_PROC,
23
+ cache: false
24
+ )
25
+ @controller_proc = controller_proc
26
+ @cache = cache
27
+ end
28
+
29
+ EFFECT_HTML = <<~HTML.freeze
30
+ <!DOCTYPE html>
31
+ <html>
32
+ <head>
33
+ <title>Swagger-UI</title>
34
+ <script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
35
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@3.23.4/swagger-ui.css"></link>
36
+ </head>
37
+ <body>
38
+ <div id="swagger">
39
+ </div>
40
+ <script>
41
+ SwaggerUIBundle({url: 'SCRIPT_NAME', dom_id: '#swagger'})
42
+ </script>
43
+ </body>
44
+ </html>
45
+ HTML
46
+
47
+ def call(env)
48
+ req = Rack::Request.new(env)
49
+ if req.path_info&.match?(/json/si) || req.get_header('Accept')&.match?(/json/si)
50
+ [200, { 'Content-Type' => 'application/json' }, [generate_json_string]]
51
+ else
52
+ [200, { 'Content-Type' => 'text/html' }, [EFFECT_HTML.gsub(/SCRIPT_NAME/, env['SCRIPT_NAME'] + '.json')]]
53
+ end
54
+ end
55
+
56
+ def generate_json_string
57
+ if cache?
58
+ @json_string ||= JSON.dump(generate_swagger)
59
+ else
60
+ JSON.dump(generate_swagger)
61
+ end
62
+ end
63
+
64
+ def cache?
65
+ @cache.respond_to?(:call) ? @cache.call : @cache
66
+ end
67
+
68
+ def generate_swagger
69
+ routes = sober_controllers.flat_map(&:defined_routes).reduce(SoberSwag::Compiler.new) { |c, r| c.add_route(r) }
70
+ {
71
+ openapi: '3.0.0',
72
+ info: {
73
+ version: '1',
74
+ title: 'SoberSwag Swagger'
75
+ }
76
+ }.merge(routes.to_swagger)
77
+ end
78
+
79
+ def sober_controllers
80
+ @controller_proc.call
81
+ end
82
+ end
83
+ end
@@ -1,4 +1,7 @@
1
1
  module SoberSwag
2
+ ##
3
+ # Container for types.
4
+ # You can use constants like SoberSwag::Types::Integer and things as a result of this module existing.
2
5
  class Types
3
6
  include ::Dry::Types()
4
7
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SoberSwag
4
- VERSION = '0.1.0'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -15,10 +15,12 @@ Gem::Specification.new do |spec|
15
15
  spec.homepage = 'https://github.com/SonderMindOrg/sober_swag'
16
16
  spec.license = 'MIT'
17
17
 
18
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
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
@@ -28,13 +30,15 @@ Gem::Specification.new do |spec|
28
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
31
  spec.require_paths = ['lib']
30
32
 
33
+ spec.add_dependency 'activesupport'
31
34
  spec.add_dependency 'dry-struct', '~> 1.0'
32
35
  spec.add_dependency 'dry-types', '~> 1.2'
33
- spec.add_dependency 'activesupport'
34
36
 
35
- spec.add_development_dependency 'pry'
36
- spec.add_development_dependency 'simplecov'
37
37
  spec.add_development_dependency 'bundler', '~> 2.0'
38
+ spec.add_development_dependency 'pry'
38
39
  spec.add_development_dependency 'rake', '~> 13.0'
39
40
  spec.add_development_dependency 'rspec', '~> 3.0'
41
+ spec.add_development_dependency 'rubocop'
42
+ spec.add_development_dependency 'rubocop-rspec'
43
+ spec.add_development_dependency 'simplecov'
40
44
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sober_swag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.6.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-05-21 00:00:00.000000000 Z
11
+ date: 2020-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: dry-struct
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -39,19 +53,19 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: '1.2'
41
55
  - !ruby/object:Gem::Dependency
42
- name: activesupport
56
+ name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - ">="
59
+ - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
61
+ version: '2.0'
62
+ type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - ">="
66
+ - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '0'
68
+ version: '2.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: pry
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -67,61 +81,75 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: simplecov
84
+ name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: '13.0'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: '13.0'
83
97
  - !ruby/object:Gem::Dependency
84
- name: bundler
98
+ name: rspec
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '2.0'
103
+ version: '3.0'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '2.0'
110
+ version: '3.0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: rake
112
+ name: rubocop
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - "~>"
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
- version: '13.0'
117
+ version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - "~>"
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
- version: '13.0'
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
- name: rspec
126
+ name: rubocop-rspec
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - "~>"
129
+ - - ">="
116
130
  - !ruby/object:Gem::Version
117
- version: '3.0'
131
+ version: '0'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - "~>"
136
+ - - ">="
123
137
  - !ruby/object:Gem::Version
124
- version: '3.0'
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
125
153
  description: Parse data, don't write docs
126
154
  email:
127
155
  - asuper@sondermind.com
@@ -129,6 +157,8 @@ executables: []
129
157
  extensions: []
130
158
  extra_rdoc_files: []
131
159
  files:
160
+ - ".github/config/rubocop_linter_action.yml"
161
+ - ".github/workflows/lint.yml"
132
162
  - ".github/workflows/ruby.yml"
133
163
  - ".gitignore"
134
164
  - ".rspec"
@@ -136,13 +166,14 @@ files:
136
166
  - ".ruby-version"
137
167
  - ".travis.yml"
138
168
  - Gemfile
139
- - Gemfile.lock
140
169
  - LICENSE.txt
141
170
  - README.md
142
171
  - Rakefile
143
172
  - bin/console
144
173
  - bin/setup
174
+ - docs/serializers.md
145
175
  - example/.gitignore
176
+ - example/.rspec
146
177
  - example/.ruby-version
147
178
  - example/Gemfile
148
179
  - example/Gemfile.lock
@@ -151,10 +182,15 @@ files:
151
182
  - example/app/controllers/application_controller.rb
152
183
  - example/app/controllers/concerns/.keep
153
184
  - example/app/controllers/people_controller.rb
185
+ - example/app/controllers/posts_controller.rb
154
186
  - example/app/jobs/application_job.rb
155
187
  - example/app/models/application_record.rb
156
188
  - example/app/models/concerns/.keep
157
189
  - example/app/models/person.rb
190
+ - example/app/models/post.rb
191
+ - example/app/output_objects/person_errors_output_object.rb
192
+ - example/app/output_objects/person_output_object.rb
193
+ - example/app/output_objects/post_output_object.rb
158
194
  - example/bin/bundle
159
195
  - example/bin/rails
160
196
  - example/bin/rake
@@ -181,27 +217,20 @@ files:
181
217
  - example/config/routes.rb
182
218
  - example/config/spring.rb
183
219
  - example/db/migrate/20200311152021_create_people.rb
220
+ - example/db/migrate/20200603172347_create_posts.rb
184
221
  - example/db/schema.rb
185
222
  - example/db/seeds.rb
186
223
  - example/lib/tasks/.keep
187
224
  - example/log/.keep
188
- - example/person.json
189
225
  - example/public/robots.txt
190
- - example/test/controllers/.keep
191
- - example/test/fixtures/.keep
192
- - example/test/fixtures/files/.keep
193
- - example/test/fixtures/people.yml
194
- - example/test/integration/.keep
195
- - example/test/models/.keep
196
- - example/test/models/person_test.rb
197
- - example/test/test_helper.rb
226
+ - example/spec/rails_helper.rb
227
+ - example/spec/requests/people/create_spec.rb
228
+ - example/spec/requests/people/get_spec.rb
229
+ - example/spec/requests/people/index_spec.rb
230
+ - example/spec/spec_helper.rb
198
231
  - example/tmp/.keep
199
232
  - example/vendor/.keep
200
233
  - lib/sober_swag.rb
201
- - lib/sober_swag/blueprint.rb
202
- - lib/sober_swag/blueprint/field.rb
203
- - lib/sober_swag/blueprint/field_syntax.rb
204
- - lib/sober_swag/blueprint/view.rb
205
234
  - lib/sober_swag/compiler.rb
206
235
  - lib/sober_swag/compiler/error.rb
207
236
  - lib/sober_swag/compiler/path.rb
@@ -212,6 +241,7 @@ files:
212
241
  - lib/sober_swag/controller/undefined_body_error.rb
213
242
  - lib/sober_swag/controller/undefined_path_error.rb
214
243
  - lib/sober_swag/controller/undefined_query_error.rb
244
+ - lib/sober_swag/input_object.rb
215
245
  - lib/sober_swag/nodes.rb
216
246
  - lib/sober_swag/nodes/array.rb
217
247
  - lib/sober_swag/nodes/attribute.rb
@@ -224,20 +254,22 @@ files:
224
254
  - lib/sober_swag/nodes/one_of.rb
225
255
  - lib/sober_swag/nodes/primitive.rb
226
256
  - lib/sober_swag/nodes/sum.rb
257
+ - lib/sober_swag/output_object.rb
258
+ - lib/sober_swag/output_object/definition.rb
259
+ - lib/sober_swag/output_object/field.rb
260
+ - lib/sober_swag/output_object/field_syntax.rb
261
+ - lib/sober_swag/output_object/view.rb
227
262
  - lib/sober_swag/parser.rb
228
- - lib/sober_swag/path.rb
229
- - lib/sober_swag/path/integer.rb
230
- - lib/sober_swag/path/lit.rb
231
- - lib/sober_swag/path/literal.rb
232
- - lib/sober_swag/path/param.rb
233
263
  - lib/sober_swag/serializer.rb
234
264
  - lib/sober_swag/serializer/array.rb
235
265
  - lib/sober_swag/serializer/base.rb
236
266
  - lib/sober_swag/serializer/conditional.rb
237
267
  - lib/sober_swag/serializer/field_list.rb
238
268
  - lib/sober_swag/serializer/mapped.rb
269
+ - lib/sober_swag/serializer/meta.rb
239
270
  - lib/sober_swag/serializer/optional.rb
240
271
  - lib/sober_swag/serializer/primitive.rb
272
+ - lib/sober_swag/server.rb
241
273
  - lib/sober_swag/types.rb
242
274
  - lib/sober_swag/version.rb
243
275
  - sober_swag.gemspec
@@ -255,14 +287,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
255
287
  requirements:
256
288
  - - ">="
257
289
  - !ruby/object:Gem::Version
258
- version: '0'
290
+ version: 2.6.0
259
291
  required_rubygems_version: !ruby/object:Gem::Requirement
260
292
  requirements:
261
293
  - - ">="
262
294
  - !ruby/object:Gem::Version
263
295
  version: '0'
264
296
  requirements: []
265
- rubygems_version: 3.1.2
297
+ rubygems_version: 3.0.3
266
298
  signing_key:
267
299
  specification_version: 4
268
300
  summary: Generate swagger types from dry-types