sober_swag 0.24.0 → 0.25.1

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: da652c96f55ead0e8113b6113381da0fef7be7f0fcbeb807764dd62cf9b9e7c1
4
- data.tar.gz: 87be8dd13b0501e69bac77907bc2db755ff0ede1f5c78a87834549a5e62202a6
3
+ metadata.gz: 7c0ed44b6b2952d8465e521a0cff59729c73d1a8f6103e23baaae0ce8087a8d6
4
+ data.tar.gz: faec9887999f9c6093e2a85f8578945d9de832c4f1471a23553b9627012d3617
5
5
  SHA512:
6
- metadata.gz: 6cefa38497b4f3d6a4769afdf8bffd5d35c44db3b4a1733402d9bf231675693c0efc9195a68285d5f07e93e621f7d2757ec5405e95f50942e97c04a4bf71a930
7
- data.tar.gz: 3b0dbf95a6ce1960d04788f7260722ff37d94786ccc50afbd5db60a0f34bb03ef4888c6c0884d9c9ee93db4fd2503df05ba43b430f1feacc242a43b316df7bc1
6
+ metadata.gz: 3641dfc6dbe689cb540c0f036baa32f8d99879b36748a3f04ffa3c2d39ef9d6b9224e8eb8bb91fb304bb2e9b0069b3254c3c33f328bbae1d747899ed8cf39bb3
7
+ data.tar.gz: 1bd7d20511c9326bf33d73e2cf081fbe729fbda2fbb5dad7fd7cd1780e1bc8bcf4b456492395d70c2313c0a01ae6cf021d6ccb773f7369abc7eb18d8713b831a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.25.1] 2022-07-2021
4
+
5
+ - Stricer validation on report arguments
6
+ - Fix a bad report argument
7
+
8
+ ## [v0.25.0] 2022-06-07
9
+
10
+ - Adds more description to the error raised if you try to use a non-reporting output type as the output of a field
11
+ - Add a lot more documentation for the reporting interface in [`docs/reporting.md`](docs/reporting.md)
12
+
13
+ ## [v0.24.1] 2022-05-26
14
+
15
+ - Added a better `#message` to `SoberSwag::Reporting::Report::Error`
16
+
3
17
  ## [v0.22.0] 2021-12-21
4
18
 
5
19
  - Added `SoberSwag::Reporting`, which is basically a v2 of the gem!
data/docs/reporting.md CHANGED
@@ -120,7 +120,7 @@ A view will *always inherit all attributes of the parent object, regardless of o
120
120
  class AlternativePersonOutput < SoberSwag::Output::Struct
121
121
  field :first_name, SoberSwag::Reporting::Output.text
122
122
 
123
- view :with_grade do
123
+ define_view :with_grade do
124
124
  field :grade, SoberSwag::Reporting::Output.text.nilable do
125
125
  if object_to_serialize.respond_to?(:grade)
126
126
  object_to_serialize.grade
@@ -187,4 +187,326 @@ There are basically two things to keep in mind when upgrading to `SoberSwag::Rep
187
187
  Instead, view management is now *explicit*.
188
188
  This is because it was too tempting to pass data to serialize in the options key, which is against the point of the serializers.
189
189
 
190
+ # API Overview
190
191
 
192
+ This section presents an overview of the available reporting outputs and inputs.
193
+
194
+ ## `SoberSwag::Reporting::Output`
195
+
196
+ This module contains reporting *outputs*.
197
+ These act as type-checked serializers.
198
+
199
+ ### Primitive Types
200
+
201
+ The following "primitive types" are available:
202
+
203
+ - `SoberSwag::Reporting::Output.bool`, which returns a `SoberSwag::Reporting::Output::Bool`.
204
+ This type is for serializing boolean values, IE, `true` or `false`.
205
+ It will serialize the boolean directly to the JSON.
206
+ - `SoberSwag::Reporting::Output.null`, which returns a `SoberSwag::Reporting::Output::Null`.
207
+ This type serializes out `null` in JSON.
208
+ This can only serialize the ruby value `nil`.
209
+ - `SoberSwag::Reporting::Output.number`, returns a `SoberSwag::Reporting::Output::Number`.
210
+ This type serializes out numbers in JSON.
211
+ It can serialize out most ruby numeric types, including `Integer` and `Float`.
212
+ - `SoberSwag::Reporting::Output.text`, which returns a `SoberSwag::Reporting::Output::Text`.
213
+ This serializes out a string type in the JSON.
214
+ It can serialize out ruby strings.
215
+
216
+ ### The Transforming Type
217
+
218
+ For `SoberSwag::Reporting::Output`, there's a "fundamental" type that does *transformation*, called `via_map`.
219
+ It lets you apply a ruby block before passing the input on to the serializer after it.
220
+ It's most often used like this:
221
+
222
+ ```ruby
223
+ screaming_output = SoberSwag::Reporting::Output.text.via_map { |old_text| old_text.upcase }
224
+ screaming_output.call("what the heck")
225
+ # => "WHAT THE HECK"
226
+ ```
227
+
228
+ Note that this calls the block *before* passing to the next serializer.
229
+ So:
230
+
231
+ ```ruby
232
+ example = SoberSwag::Reporting::Output.text.via_map { |x| x.downcase }.via_map { |x| x + ", OK?" }
233
+ example.call("WHAT THE HECK?")
234
+ # => "what the heck, ok?"
235
+ ```
236
+
237
+ This type winds up being extremely useful in a *lot* of places.
238
+ For example, you can use it to provide extra information to a serializer:
239
+
240
+ ```ruby
241
+ serializer = MyCoolOutput.via_map { |x| CoolStruct.new(record: x, metadata: metadata_from_elsewhere) }
242
+ render json: serializer.list.call(my_record_relation)
243
+ ```
244
+
245
+ ### Composite Types
246
+
247
+ The following "composite types," or types built from other types, are available:
248
+
249
+ - `SoberSwag::Reporting::Output::List`, which seralizes out *lists* of values.
250
+ You can construct one in two ways:
251
+
252
+ ```ruby
253
+ SoberSwag::Reporting::Output::List.new(SoberSwag::Reporting::Output.text)
254
+ # or, via the instance method
255
+ SoberSwag::Reporting::Output.text.list
256
+ ```
257
+ This produces an output that can serialize to JSON arrays.
258
+ For example, either of these can produce:
259
+
260
+ ```json
261
+ ["foo", "bar"]
262
+ ```
263
+
264
+ This serialize will work with anything that responds to `#map`.
265
+
266
+ - `SoberSwag::Reporting::Output::Dictionary`, which can be constructed via:
267
+ ```ruby
268
+ SoberSwag::Reporting::Output::Dictionary.of(SoberSwag::Reporting::Output.number)
269
+ ```
270
+
271
+ This type serializes out a key-value dictionary, IE, a JSON object.
272
+ So, the above can serialize:
273
+ ```ruby
274
+ { "foo": 10, "bar": 11 }
275
+ ```
276
+ This type will only serialize out ruby hashes.
277
+ It will, conveniently, convert symbol keys to strings for you.
278
+
279
+ - `SoberSwag::Reporting::Output::Partitioned`, which represents the *choice* of two serializers.
280
+ It takes in a block to decide which serializer to use, a serializer to use if the block returns `true`, and a serializer to use if the block returns `false`.
281
+ That is, to serialize out *either* a string *or* a number, you might use:
282
+ ```ruby
283
+ SoberSwag::Reporting::Output::Partitioned.new(
284
+ proc { |x| x.is_a?(String) },
285
+ SoberSwag::Reporting::Output.text,
286
+ SoberSwag::Reporting::Output.number
287
+ )
288
+ ```
289
+ - `SoberSwag::Reporting::Output::Viewed`, which lets you define a *view map* for an object.
290
+ This is mostly used as an implementation detail, but can be occasionally useful if you want to provide
291
+ a list of "views" with no common "base," like an output object might have. In this case, the "base"
292
+ view is more of a "default" rather than a "parent."
293
+
294
+ ### Validation Types
295
+
296
+ OpenAPI v3 supports some *validations* on types, in addition to raw types.
297
+ For example, you can specify in your documentation that a value will be within a *range* of values.
298
+ These `SoberSwag::Reporting::Output` types provide that documentation - and perform those validations!
299
+
300
+ - `SoberSwag::Reporting::Output::InRange` validates that a value will be within a certain *range* of values.
301
+ This is most useful with numbers.
302
+ For example:
303
+ ```ruby
304
+ SoberSwag::Reporting::Output.number.in_range(0..10)
305
+ ```
306
+ - `SoberSwag::Reporting::Output::Pattern` validates that a value will match a certain *pattern.*
307
+ This is useful with strings:
308
+ ```ruby
309
+ SoberSwag::Reporting::Output::Pattern.new(SoberSwag::Reporting::Output.text, /foo|bar|baz|my-[0-5*/)
310
+ ```
311
+
312
+ ## `SoberSwag::Reporting::Input`
313
+
314
+ This module is used for *parsers*, which take in some input and return a nicer type.
315
+
316
+ ### Basic Types
317
+
318
+ These types are the "primitives" of `SoberSwag::Reporting::Input`, the most basic types:
319
+
320
+ - `SoberSwag::Reporting::Input::Null` parses a JSON `null` value.
321
+ It will parse it to a ruby `nil`, naturally.
322
+ You probably want to construct one via `SoberSwag::Reporting::Input.null`.
323
+ - `SoberSwag::Reporting::Input::Number` parses a JSON number.
324
+ It will parse to either a ruby `Integer` or a ruby `Float`, depending on the format (we use Ruby's internal format for this).
325
+ You probably want to construct one via `SoberSwag::Reporting::Input.number`.
326
+ - `SoberSwag::Reporting::Input::Bool`, which parses a JSON bool (`true` or `false`).
327
+ This will parse to a ruby `true` or `false`.
328
+ You probably want to construct it with `SoberSwag::Reporting::Output.bool`.
329
+ - `SoberSwag::Reporting::Input::Text`, which parses a JSON string (`"mike stoklassa"`, `"richard evans"`, or `"jay bauman"` for example).
330
+ This will parse to a ruby string.
331
+ You probably want to construct it with `SoberSwag::Reporting::Output.text`.
332
+
333
+ ### The Transforming Type
334
+
335
+ Much like `via_map` for `SoberSwag::Reporting::Output`, there's a fundamental type that does *transformation*, called the `mapped`.
336
+ This lets you do some transformation of input *after* others have ran.
337
+ So:
338
+
339
+ ```ruby
340
+ quiet = SoberSwag::Reporting::Input.text.mapped { |x| x.downcase }
341
+ quiet.call("WHAT THE HECK")
342
+ # => "what the heck"
343
+ ```
344
+
345
+ Note that this composes as follows:
346
+
347
+ ```ruby
348
+ example = SoberSwag::Reporting::Input.text.mapped { |x| x.downcase }.mapped { |x| x + ", OK?" }
349
+
350
+ example.call("WHAT THE HECK")
351
+ # => "what the heck, OK?"
352
+ # As you can see, the *first* function applies first, then the *second*.
353
+ ```
354
+
355
+ You might notice that this is the opposite behavior of of `SoberSwag::Reporting::Output::ViaMap`.
356
+ This is because *serialization* is the *opposite* of *parsing*.
357
+ Kinda neat, huh?
358
+
359
+ ### Composite Types
360
+
361
+ These types work with *one or more* inputs to build up *another*.
362
+
363
+ - `SoberSwag::Reporting::Input::List`, which lets you parse a JSON array.
364
+ IE:
365
+ ```ruby
366
+ SoberSwag::Reporting::Input::List.of(SoberSwag::Reporting::Input.number)
367
+ ```
368
+ Lets you parse a list of numbers.
369
+ - `SoberSwag::Reporting::Input::Either`, which lets you parse one input, and if that fails, parse another.
370
+ This represents a *choice* of input types.
371
+ This is best used via:
372
+ ```ruby
373
+ SoberSwag::Reporting::Input.text | SoberSwag::Reporting::Input.number
374
+ # or
375
+ SoberSwag::Reporting::Input.text.or SoberSwag::Reporting::Input.number
376
+ ```
377
+ This is useful if you want to allow multiple input formats.
378
+ - `SoberSwag::Reporting::Input::Dictionary`, which lets you parse a JSON dictionary with arbitrary keys.
379
+ For example, to parse this JSON (assuming you don't know the keys ahead of time):
380
+ ```json
381
+ {
382
+ "mike": 100,
383
+ "bob": 1000,
384
+ "joey": 12,
385
+ "yes": 1213
386
+ }
387
+ ```
388
+ You can use:
389
+ ```ruby
390
+ SoberSwag::Reporting::Input::Dictionary.of(SoberSwag::Reporting::Input.number)
391
+ ```
392
+
393
+ This will parse to a Ruby hash, with string keys.
394
+ If you want symbols, you can simply use `.mapped`:
395
+ ```ruby
396
+ SoberSwag::Reporting::Input::Dictionary.of(
397
+ SoberSwag::Reporting::Input.number
398
+ ).mapped { |hash| hash.transform_keys(&:to_sym) }
399
+ ```
400
+ Pretty cool, right?
401
+ - `SoberSwag::Reporting::Input::Enum`, which lets you parse an *enum value*.
402
+ This input will validate that the given value is in the enum.
403
+ Note that this doesn't only work with strings!
404
+ You can use:
405
+
406
+ ```ruby
407
+ SoberSwag::Reporting::Input.number.enum(-1, 0, 1)
408
+ ```
409
+
410
+ And things will work fine.
411
+
412
+ ### Validating Types
413
+
414
+ These types provide *validation* on an input.
415
+ The validations provided match the specifications in swagger.
416
+
417
+ - `SoberSwag::Reporting::Input::InRange`, which specifies that a value should be *within a range*.
418
+ You can use it like:
419
+
420
+ ```ruby
421
+ SoberSwag::Reporting::Input.number.in_range(1..100)
422
+ ```
423
+ - `SoberSwag::Reporting::Input::MultipleOf`, which specifies that a number is a *multiple of* some other number.
424
+ You can use it like this:
425
+ ```ruby
426
+ SoberSwag::Reporting::Input.number.multiple_of(2)
427
+ ```
428
+
429
+ Note that the `#multiple_of` method is only available on the `SoberSwag::Reporting::Input::Number` class.
430
+ - `SoberSwag::Reporting::Input::Pattern`, which lets you check that an input *matches a regexp*.
431
+ You can use it like:
432
+
433
+ ```ruby
434
+ SoberSwag::Reporting::Input.text.with_pattern(/\A(R|r)ich (E|e)vans\z/)
435
+ ```
436
+
437
+ Note that the `with_pattern` method is only available on `SoberSwag::Reporting::Input::Text`
438
+
439
+ #### Custom Validations
440
+
441
+ You might have a scenario where you need to do a *custom validation* that is not in this list.
442
+ In order to do this, you can use our old friend, `mapped`.
443
+ If you return any instance of `SoberSwag::Reporting::Report::Base` from via-map, it will be treated as a *parse error*.
444
+ This can be used for custom validations, like so:
445
+
446
+ ```ruby
447
+ UuidInput = SoberSwag::Reporting::Input.text.format('custom-identifier').mapped do |inputted_string|
448
+ if inputted_string == 'special-value'
449
+ SoberSwag::Reporting::Report::Value.new(['was the string "special-value", which is reserved'])
450
+ else
451
+ inputted_string
452
+ end
453
+ end
454
+ ```
455
+
456
+ Please note that this functionality is intended to enable data *format* validation.
457
+ **If you are making a call to a database or some API within a `mapped` block, you are doing something weird**.
458
+ Sometimes you do need to do weird things, of course, but it is generally **not appropriate** to use input validation to ensure that ids exist or whatever - leave that up to your rails models!
459
+
460
+ ### Documentating Types
461
+
462
+ These types allow you to add additional documentation.
463
+
464
+ - `SoberSwag::Reporting::Input::Format`, which provides *format description*.
465
+ This lets you specify that a given input should have a given format.
466
+ Formats are just a string, so you can use custom formats:
467
+ ```ruby
468
+ SoberSwag::Reporting::Input.text.format('user-uuid')
469
+ ```
470
+
471
+ Note that adding a format *will not do any magic validation whatsoever*.
472
+ See the section on custom validations for how to do that.
473
+
474
+ ### Converting Types
475
+
476
+ For convenience's sake, SoberSwag comes with a few built-in *converting inputs*.
477
+ These convert JSON objects into some common types that you would want to use in ruby.
478
+
479
+ - `SoberSwag::Reporting::Input::Converting::Bool`, which tries to coerce an input value to a boolean.
480
+ It will convert the following JSON values to `true`:
481
+
482
+ - The strings `"y"`, `"yes"`, `"true"`, `"t"`, or the all-caps variants of any
483
+ - The string `"1"`
484
+ - The number `1`
485
+ - a JSON `true`
486
+
487
+ And the following to false:
488
+
489
+ - The strings `"f"`, `"no"`, `"false"`, `"n"`, or any allcaps variant
490
+ - The number `0`
491
+ - An actual JSON `false`
492
+ - `SoberSwag::Reporting::Input::Converting::Date`, which tries to parse a date string.
493
+ More specifically, it:
494
+ - First tries to parse a date with [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339).
495
+ It uses [`Date#rfc3339`](https://ruby-doc.org/stdlib-3.1.2/libdoc/date/rdoc/Date.html#method-c-rfc3339) to do this.
496
+ - Then tries to parse with [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
497
+ It uses [`Date#iso8601`](https://ruby-doc.org/stdlib-3.1.2/libdoc/date/rdoc/Date.html#method-c-iso8601) to do this.
498
+ - If both of the above fail, return a descriptive error (more specifically, the error specifies that the string was not an RFC 3339 date string or an ISO 8601 date string).
499
+ - `SoberSwag::Reporting::Input::Converting::DateTime`, which works in much the same way.
500
+ More specifically, it...
501
+ - First tries to parse a timestamp with [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339).
502
+ It uses [`DateTime#rfc3339`](https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-c-rfc3339) to do this.
503
+ - Then tries to parse with [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
504
+ It uses [`DateTime#iso8601`](https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-c-iso8601) to do this.
505
+ - If both of the above fail, return a descriptive error (more specifically, the error specifies that the string was not an RFC 3339 date-time or an ISO 8601 date-time string).
506
+ - `SoberSwag::Reporting::Input::Converting::Decimal`, which tries to parse a decimal number.
507
+ If a number is passed, it will convert that number to a `BigDecimal` via `#to_d`.
508
+ If a string is passed, it uses [`Kernel#BigDecimal`](https://ruby-doc.org/stdlib-2.6/libdoc/bigdecimal/rdoc/Kernel.html#method-i-BigDecimal) to try to parse a decimal from a string.
509
+ Note: you may wish to combine this with some sort of source-length check, to ensure people cannot force you to construct extremely large, memory-intense decimals.
510
+ - `SoberSwag::Reporting::Input::Converting::Integer`, which tries to parse an integer number.
511
+ If a JSON number is passed, it uses `#to_i` to convert it to an integer.
512
+ If a string it passed, it uses [`Kernel#Integer`](https://ruby-doc.org/core-2.7.1/Kernel.html) to attempt to do the conversion, and reports an error if that fails.
data/example/Gemfile.lock CHANGED
@@ -64,7 +64,7 @@ GEM
64
64
  minitest (~> 5.1)
65
65
  tzinfo (~> 1.1)
66
66
  zeitwerk (~> 2.2, >= 2.2.2)
67
- bootsnap (1.11.1)
67
+ bootsnap (1.12.0)
68
68
  msgpack (~> 1.2)
69
69
  builder (3.2.4)
70
70
  byebug (11.1.3)
@@ -107,7 +107,7 @@ GEM
107
107
  listen (3.7.1)
108
108
  rb-fsevent (~> 0.10, >= 0.10.3)
109
109
  rb-inotify (~> 0.9, >= 0.9.10)
110
- loofah (2.16.0)
110
+ loofah (2.18.0)
111
111
  crass (~> 1.0.2)
112
112
  nokogiri (>= 1.5.9)
113
113
  mail (2.7.1)
@@ -117,9 +117,9 @@ GEM
117
117
  mini_mime (1.1.2)
118
118
  mini_portile2 (2.8.0)
119
119
  minitest (5.15.0)
120
- msgpack (1.5.1)
120
+ msgpack (1.5.2)
121
121
  nio4r (2.5.8)
122
- nokogiri (1.13.4)
122
+ nokogiri (1.13.6)
123
123
  mini_portile2 (~> 2.8.0)
124
124
  racc (~> 1.4)
125
125
  pry (0.14.1)
@@ -149,7 +149,7 @@ GEM
149
149
  rails-dom-testing (2.0.3)
150
150
  activesupport (>= 4.2.0)
151
151
  nokogiri (>= 1.6)
152
- rails-html-sanitizer (1.4.2)
152
+ rails-html-sanitizer (1.4.3)
153
153
  loofah (~> 2.3)
154
154
  railties (6.0.4.1)
155
155
  actionpack (= 6.0.4.1)
@@ -189,7 +189,7 @@ GEM
189
189
  actionpack (>= 5.2)
190
190
  activesupport (>= 5.2)
191
191
  sprockets (>= 3.0.0)
192
- sqlite3 (1.4.2)
192
+ sqlite3 (1.4.4)
193
193
  thor (1.2.1)
194
194
  thread_safe (0.3.6)
195
195
  tzinfo (1.2.9)
@@ -12,7 +12,7 @@ module SoberSwag
12
12
  .mapped do |v|
13
13
  BigDecimal(v)
14
14
  rescue ArgumentError
15
- Report::Value.new('was not a decimal')
15
+ Report::Value.new(['was not a decimal'])
16
16
  end).described(<<~MARKDOWN).referenced('SoberSwag.Converting.Decimal')
17
17
  Decimal formatted input.
18
18
  Will either convert a JSON number to a decimal, or accept a string representation.
@@ -27,6 +27,8 @@ module SoberSwag
27
27
  self | Null.new
28
28
  end
29
29
 
30
+ alias nilable optional
31
+
30
32
  ##
31
33
  # A list of this input.
32
34
  #
@@ -35,6 +37,8 @@ module SoberSwag
35
37
  List.new(self)
36
38
  end
37
39
 
40
+ alias array list
41
+
38
42
  ##
39
43
  # Constrained values: must be in range.
40
44
  # @return [InRange]
@@ -6,6 +6,12 @@ module SoberSwag
6
6
  #
7
7
  # Called List to avoid name conflicts.
8
8
  class List < Base
9
+ ##
10
+ # @see #new
11
+ def self.of(element)
12
+ initialize(element)
13
+ end
14
+
9
15
  ##
10
16
  # @param element [Base] the parser for elements
11
17
  def initialize(element)
@@ -10,6 +10,13 @@ module SoberSwag
10
10
  input
11
11
  end
12
12
 
13
+ ##
14
+ # @param other [Integer] number to specify this is a multiple of
15
+ # @return [SoberSwag::Reporting::Input::MultipleOf]
16
+ def multiple_of(other)
17
+ MultipleOf.new(self, other)
18
+ end
19
+
13
20
  def swagger_schema
14
21
  [{ type: 'number' }, {}]
15
22
  end
@@ -76,7 +76,7 @@ module SoberSwag
76
76
  #
77
77
  def add_attribute!(name, input, required:, description: nil)
78
78
  raise ArgumentError, 'name must be a symbol' unless name.is_a?(Symbol)
79
- raise ArgumentError, 'input type must be a SoberSwag::Reporting::Input::Interface' unless input.is_a?(Interface)
79
+ raise ArgumentError, "input type must be a SoberSwag::Reporting::Input::Interface (at key #{name.inspect})" unless input.is_a?(Interface)
80
80
 
81
81
  define_attribute(name) # defines an instance method to access this attribute
82
82
 
@@ -53,6 +53,8 @@ module SoberSwag
53
53
  List.new(self)
54
54
  end
55
55
 
56
+ alias array list
57
+
56
58
  ##
57
59
  # Partition this serializer into two potentials.
58
60
  # If the block given returns *false*, we will use `other` as the serializer.
@@ -85,10 +87,6 @@ module SoberSwag
85
87
  )
86
88
  end
87
89
 
88
- def array
89
- List.new(self)
90
- end
91
-
92
90
  def described(description)
93
91
  Described.new(self, description)
94
92
  end
@@ -20,7 +20,7 @@ module SoberSwag
20
20
  #
21
21
  # You can access other methods from this method.
22
22
  def field(name, output, description: nil, &extract)
23
- raise ArgumentError, "output of field #{name} is not a SoberSwag::Reporting::Output::Interface" unless output.is_a?(Interface)
23
+ raise ArgumentError, bad_field_message(name, output) unless output.is_a?(Interface)
24
24
 
25
25
  define_field(name, extract)
26
26
 
@@ -236,6 +236,14 @@ module SoberSwag
236
236
 
237
237
  private
238
238
 
239
+ def bad_field_message(name, field_type)
240
+ [
241
+ "Output type used for field #{name.inspect} was",
242
+ "#{field_type.inspect}, which is not an instance of",
243
+ SoberSwag::Reporting::Output::Interface.name
244
+ ].join(' ')
245
+ end
246
+
239
247
  def define_view_with_parent(name, parent, block)
240
248
  raise ArgumentError, "duplicate view #{name}" if name == :base || views.include?(name)
241
249
 
@@ -8,6 +8,10 @@ module SoberSwag
8
8
  @report = report
9
9
  end
10
10
 
11
+ def message
12
+ "Reported errors: #{report.full_errors.join(', ')}"
13
+ end
14
+
11
15
  attr_reader :report
12
16
  end
13
17
  end
@@ -8,6 +8,8 @@ module SoberSwag
8
8
  ##
9
9
  # @param problems [Array<String>] problems with it
10
10
  def initialize(problems)
11
+ raise ArgumentError, "#{problems} was not an enum" unless problems.respond_to?(:each)
12
+
11
13
  @problems = problems
12
14
  end
13
15
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SoberSwag
4
- VERSION = '0.24.0'
4
+ VERSION = '0.25.1'
5
5
  end
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.24.0
4
+ version: 0.25.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anthony Super
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-13 00:00:00.000000000 Z
11
+ date: 2022-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport