sober_swag 0.21.0 → 0.22.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/CHANGELOG.md +5 -0
  4. data/bin/console +30 -10
  5. data/docs/reporting.md +190 -0
  6. data/example/Gemfile +2 -2
  7. data/example/Gemfile.lock +92 -101
  8. data/example/app/controllers/application_controller.rb +4 -0
  9. data/example/app/controllers/people_controller.rb +44 -28
  10. data/example/app/output_objects/identified_output.rb +7 -0
  11. data/example/app/output_objects/person_output_object.rb +37 -11
  12. data/example/app/output_objects/post_output_object.rb +0 -4
  13. data/example/app/output_objects/reporting_post_output.rb +18 -0
  14. data/example/bin/rspec +29 -0
  15. data/example/spec/requests/people/create_spec.rb +3 -2
  16. data/example/spec/requests/people/index_spec.rb +1 -1
  17. data/lib/sober_swag/compiler/path.rb +3 -1
  18. data/lib/sober_swag/compiler.rb +58 -12
  19. data/lib/sober_swag/controller/route.rb +44 -8
  20. data/lib/sober_swag/controller.rb +18 -5
  21. data/lib/sober_swag/reporting/compiler.rb +39 -0
  22. data/lib/sober_swag/reporting/input/base.rb +11 -0
  23. data/lib/sober_swag/reporting/input/bool.rb +19 -0
  24. data/lib/sober_swag/reporting/input/converting/bool.rb +24 -0
  25. data/lib/sober_swag/reporting/input/converting/date.rb +30 -0
  26. data/lib/sober_swag/reporting/input/converting/date_time.rb +28 -0
  27. data/lib/sober_swag/reporting/input/converting/decimal.rb +24 -0
  28. data/lib/sober_swag/reporting/input/converting/integer.rb +19 -0
  29. data/lib/sober_swag/reporting/input/converting.rb +16 -0
  30. data/lib/sober_swag/reporting/input/defer.rb +29 -0
  31. data/lib/sober_swag/reporting/input/described.rb +38 -0
  32. data/lib/sober_swag/reporting/input/dictionary.rb +37 -0
  33. data/lib/sober_swag/reporting/input/either.rb +51 -0
  34. data/lib/sober_swag/reporting/input/enum.rb +44 -0
  35. data/lib/sober_swag/reporting/input/format.rb +39 -0
  36. data/lib/sober_swag/reporting/input/interface.rb +87 -0
  37. data/lib/sober_swag/reporting/input/list.rb +44 -0
  38. data/lib/sober_swag/reporting/input/mapped.rb +36 -0
  39. data/lib/sober_swag/reporting/input/merge_objects.rb +72 -0
  40. data/lib/sober_swag/reporting/input/null.rb +34 -0
  41. data/lib/sober_swag/reporting/input/number.rb +19 -0
  42. data/lib/sober_swag/reporting/input/object/property.rb +53 -0
  43. data/lib/sober_swag/reporting/input/object.rb +100 -0
  44. data/lib/sober_swag/reporting/input/pattern.rb +46 -0
  45. data/lib/sober_swag/reporting/input/referenced.rb +38 -0
  46. data/lib/sober_swag/reporting/input/struct.rb +271 -0
  47. data/lib/sober_swag/reporting/input/text.rb +42 -0
  48. data/lib/sober_swag/reporting/input.rb +54 -0
  49. data/lib/sober_swag/reporting/invalid_schema_error.rb +21 -0
  50. data/lib/sober_swag/reporting/output/base.rb +25 -0
  51. data/lib/sober_swag/reporting/output/bool.rb +25 -0
  52. data/lib/sober_swag/reporting/output/defer.rb +69 -0
  53. data/lib/sober_swag/reporting/output/described.rb +42 -0
  54. data/lib/sober_swag/reporting/output/dictionary.rb +46 -0
  55. data/lib/sober_swag/reporting/output/interface.rb +83 -0
  56. data/lib/sober_swag/reporting/output/list.rb +54 -0
  57. data/lib/sober_swag/reporting/output/merge_objects.rb +97 -0
  58. data/lib/sober_swag/reporting/output/null.rb +25 -0
  59. data/lib/sober_swag/reporting/output/number.rb +25 -0
  60. data/lib/sober_swag/reporting/output/object/property.rb +45 -0
  61. data/lib/sober_swag/reporting/output/object.rb +54 -0
  62. data/lib/sober_swag/reporting/output/partitioned.rb +77 -0
  63. data/lib/sober_swag/reporting/output/pattern.rb +50 -0
  64. data/lib/sober_swag/reporting/output/referenced.rb +42 -0
  65. data/lib/sober_swag/reporting/output/struct.rb +262 -0
  66. data/lib/sober_swag/reporting/output/text.rb +25 -0
  67. data/lib/sober_swag/reporting/output/via_map.rb +67 -0
  68. data/lib/sober_swag/reporting/output/viewed.rb +72 -0
  69. data/lib/sober_swag/reporting/output.rb +54 -0
  70. data/lib/sober_swag/reporting/report/base.rb +57 -0
  71. data/lib/sober_swag/reporting/report/either.rb +36 -0
  72. data/lib/sober_swag/reporting/report/error.rb +15 -0
  73. data/lib/sober_swag/reporting/report/list.rb +28 -0
  74. data/lib/sober_swag/reporting/report/merged_object.rb +25 -0
  75. data/lib/sober_swag/reporting/report/object.rb +29 -0
  76. data/lib/sober_swag/reporting/report/output.rb +14 -0
  77. data/lib/sober_swag/reporting/report/value.rb +28 -0
  78. data/lib/sober_swag/reporting/report.rb +16 -0
  79. data/lib/sober_swag/reporting.rb +11 -0
  80. data/lib/sober_swag/version.rb +1 -1
  81. data/lib/sober_swag.rb +1 -0
  82. metadata +65 -2
@@ -0,0 +1,53 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Input
4
+ class Object
5
+ ##
6
+ # Describe a single property key in an object.
7
+ class Property
8
+ def initialize(value, required:, description: '')
9
+ @value = value
10
+ @required = required
11
+ @description = description
12
+ end
13
+
14
+ ##
15
+ # @return [SoberSwag::Reporting::Input::Interface] value type
16
+ attr_reader :value
17
+
18
+ def required?
19
+ @required
20
+ end
21
+
22
+ ##
23
+ # @return [String, nil] description
24
+ attr_reader :description
25
+
26
+ def property_schema
27
+ direct, refined = value.swagger_schema
28
+
29
+ if description
30
+ [add_description(direct), refined]
31
+ else
32
+ [direct, refined]
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def add_description(dir)
39
+ t =
40
+ if dir.key?(:$ref)
41
+ # workaround: we have to do this if we want to allow
42
+ # descriptions in reference types
43
+ { allOf: [dir] }
44
+ else
45
+ dir
46
+ end
47
+ t.merge(description: description)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,100 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Input
4
+ ##
5
+ # Input object values
6
+ class Object < Base
7
+ autoload :Property, 'sober_swag/reporting/input/object/property'
8
+ ##
9
+ # @param fields [Hash<Symbol, Property>]
10
+ def initialize(fields)
11
+ @fields = fields
12
+ end
13
+
14
+ ##
15
+ # @return [Hash<String,#call>]
16
+ attr_reader :fields
17
+
18
+ def call(value)
19
+ return Report::Value.new(['was a not a JSON object']) unless value.is_a?(Hash)
20
+
21
+ bad, good = fields.map { |k, prop|
22
+ extract_value(k, prop, value)
23
+ }.compact.partition { |(_, v)| v.is_a?(Report::Base) }
24
+
25
+ return Report::Object.new(bad.to_h) if bad.any?
26
+
27
+ good.to_h
28
+ end
29
+
30
+ def swagger_schema
31
+ fields, found = field_schemas
32
+
33
+ obj = {
34
+ type: 'object',
35
+ properties: fields
36
+ }.merge(required_portion)
37
+
38
+ [obj, found]
39
+ end
40
+
41
+ def swagger_query_schema
42
+ swagger_parameter_schema.map do |param|
43
+ param.merge({ in: :query, style: :deepObject, explode: true })
44
+ end
45
+ end
46
+
47
+ def swagger_path_schema
48
+ swagger_parameter_schema.map do |param|
49
+ param.merge({ in: :path })
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def swagger_parameter_schema
56
+ fields.map do |name, field|
57
+ key_schema, = field.property_schema
58
+ base = {
59
+ name: name,
60
+ schema: key_schema,
61
+ required: field.required?
62
+ }
63
+ field.description ? base.merge(description: field.description) : base
64
+ end
65
+ end
66
+
67
+ def field_schemas
68
+ fields.reduce([{}, Set.new]) do |(field_schemas, found), (k, v)|
69
+ key_schema, key_found = v.property_schema
70
+ [
71
+ field_schemas.merge(k => key_schema),
72
+ found.merge(key_found)
73
+ ]
74
+ end
75
+ end
76
+
77
+ ##
78
+ # Either the list of required keys, or something stating "provide at least one key."
79
+ # This is needed because you can't have an empty list of keys.
80
+ def required_portion
81
+ required_fields = fields.map { |k, v| k if v.required? }.compact
82
+
83
+ if required_fields.empty?
84
+ {}
85
+ else
86
+ { required: required_fields }
87
+ end
88
+ end
89
+
90
+ def extract_value(key, property, input)
91
+ if input.key?(key)
92
+ [key, property.value.call(input[key])]
93
+ elsif property.required?
94
+ [key, Report::Value.new(['is required'])]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,46 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Input
4
+ ##
5
+ # Input values that validate against a pattern
6
+ class Pattern < Base
7
+ def initialize(input, pattern)
8
+ @input = input
9
+ @pattern = pattern
10
+ end
11
+
12
+ ##
13
+ # @return [#call] input type
14
+ attr_reader :input
15
+
16
+ ##
17
+ # @return [#matches] regexp matcher
18
+ attr_reader :pattern
19
+
20
+ def call(value)
21
+ val = input.call(value)
22
+
23
+ return val if val.is_a?(Report::Base)
24
+
25
+ if pattern.match?(value)
26
+ value
27
+ else
28
+ Report::Value.new(["did not match pattern #{pattern}"])
29
+ end
30
+ end
31
+
32
+ def swagger_schema
33
+ single, found = input.swagger_schema
34
+
35
+ [add_schema_key(single, { pattern: formatted_pattern }), found]
36
+ end
37
+
38
+ ##
39
+ # Try to format a pattern so it'll work nicely with JS.
40
+ def formatted_pattern
41
+ pattern.to_s.gsub('?-mix:', '')
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Input
4
+ ##
5
+ # An input that should be "referenced" in the final schema.
6
+ class Referenced < Base
7
+ def initialize(value, reference)
8
+ @value = value
9
+ @reference = reference
10
+ end
11
+
12
+ ##
13
+ # @return [Interface] the actual input
14
+ attr_reader :value
15
+ ##
16
+ # @return [String] key in the components hash
17
+ attr_reader :reference
18
+
19
+ def call(input)
20
+ @value.call(input)
21
+ end
22
+
23
+ def swagger_schema
24
+ [
25
+ { "$ref": ref_path },
26
+ { reference => proc { value.swagger_schema } }
27
+ ]
28
+ end
29
+
30
+ private
31
+
32
+ def ref_path
33
+ "#/components/schemas/#{reference}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,271 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Input
4
+ ##
5
+ # Base class of input structs.
6
+ #
7
+ # These allow you to define both an input type and a ruby type at once.
8
+ # They provide a fluid interface for doing so.
9
+ #
10
+ # Classes which inherit from {Struct} "quack like" an {Interface}, so you can use them as input type definitions.
11
+ #
12
+ # You should add attributes using the {.attribute} or {.attribute?} methods.
13
+ # These also let you nest definitions, so this is okay:
14
+ #
15
+ # ```ruby
16
+ # class Person < SoberSwag::Reporting::Input::Struct
17
+ # attribute :first_name, SoberSwag::Reporting::Input.text
18
+ # attribute :stats do
19
+ # attribute :average_score, SoberSwag::Reporting::Input.number
20
+ # end
21
+ # end
22
+ # ```
23
+ class Struct # rubocop:disable Metrics/ClassLength
24
+ class << self
25
+ ##
26
+ # @overload attribute(name, input, description: nil)
27
+ # Define a new attribute, which will be required.
28
+ # @param name [Symbol] the name of this attribute
29
+ # @param input [Interface] input reporting type
30
+ # @param description [String,nil] description for this attribute
31
+ # @overload attribute(name, description: nil, &block)
32
+ # Define a new nested attribute, which will be required, using a block to describe
33
+ # a sub-struct. This block will immediately be evaluated to create a child struct.
34
+ # @param name [Symbol] the name of the attribute.
35
+ # The sub-struct defined will be stored in a constant on this class,
36
+ # under this name, classified.
37
+ #
38
+ # So if the name is :first_name, then the constant will be FirstName
39
+ # @param description [String, nil] describe this attribute
40
+ # @yieldself [SoberSwag::Reporting::Input::Struct] yields
41
+ def attribute(name, input = nil, description: nil, &block)
42
+ input_type = make_input_type(name, input, block)
43
+ add_attribute!(name, input_type, required: true, description: description)
44
+ end
45
+
46
+ ##
47
+ # @overload attribute?(name, input, description: nil)
48
+ # Define a new attribute, which will be not required.
49
+ # @param name [Symbol] the name of this attribute
50
+ # @param input [Interface] input reporting type
51
+ # @param description [String,nil] description for this attribute
52
+ # @overload attribute?(name, description: nil, &block)
53
+ # Define a new nested attribute, which will not be required, using a block to describe
54
+ # a sub-struct. This block will immediately be evaluated to create a child struct.
55
+ # @param name [Symbol] the name of the attribute.
56
+ # The sub-struct defined will be stored in a constant on this class,
57
+ # under this name, classified.
58
+ #
59
+ # So if the name is :first_name, then the constant will be FirstName
60
+ # @param description [String, nil] describe this attribute
61
+ # @yieldself [SoberSwag::Reporting::Input::Struct] yields
62
+ def attribute?(name, input = nil, description: nil, &block)
63
+ input_type = make_input_type(name, input, block)
64
+
65
+ add_attribute!(name, input_type, required: false, description: description)
66
+ end
67
+
68
+ ##
69
+ # Add an attribute, specifying if it is required or not via an argument.
70
+ # You should use {#attribute} or {#attribute?} instead of this almost always.
71
+ #
72
+ # @param name [Symbol] name of this attribute
73
+ # @param input [Interface] type fot this attribue
74
+ # @param required [true,false] if this attribute is required
75
+ # @param description [String,nil] optional description for this attribute
76
+ #
77
+ def add_attribute!(name, input, required:, description: nil)
78
+ raise ArgumentError, 'name must be a symbol' unless name.is_a?(Symbol)
79
+
80
+ define_attribute(name) # defines an instance method to access this attribute
81
+
82
+ object_properties[name] = Object::Property.new(
83
+ input,
84
+ required: required,
85
+ description: description
86
+ )
87
+ end
88
+
89
+ ##
90
+ # Get a list of properties defined by *this instance*.
91
+ #
92
+ # Please do not mutate this, it will break everything.
93
+ #
94
+ # @return [Hash<Symbol, Object::Property>]
95
+ def object_properties
96
+ @object_properties ||= {}
97
+ end
98
+
99
+ ##
100
+ # @return [SoberSwag::Reporting::Input::Struct,nil] the struct we inherit from.
101
+ # Used to implement `allOf` style inheritance.
102
+ attr_accessor :parent_struct
103
+
104
+ ##
105
+ # @param other [Class] the inheriting class
106
+ #
107
+ # Used to implement `allOf` style inheritance by setting {#parent_struct} on the object that is inheriting from us.
108
+ def inherited(other)
109
+ other.parent_struct = self unless self == SoberSwag::Reporting::Input::Struct
110
+ end
111
+
112
+ include Interface
113
+
114
+ ##
115
+ # @return [SoberSwag::Reporting::Input::Base] the type to use for input.
116
+ def input_type
117
+ object_type.mapped { |x| new(x) }.referenced(identifier)
118
+ end
119
+
120
+ ##
121
+ # @overload identifier()
122
+ # @return [String,nil] the identifier for this object, used for its reference path.
123
+ # @overload identifier(val)
124
+ # Sets an identifier for this struct.
125
+ # @param val [String] the identifier to set
126
+ # @return [String] the set identifier.
127
+ def identifier(val = nil)
128
+ if val
129
+ @identifier = val
130
+ else
131
+ @identifier || name&.gsub('::', '.')
132
+ end
133
+ end
134
+
135
+ ##
136
+ # @return [SoberSwag::Reporting::Input::Struct, SoberSwag::Reporting::Report::Base] the struct class,
137
+ # or a report of what went wrong.
138
+ def call(attrs)
139
+ input_type.call(attrs)
140
+ end
141
+
142
+ ##
143
+ # @see #call
144
+ def parse(json)
145
+ call(json)
146
+ end
147
+
148
+ ##
149
+ # @see call!
150
+ def parse!(json)
151
+ call!(json)
152
+ end
153
+
154
+ ##
155
+ # @return [Array[Hash, Hash]] swagger schema type.
156
+ def swagger_schema
157
+ input_type.swagger_schema
158
+ end
159
+
160
+ def swagger_query_schema
161
+ object_type.swagger_query_schema
162
+ end
163
+
164
+ def swagger_path_schema
165
+ object_type.swagger_path_schema
166
+ end
167
+
168
+ private
169
+
170
+ def make_input_type(name, input, block)
171
+ raise ArgumentError, 'cannot pass a block to make a sub-struct and a field type' if input && block
172
+
173
+ return input if input
174
+
175
+ raise ArgumentError, 'must pass an input type OR a block to make a sub-struct' unless block
176
+
177
+ const_name = name.to_s.camelize
178
+
179
+ raise ArgumentError, 'cannot define struct sub-type, constant already exists!' if const_defined?(const_name)
180
+
181
+ Class.new(SoberSwag::Reporting::Input::Struct, &block).tap { |c| const_set(const_name, c) }
182
+ end
183
+
184
+ ##
185
+ # Quick method which defines an accessor method for this struct.
186
+ def define_attribute(name)
187
+ define_method(name) do
188
+ struct_properties[name]
189
+ end
190
+ define_method("#{name}_present?") do
191
+ struct_properties.key?(name)
192
+ end
193
+ end
194
+
195
+ def object_type
196
+ if parent_struct.nil?
197
+ Object.new(object_properties)
198
+ else
199
+ MergeObjects.new(parent_struct, Object.new(object_properties))
200
+ end
201
+ end
202
+ end
203
+
204
+ def initialize(props)
205
+ @struct_properties = props
206
+ end
207
+
208
+ attr_reader :struct_properties
209
+
210
+ def [](name)
211
+ @struct_properties[name]
212
+ end
213
+
214
+ ##
215
+ # Hash code for this struct.
216
+ def hash
217
+ [self.class.hash, *ordered_values.hash].hash
218
+ end
219
+
220
+ ##
221
+ # Return an array of the values of this, in order.
222
+ def ordered_values
223
+ self.class.object_properties.keys.map { |k| @struct_properties[k] }
224
+ end
225
+
226
+ ##
227
+ # Allow structs to be compared like values.
228
+ def eql?(other)
229
+ return false unless other.is_a?(self.class)
230
+
231
+ ordered_values.eql?(other.ordered_values)
232
+ end
233
+
234
+ ##
235
+ # Allow structs to be ordered like values.
236
+ def <=>(other)
237
+ return nil unless other.is_a?(self.class)
238
+
239
+ ordered_values <=> other.ordered_values
240
+ end
241
+
242
+ include Comparable
243
+
244
+ ##
245
+ # Extracts the transformed struct properties.
246
+ #
247
+ # Keys not present in the input will also not be present in this hash.
248
+ def to_h
249
+ @struct_properties.transform_values do |value|
250
+ if value.is_a?(SoberSwag::Reporting::Input::Struct)
251
+ value.to_h
252
+ else
253
+ value
254
+ end
255
+ end
256
+ end
257
+
258
+ def to_s
259
+ inspect
260
+ end
261
+
262
+ def inspect
263
+ keys = self.class.object_properties.keys.each.with_object([]) do |k, obj|
264
+ obj << "#{k}=#{public_send(k).inspect}" if public_send(:"#{k}_present?")
265
+ end
266
+ "#<#{self.class.name || self.class.inspect[2..-2]} #{keys.join(' ')}>"
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,42 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Input
4
+ ##
5
+ # Input for a single text value.
6
+ class Text < Base
7
+ def call(value)
8
+ return value if value.is_a?(String)
9
+
10
+ Report::Value.new(['was not a string'])
11
+ end
12
+
13
+ ##
14
+ # Get a new input value which requires a regexp.
15
+ #
16
+ # @paran regexp [Regexp] regular expression to match on
17
+ # @return [Pattern] pattern-based input.
18
+ def with_pattern(regexp)
19
+ Pattern.new(self, regexp)
20
+ end
21
+
22
+ include Comparable
23
+
24
+ def eql?(other)
25
+ other.class == self.class
26
+ end
27
+
28
+ def <=>(other)
29
+ eql?(other) ? 0 : 1
30
+ end
31
+
32
+ def hash
33
+ [self.class.hash, 1].hash
34
+ end
35
+
36
+ def swagger_schema
37
+ [{ type: 'string' }, {}]
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ ##
4
+ # Module for SoberSwag reporting inputs.
5
+ module Input
6
+ autoload :Base, 'sober_swag/reporting/input/base'
7
+ autoload :Bool, 'sober_swag/reporting/input/bool'
8
+ autoload :Converting, 'sober_swag/reporting/input/converting'
9
+ autoload :Described, 'sober_swag/reporting/input/described'
10
+ autoload :Dictionary, 'sober_swag/reporting/input/dictionary'
11
+ autoload :Defer, 'sober_swag/reporting/input/defer'
12
+ autoload :Enum, 'sober_swag/reporting/input/enum'
13
+ autoload :Either, 'sober_swag/reporting/input/either'
14
+ autoload :Format, 'sober_swag/reporting/input/format'
15
+ autoload :Number, 'sober_swag/reporting/input/number'
16
+ autoload :Interface, 'sober_swag/reporting/input/interface'
17
+ autoload :List, 'sober_swag/reporting/input/list'
18
+ autoload :Mapped, 'sober_swag/reporting/input/mapped'
19
+ autoload :MergeObjects, 'sober_swag/reporting/input/merge_objects'
20
+ autoload :Null, 'sober_swag/reporting/input/null'
21
+ autoload :Object, 'sober_swag/reporting/input/object'
22
+ autoload :Pattern, 'sober_swag/reporting/input/pattern'
23
+ autoload :Referenced, 'sober_swag/reporting/input/referenced'
24
+ autoload :Struct, 'sober_swag/reporting/input/struct'
25
+ autoload :Text, 'sober_swag/reporting/input/text'
26
+
27
+ class << self
28
+ ##
29
+ # @return [SoberSwag::Reporting::Input::Bool]
30
+ def bool
31
+ Bool.new
32
+ end
33
+
34
+ ##
35
+ # @return [SoberSwag::Reporting::Input::Text]
36
+ def text
37
+ Text.new
38
+ end
39
+
40
+ ##
41
+ # @return [SoberSwag::Reporting::Input::Number]
42
+ def number
43
+ Number.new
44
+ end
45
+
46
+ ##
47
+ # @return [SoberSwag::Reporting::Input::Null]
48
+ def null
49
+ Null.new
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,21 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ ##
4
+ # Thrown we cannot generate a swagger schema for some reason.
5
+ #
6
+ # This typically only occurs if you use types that are too complicated.
7
+ # For example, an object type cannot be used as part of the path params.
8
+ class InvalidSchemaError < StandardError
9
+ def initialize(input)
10
+ @input = input
11
+
12
+ super("Could not generate schema for #{input}")
13
+ end
14
+
15
+ attr_reader :input
16
+
17
+ class InvalidForPathError < InvalidSchemaError; end
18
+ class InvalidForQueryError < InvalidSchemaError; end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Output
4
+ ##
5
+ # Base type for simple outputs.
6
+ class Base
7
+ include Interface
8
+
9
+ ##
10
+ # Acceptable views to use with this output.
11
+ #
12
+ # @return [Set<Symbol>] the views
13
+ def views
14
+ %i[base].to_set
15
+ end
16
+
17
+ def view(view_key)
18
+ return self if view_key == :base
19
+
20
+ raise ArgumentError, "#{view_key} is not a view" unless views.include?(view_key)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module SoberSwag
2
+ module Reporting
3
+ module Output
4
+ ##
5
+ # Output booleans.
6
+ class Bool < Base
7
+ def call(input)
8
+ input
9
+ end
10
+
11
+ def serialize_report(input)
12
+ result = call(input)
13
+
14
+ return Report::Value.new(['was not a boolean']) unless [true, false].include?(result)
15
+
16
+ result
17
+ end
18
+
19
+ def swagger_schema
20
+ [{ type: 'boolean' }, {}]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end