sober_swag 0.19.0 → 0.20.0

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