sober_swag 0.19.0 → 0.20.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +1 -1
  3. data/.github/workflows/ruby.yml +1 -1
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +5 -1
  6. data/.yardopts +7 -0
  7. data/CHANGELOG.md +5 -0
  8. data/Gemfile +8 -0
  9. data/README.md +1 -1
  10. data/docs/serializers.md +3 -0
  11. data/example/Gemfile +1 -1
  12. data/example/config/environments/production.rb +1 -1
  13. data/lib/sober_swag.rb +6 -1
  14. data/lib/sober_swag/compiler.rb +29 -3
  15. data/lib/sober_swag/compiler/path.rb +42 -3
  16. data/lib/sober_swag/compiler/paths.rb +20 -0
  17. data/lib/sober_swag/compiler/primitive.rb +17 -0
  18. data/lib/sober_swag/compiler/type.rb +105 -22
  19. data/lib/sober_swag/controller.rb +39 -12
  20. data/lib/sober_swag/controller/route.rb +103 -20
  21. data/lib/sober_swag/input_object.rb +90 -7
  22. data/lib/sober_swag/nodes/array.rb +19 -0
  23. data/lib/sober_swag/nodes/attribute.rb +45 -4
  24. data/lib/sober_swag/nodes/base.rb +27 -7
  25. data/lib/sober_swag/nodes/binary.rb +30 -13
  26. data/lib/sober_swag/nodes/enum.rb +16 -1
  27. data/lib/sober_swag/nodes/list.rb +20 -0
  28. data/lib/sober_swag/nodes/nullable_primitive.rb +3 -0
  29. data/lib/sober_swag/nodes/object.rb +4 -1
  30. data/lib/sober_swag/nodes/one_of.rb +11 -3
  31. data/lib/sober_swag/nodes/primitive.rb +34 -2
  32. data/lib/sober_swag/nodes/sum.rb +8 -0
  33. data/lib/sober_swag/output_object.rb +35 -4
  34. data/lib/sober_swag/output_object/definition.rb +31 -1
  35. data/lib/sober_swag/output_object/field.rb +31 -11
  36. data/lib/sober_swag/output_object/field_syntax.rb +19 -3
  37. data/lib/sober_swag/output_object/view.rb +46 -1
  38. data/lib/sober_swag/parser.rb +7 -1
  39. data/lib/sober_swag/serializer/array.rb +27 -3
  40. data/lib/sober_swag/serializer/base.rb +75 -25
  41. data/lib/sober_swag/serializer/conditional.rb +33 -1
  42. data/lib/sober_swag/serializer/field_list.rb +18 -2
  43. data/lib/sober_swag/serializer/mapped.rb +10 -1
  44. data/lib/sober_swag/serializer/optional.rb +18 -1
  45. data/lib/sober_swag/serializer/primitive.rb +3 -0
  46. data/lib/sober_swag/server.rb +5 -1
  47. data/lib/sober_swag/type/named.rb +14 -0
  48. data/lib/sober_swag/types/comma_array.rb +4 -0
  49. data/lib/sober_swag/version.rb +1 -1
  50. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63ab4c5db5ca2e6fe63713b7416d127b3bdc730e82562d4ca13b477eaf362460
4
- data.tar.gz: ce6351db33dc3506be6baa66e228532bd8e3830c9e7b67b27c2c6b899a77677c
3
+ metadata.gz: 97cb4bbfd84f28f6f79368c3f78182835217868a1d3142c38107ab3b03552952
4
+ data.tar.gz: 1cd8446250a8ddadc16eb114a773b9d317c2182e2bcb8c692c59681331ef972b
5
5
  SHA512:
6
- metadata.gz: bab9a59d19cef6552f33555a5cac081c750df97f0364d06fb39fbd5ad4973262f43ca233d52be1f0544815830345a59574dd4913ca2ec6cb2ef7f9e465502f35
7
- data.tar.gz: f0e3db070ac4b30457d309d68a88c81b2cb090478553a622f9463e8cff9f3f67bb128f120ebcd9ce73164f21fb2e83b4a8befa02e87779e241fae96188ccc077
6
+ metadata.gz: f6b055ea451db16f12a02ebe2bc6da459aefcf13605a54f4f0b0cff8aa694b73e2c4cb2c2f759a89c859a820a74a37391728998e0939a770cda2783c2190586e
7
+ data.tar.gz: 3a49c153297750447c2776b086da135b81c80a1b628780d9a7ab6f68f204ddeaa3b21e08ffd02f8a49e92f1a7ac5942b0e42d295307299e8623ebb6a765731dd
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby: [ '2.6', '2.7' ]
22
+ ruby: [ '2.6', '2.7', '3.0' ]
23
23
 
24
24
  steps:
25
25
  - uses: actions/checkout@v2
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby: [ '2.6', '2.7' ]
22
+ ruby: [ '2.6', '2.7', '3.0' ]
23
23
  steps:
24
24
  - uses: actions/checkout@v2
25
25
  - name: Set up Ruby
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ Gemfile.lock
15
15
  *.gem
16
16
 
17
17
  /vendor/
18
+
19
+ .yardoc
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
@@ -0,0 +1,7 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
3
+ --plugin activesupport-concern
4
+ --plugin solargraph
5
+ --private
6
+ --protected
7
+ --files docs/serializers.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.20.0] 2021-05-17
4
+
5
+ - Added YARD documentation to almost every method
6
+ - Added `except` parameter to the `merge` method, which allows a specified field to be excluded from the merge.
7
+
3
8
  ## [v0.19.0] 2021-03-10
4
9
 
5
10
  - Use [redoc](https://github.com/Redocly/redoc) for generated documentation UI
data/Gemfile CHANGED
@@ -4,3 +4,11 @@ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in sober_swag.gemspec
6
6
  gemspec
7
+
8
+ gem 'yard'
9
+
10
+ gem 'redcarpet'
11
+
12
+ gem 'yard-activesupport-concern'
13
+
14
+ gem 'solargraph'
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
- - [Serializers](docs/serializers.md)
14
+ - {file:docs/serializers.md Serializers}
15
15
 
16
16
  ## Types for a fully-automated API
17
17
 
data/docs/serializers.md CHANGED
@@ -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
@@ -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.5'
37
+ gem 'listen', '>= 3.0.5', '< 3.6'
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'
@@ -60,7 +60,7 @@ Rails.application.configure do
60
60
  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
61
61
 
62
62
  if ENV['RAILS_LOG_TO_STDOUT'].present?
63
- logger = ActiveSupport::Logger.new(STDOUT)
63
+ logger = ActiveSupport::Logger.new($stdout)
64
64
  logger.formatter = config.log_formatter
65
65
  config.logger = ActiveSupport::TaggedLogging.new(logger)
66
66
  end
data/lib/sober_swag.rb CHANGED
@@ -9,8 +9,10 @@ require 'sober_swag/version'
9
9
  require 'active_support/inflector'
10
10
 
11
11
  ##
12
- # Root namespace
12
+ # Root namespace for the SoberSwag Module.
13
13
  module SoberSwag
14
+ ##
15
+ # Root Error Class for SoberSwag errors.
14
16
  class Error < StandardError; end
15
17
 
16
18
  autoload :Parser, 'sober_swag/parser'
@@ -26,7 +28,10 @@ module SoberSwag
26
28
  ##
27
29
  # Define a struct of something.
28
30
  # Useful to prevent weirdness from autoloading.
31
+ #
29
32
  # @param parent [Class] the base class for the struct (default of {SoberSwag::Struct})
33
+ # @yieldself [SoberSwag::InputObject]
34
+ # @return [Class] the input object class generated
30
35
  def self.input_object(parent = nil, &block)
31
36
  Class.new(parent || SoberSwag::InputObject, &block)
32
37
  end
@@ -17,6 +17,8 @@ module SoberSwag
17
17
 
18
18
  ##
19
19
  # Convert a compiler to the overall type definition.
20
+ #
21
+ # @return Hash the swagger definition.
20
22
  def to_swagger
21
23
  {
22
24
  paths: path_schemas,
@@ -29,16 +31,23 @@ module SoberSwag
29
31
  ##
30
32
  # Add a path to be compiled.
31
33
  # @param route [SoberSwag::Controller::Route] the route to add.
34
+ # @return [Compiler] self
32
35
  def add_route(route)
33
36
  tap { @paths.add_route(route) }
34
37
  end
35
38
 
39
+ ##
40
+ # Get the schema of each object type defined in this Compiler.
41
+ #
42
+ # @return [Hash]
36
43
  def object_schemas
37
44
  @types.map { |v| [v.ref_name, v.object_schema] }.to_h
38
45
  end
39
46
 
40
47
  ##
41
48
  # The path section of the swagger schema.
49
+ #
50
+ # @return [Hash]
42
51
  def path_schemas
43
52
  @paths.paths_list(self)
44
53
  end
@@ -46,6 +55,9 @@ module SoberSwag
46
55
  ##
47
56
  # Compile a type to a new, path-params list.
48
57
  # This will add all subtypes to the found types list.
58
+ #
59
+ # @param type [Class] the type to get a path_params definition for
60
+ # @return [Hash]
49
61
  def path_params_for(type)
50
62
  with_types_discovered(type).path_schema
51
63
  end
@@ -53,6 +65,9 @@ module SoberSwag
53
65
  ##
54
66
  # Get the query params list for a type.
55
67
  # All found types will be added to the reference dictionary.
68
+ #
69
+ # @param type [Class] the type to get the query_params definitions for
70
+ # @return [Hash]
56
71
  def query_params_for(type)
57
72
  with_types_discovered(type).query_schema
58
73
  end
@@ -60,25 +75,36 @@ module SoberSwag
60
75
  ##
61
76
  # Get the request body definition for a type.
62
77
  # This will always be a ref.
78
+ #
79
+ # @param type [Class] the type to get the body definition for
80
+ # @return [Hash]
63
81
  def body_for(type)
64
82
  add_type(type)
65
83
  Type.new(type).schema_stub
66
84
  end
67
85
 
68
86
  ##
69
- # Get the definition of a response type
87
+ # Get the definition of a response type.
88
+ #
89
+ # This is an alias of {#body_for}
90
+ # @see body_for
70
91
  def response_for(type)
71
92
  body_for(type)
72
93
  end
73
94
 
74
95
  ##
75
- # Get the existing schema for a given type
96
+ # Get the existing schema for a given type.
97
+ #
98
+ # @param type [Class] the type to get the schema for
99
+ # @return [Hash,nil] the swagger schema for this object, or nil if it was not found.
76
100
  def schema_for(type)
77
101
  @types.find { |type_comp| type_comp.type == type }&.object_schema
78
102
  end
79
103
 
80
104
  ##
81
- # Add a type in the types reference dictionary, essentially
105
+ # Add a type in the types reference dictionary, essentially.
106
+ # @param type [Class] the type to compiler
107
+ # @return [SoberSwag::Compiler] self
82
108
  def add_type(type)
83
109
  # use tap here to avoid an explicit self at the end of this
84
110
  # which makes this method chainable
@@ -1,8 +1,11 @@
1
1
  module SoberSwag
2
2
  class Compiler
3
3
  ##
4
- # Compile a singular path, and that's it.
5
- # Only handles the actual body.
4
+ # This compiler transforms a {SoberSwag::Controller::Route} object into its associated OpenAPI V3 definition.
5
+ # These definitions are [called "paths" in the OpenAPI V3 spec](https://swagger.io/docs/specification/paths-and-operations/),
6
+ # thus the name of this compiler.
7
+ #
8
+ # It only compiles a *single* "path" at a time.
6
9
  class Path
7
10
  ##
8
11
  # @param route [SoberSwag::Controller::Route] a route to use
@@ -12,8 +15,18 @@ module SoberSwag
12
15
  @compiler = compiler
13
16
  end
14
17
 
15
- attr_reader :route, :compiler
18
+ ##
19
+ # @return [SoberSwag::Controller::Route]
20
+ attr_reader :route
21
+
22
+ ##
23
+ # @return [SoberSwag::Compiler] the compiler used for type compilation
24
+ attr_reader :compiler
16
25
 
26
+ ##
27
+ # The OpenAPI V3 "path" object for the associated {SoberSwag::Controller::Route}
28
+ #
29
+ # @return [Hash] the OpenAPI V3 description
17
30
  def schema
18
31
  base = {}
19
32
  base[:summary] = route.summary if route.summary
@@ -25,6 +38,11 @@ module SoberSwag
25
38
  base
26
39
  end
27
40
 
41
+ ##
42
+ # An array of "response" objects from swagger.
43
+ #
44
+ # @return [Hash{String => Hash}]
45
+ # response code to response object.
28
46
  def responses # rubocop:disable Metrics/MethodLength
29
47
  route.response_serializers.map { |status, serializer|
30
48
  [
@@ -41,10 +59,19 @@ module SoberSwag
41
59
  }.to_h
42
60
  end
43
61
 
62
+ ##
63
+ # An array of all parameters, be they in the query or in the path.
64
+ # See [this page](https://swagger.io/docs/specification/serialization/) for what that looks like.
65
+ #
66
+ # @return [Array<Hash>]
44
67
  def params
45
68
  query_params + path_params
46
69
  end
47
70
 
71
+ ##
72
+ # An array of schemas for all query parameters.
73
+ #
74
+ # @return [Array<Hash>] the schemas
48
75
  def query_params
49
76
  if route.query_params_class
50
77
  compiler.query_params_for(route.query_params_class)
@@ -53,6 +80,10 @@ module SoberSwag
53
80
  end
54
81
  end
55
82
 
83
+ ##
84
+ # An array of schemas for all path parameters.
85
+ #
86
+ # @return [Array<Hash>] the schemas
56
87
  def path_params
57
88
  if route.path_params_class
58
89
  compiler.path_params_for(route.path_params_class)
@@ -61,6 +92,11 @@ module SoberSwag
61
92
  end
62
93
  end
63
94
 
95
+ ##
96
+ # The schema for a request body.
97
+ # Matches [this spec.](https://swagger.io/docs/specification/paths-and-operations/)
98
+ #
99
+ # @return [Hash] the schema
64
100
  def request_body
65
101
  return nil unless route.request_body_class
66
102
 
@@ -74,6 +110,9 @@ module SoberSwag
74
110
  }
75
111
  end
76
112
 
113
+ ##
114
+ # The tags for this path.
115
+ # @return [Array<String>] the tags
77
116
  def tags
78
117
  return nil unless route.tags.any?
79
118
 
@@ -2,15 +2,28 @@ module SoberSwag
2
2
  class Compiler
3
3
  ##
4
4
  # Compile multiple routes into a paths set.
5
+ # This basically just aggregates {SoberSwag::Controller::Route} objects.
5
6
  class Paths
7
+ ##
8
+ # Set up a new paths compiler with no routes in it.
6
9
  def initialize
7
10
  @routes = []
8
11
  end
9
12
 
13
+ ##
14
+ # Add on a new {SoberSwag::Controller::Route}
15
+ #
16
+ # @param route [SoberSwag::Controller::Route] the route description to add to compilation
17
+ # @return [SoberSwag::Compiler::Paths] self
10
18
  def add_route(route)
11
19
  @routes << route
20
+
21
+ self
12
22
  end
13
23
 
24
+ ##
25
+ # In the OpenAPI V3 spec, we group action definitions by their path.
26
+ # This helps us do that.
14
27
  def grouped_paths
15
28
  routes.group_by(&:path)
16
29
  end
@@ -20,6 +33,9 @@ module SoberSwag
20
33
  # paths list. Since this is only a compiler for paths,
21
34
  # it has *no idea* how to handle types. So, it takes a compiler
22
35
  # which it will use to do that for it.
36
+ #
37
+ # @param compiler [SoberSwag::Compiler::Type] the type compiler to use
38
+ # @return [Hash] a schema for all contained routes.
23
39
  def paths_list(compiler)
24
40
  grouped_paths.transform_values do |values|
25
41
  values.map { |route|
@@ -31,6 +47,8 @@ module SoberSwag
31
47
  ##
32
48
  # Get a list of all types we discovered when compiling
33
49
  # the paths.
50
+ #
51
+ # @yield [Class] all the types found in all the routes described in here.
34
52
  def found_types
35
53
  return enum_for(:found_types) unless block_given?
36
54
 
@@ -41,6 +59,8 @@ module SoberSwag
41
59
  end
42
60
  end
43
61
 
62
+ ##
63
+ # @return [Array<SoberSwag::Controller::Route>] the routes to document
44
64
  attr_reader :routes
45
65
 
46
66
  private
@@ -4,7 +4,11 @@ module SoberSwag
4
4
  # Compiles a primitive type.
5
5
  # Almost always constructed with the values from
6
6
  # {SoberSwag::Nodes::Primitive}.
7
+ #
8
+ # This works by either generating a swagger primitive definition, *or* a `$ref` to one with a given identifier.
7
9
  class Primitive
10
+ ##
11
+ # @param type [Class] the swagger-able class to document.
8
12
  def initialize(type)
9
13
  @type = type
10
14
 
@@ -13,6 +17,8 @@ module SoberSwag
13
17
 
14
18
  attr_reader :type
15
19
 
20
+ ##
21
+ # Is this documenting one of the build-in swagger types?
16
22
  def swagger_primitive?
17
23
  SWAGGER_PRIMITIVE_DEFS.include?(type)
18
24
  end
@@ -25,6 +31,9 @@ module SoberSwag
25
31
 
26
32
  ##
27
33
  # Turn this type into a swagger hash with a proper type key.
34
+ # This is suitable for use as the value of a `schema` key in a definition.
35
+ #
36
+ # @return [Hash] the schema.
28
37
  def type_hash
29
38
  if swagger_primitive?
30
39
  SWAGGER_PRIMITIVE_DEFS.fetch(type)
@@ -37,10 +46,16 @@ module SoberSwag
37
46
  end
38
47
  end
39
48
 
49
+ ##
50
+ # Primitive schema used for ruby `Date` values.
40
51
  DATE_PRIMITIVE = { type: :string, format: :date }.freeze
52
+ ##
53
+ # Primitive schema used for ruby `DateTime` values.
41
54
  DATE_TIME_PRIMITIVE = { type: :string, format: :'date-time' }.freeze
42
55
  HASH_PRIMITIVE = { type: :object, additionalProperties: true }.freeze
43
56
 
57
+ ##
58
+ # Map of types that are considered "primitive types" in the OpenAPI V3 spec.
44
59
  SWAGGER_PRIMITIVE_DEFS =
45
60
  {
46
61
  NilClass => :null,
@@ -57,6 +72,8 @@ module SoberSwag
57
72
  Hash => HASH_PRIMITIVE
58
73
  ).transform_values(&:freeze).freeze
59
74
 
75
+ ##
76
+ # @return [String] the schema reference
60
77
  def ref_name
61
78
  raise Error, 'is not a type that is named!' if swagger_primitive?
62
79