media_types 0.6.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/debian.yml +42 -0
  3. data/.github/workflows/ruby.yml +22 -0
  4. data/.gitignore +10 -10
  5. data/CHANGELOG.md +76 -41
  6. data/Gemfile +6 -6
  7. data/Gemfile.lock +17 -83
  8. data/LICENSE +21 -0
  9. data/README.md +364 -91
  10. data/Rakefile +12 -12
  11. data/lib/media_types.rb +58 -2
  12. data/lib/media_types/constructable.rb +36 -10
  13. data/lib/media_types/dsl.rb +110 -29
  14. data/lib/media_types/dsl/errors.rb +18 -0
  15. data/lib/media_types/errors.rb +19 -0
  16. data/lib/media_types/scheme.rb +153 -2
  17. data/lib/media_types/scheme/errors.rb +66 -0
  18. data/lib/media_types/scheme/links.rb +15 -0
  19. data/lib/media_types/scheme/missing_validation.rb +12 -4
  20. data/lib/media_types/scheme/output_empty_guard.rb +5 -4
  21. data/lib/media_types/scheme/output_iterator_with_predicate.rb +13 -2
  22. data/lib/media_types/scheme/output_type_guard.rb +1 -1
  23. data/lib/media_types/scheme/rules.rb +53 -1
  24. data/lib/media_types/scheme/rules_exhausted_guard.rb +15 -4
  25. data/lib/media_types/scheme/validation_options.rb +17 -5
  26. data/lib/media_types/testing/assertions.rb +20 -0
  27. data/lib/media_types/validations.rb +15 -5
  28. data/lib/media_types/version.rb +1 -1
  29. data/media_types.gemspec +4 -7
  30. metadata +20 -62
  31. data/.travis.yml +0 -19
  32. data/lib/media_types/defaults.rb +0 -31
  33. data/lib/media_types/integrations.rb +0 -32
  34. data/lib/media_types/integrations/actionpack.rb +0 -21
  35. data/lib/media_types/integrations/http.rb +0 -47
  36. data/lib/media_types/minitest/assert_media_type_format.rb +0 -10
  37. data/lib/media_types/minitest/assert_media_types_registered.rb +0 -166
  38. data/lib/media_types/registrar.rb +0 -148
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # MediaTypes
2
- [![Build Status](https://travis-ci.com/SleeplessByte/media-types-ruby.svg?branch=master)](https://travis-ci.com/SleeplessByte/media-types-ruby)
2
+ [![Build Status](https://github.com/SleeplessByte/media-types-ruby/workflows/Ruby/badge.svg?branch=master)](https://github.com/SleeplessByte/media-types-ruby/actions?query=workflow%3ARuby)
3
3
  [![Gem Version](https://badge.fury.io/rb/media_types.svg)](https://badge.fury.io/rb/media_types)
4
4
  [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/6f2dc1fb37ecb98c4363/maintainability)](https://codeclimate.com/github/SleeplessByte/media-types-ruby/maintainability)
6
6
 
7
- Media Types based on scheme, with versioning, views, suffixes and validations. Integrations available for [Rails](https://github.com/rails/rails) / ActionPack and [http.rb](https://github.com/httprb/http).
7
+ Media Types based on scheme, with versioning, views, suffixes and validations.
8
+
9
+ This library makes it easy to define schemas that can be used to validate JSON objects based on their Content-Type.
8
10
 
9
11
  ## Installation
10
12
 
@@ -24,15 +26,33 @@ Or install it yourself as:
24
26
 
25
27
  ## Usage
26
28
 
27
- By default there are no media types registered or defined, except for an abstract base type.
29
+ Define a validation:
30
+
31
+ ```ruby
32
+ require 'media_types'
33
+
34
+ module Acme
35
+ MediaTypes::set_organisation Acme, 'acme'
36
+
37
+ class FooValidator
38
+ include MediaTypes::Dsl
28
39
 
29
- ## Definition
30
- You can define media types by inheriting from this base type, or create your own base type with a class method `.base_format` that is used to create the final media type string by injecting formatted parameters:
40
+ use_name 'foo'
41
+
42
+ validations do
43
+ attribute :foo, String
44
+ end
45
+ end
46
+ end
47
+ ```
31
48
 
32
- - `%<type>s`: the type `media_type` received
33
- - `%<version>s`: the version, defaults to `:current_version`
34
- - `%<view>s`: the view, defaults to <empty>
35
- - `%<suffix>s`: the suffix
49
+ Validate an object:
50
+
51
+ ```ruby
52
+ Acme::FooValidator.validate!({ foo: 'bar' })
53
+ ```
54
+
55
+ ## Full example
36
56
 
37
57
  ```Ruby
38
58
  require 'media_types'
@@ -40,22 +60,24 @@ require 'media_types'
40
60
  class Venue
41
61
  include MediaTypes::Dsl
42
62
 
43
- def self.base_format
44
- 'application/vnd.mydomain.%<type>s.v%<version>s.%<view>s+%<suffix>s'
63
+ def self.organisation
64
+ 'mydomain'
45
65
  end
46
66
 
47
- media_type 'venue', defaults: { suffix: :json, version: 2 }
67
+ use_name 'venue'
48
68
 
49
69
  validations do
50
- attribute :name, String
51
- collection :location do
52
- attribute :latitude, Numeric
53
- attribute :longitude, Numeric
54
- attribute :altitude, AllowNil(Numeric)
55
- end
70
+ version 2 do
71
+ attribute :name, String
72
+ collection :location do
73
+ attribute :latitude, Numeric
74
+ attribute :longitude, Numeric
75
+ attribute :altitude, AllowNil(Numeric)
76
+ end
56
77
 
57
- link :self
58
- link :route, allow_nil: true
78
+ link :self
79
+ link :route, allow_nil: true
80
+ end
59
81
 
60
82
  version 1 do
61
83
  attribute :name, String
@@ -81,35 +103,24 @@ class Venue
81
103
  end
82
104
  end
83
105
  end
84
-
85
- registrations :venue_json do
86
- view 'create', :create_venue
87
- view 'index', :venue_urls
88
- view 'collection', :venue_collection
89
-
90
- versions [1,2]
91
-
92
- suffix :json
93
- suffix :xml
94
- end
95
106
  end
96
107
  ```
97
108
 
98
109
  ## Schema Definitions
99
110
 
100
- If you define a scheme using `current_scheme { }`, you may use any of the following dsl:
111
+ If you include 'MediaTypes::Dsl' in your class you can use the following functions within a `validation do` block to define your schema:
101
112
 
102
113
  ### `attribute`
103
114
 
104
115
  Adds an attribute to the schema, if a +block+ is given, uses that to test against instead of +type+
105
116
 
106
- | param | type | description |
107
- |-------|------|-------------|
108
- | key | `Symbol` | the attribute name |
109
- | opts | `Hash` | options to pass to `Scheme` or `Attribute` |
110
- | type | `Class`, `===`, Scheme | The type of the value, can be anything that responds to `===`, or scheme to use if no `&block` is given. Defaults to `Object` without a `&block` and to Hash with a `&block`. |
111
- | optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false` |
112
- | &block | `Block` | defines the scheme of the value of this attribute |
117
+ | param | type | description |
118
+ | --------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
119
+ | key | `Symbol` | the attribute name |
120
+ | opts | `Hash` | options to pass to `Scheme` or `Attribute` |
121
+ | type | `Class`, `===`, Scheme | The type of the value can be anything that responds to `===`, or scheme to use if no `&block` is given. Defaults to `Object` without a `&block` and to Hash with a `&block`. |
122
+ | optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false` |
123
+ | &block | `Block` | defines the scheme of the value of this attribute |
113
124
 
114
125
  #### Add an attribute named foo, expecting a string
115
126
  ```Ruby
@@ -147,12 +158,12 @@ MyMedia.valid?({ foo: { bar: 'my-string' }})
147
158
  ### `any`
148
159
  Allow for any key. The `&block` defines the Schema for each value.
149
160
 
150
- | param | type | description |
151
- |-------|------|-------------|
152
- | scheme | `Scheme`, `NilClass` | scheme to use if no `&block` is given |
153
- | allow_empty: | `TrueClass`, `FalsClass` | if true, empty (no key/value present) is allowed |
154
- | expected_type: | `Class`, | forces the validated value to have this type, defaults to `Hash`. Use `Object` if either `Hash` or `Array` is fine |
155
- | &block | `Block` | defines the scheme of the value of this attribute |
161
+ | param | type | description |
162
+ | -------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
163
+ | scheme | `Scheme`, `NilClass` | scheme to use if no `&block` is given |
164
+ | allow_empty: | `TrueClass`, `FalsClass` | if true, empty (no key/value present) is allowed |
165
+ | expected_type: | `Class`, | forces the validated value to have this type, defaults to `Hash`. Use `Object` if either `Hash` or `Array` is fine |
166
+ | &block | `Block` | defines the scheme of the value of this attribute |
156
167
 
157
168
  #### Add a collection named foo, expecting any key with a defined value
158
169
  ```Ruby
@@ -196,14 +207,14 @@ MyMedia.valid?({ foo: [{ required: 'test', bar: 42 }] })
196
207
  ### `collection`
197
208
  Expect a collection such as an array or hash. The `&block` defines the Schema for each item in that collection.
198
209
 
199
- | param | type | description |
200
- |-------|------|-------------|
201
- | key | `Symbol` | key of the collection (same as `#attribute`) |
202
- | scheme | `Scheme`, `NilClass`, `Class` | scheme to use if no `&block` is given or `Class` of each item in the |
203
- | allow_empty: | `TrueClass`, `FalseClass` | if true, empty (no key/value present) is allowed |
204
- | expected_type: | `Class`, | forces the validated value to have this type, defaults to `Array`. Use `Object` if either `Array` or `Hash` is fine. |
205
- | optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false` |
206
- | &block | `Block` | defines the scheme of the value of this attribute |
210
+ | param | type | description |
211
+ | -------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------- |
212
+ | key | `Symbol` | key of the collection (same as `#attribute`) |
213
+ | scheme | `Scheme`, `NilClass`, `Class` | scheme to use if no `&block` is given or `Class` of each item in the collection |
214
+ | allow_empty: | `TrueClass`, `FalseClass` | if true, empty (no key/value present) is allowed |
215
+ | expected_type: | `Class`, | forces the validated value to have this type, defaults to `Array`. Use `Object` if either `Array` or `Hash` is fine. |
216
+ | optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false` |
217
+ | &block | `Block` | defines the scheme of the value of this attribute |
207
218
 
208
219
 
209
220
  #### Collection with an array of string
@@ -242,12 +253,12 @@ MyMedia.valid?({ foo: [{ required: 'test', number: 42 }, { required: 'other', nu
242
253
 
243
254
  Expect a link with a required `href: String` attribute
244
255
 
245
- | param | type | description |
246
- |-------|------|-------------|
247
- | key | `Symbol` | key of the link (same as `#attribute`) |
248
- | allow_nil: | `TrueClass`, `FalseClass` | if true, value may be nil |
249
- | optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false` |
250
- | &block | `Block` | defines the scheme of the value of this attribute, in addition to the `href` attribute |
256
+ | param | type | description |
257
+ | ---------- | ------------------------- | -------------------------------------------------------------------------------------- |
258
+ | key | `Symbol` | key of the link (same as `#attribute`) |
259
+ | allow_nil: | `TrueClass`, `FalseClass` | if true, value may be nil |
260
+ | optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false` |
261
+ | &block | `Block` | defines the scheme of the value of this attribute, in addition to the `href` attribute |
251
262
 
252
263
  #### Links as defined in HAL, JSON-Links and other specs
253
264
  ```Ruby
@@ -255,7 +266,7 @@ class MyMedia
255
266
  include MediaTypes::Dsl
256
267
 
257
268
  validations do
258
- link :_self
269
+ link :self
259
270
  link :image
260
271
  end
261
272
  end
@@ -315,69 +326,331 @@ expected_object.valid?([{ foo: 'string' }])
315
326
  ```
316
327
 
317
328
  ## Formatting for headers
318
- Any media type object can be coerced in valid string to be used with `Content-Type` or `Accept`:
329
+ Any media type object can be converted in valid string to be used with `Content-Type` or `Accept`:
319
330
 
320
331
  ```Ruby
321
- Venue.mime_type.to_s
332
+ Venue.mime_type.identifier
322
333
  # => "application/vnd.mydomain.venue.v2+json"
323
334
 
324
- Venue.mime_type.version(1).to_s
335
+ Venue.mime_type.version(1).identifier
325
336
  # => "application/vnd.mydomain.venue.v1+json"
326
337
 
327
- Venue.mime_type.version(1).suffix(:xml).to_s
328
- # => "application/vnd.mydomain.venue.v1+xml"
329
-
330
338
  Venue.mime_type.to_s(0.2)
331
339
  # => "application/vnd.mydomain.venue.v2+json; q=0.2"
332
340
 
333
- Venue.mime_type.collection.to_s
341
+ Venue.mime_type.collection.identifier
334
342
  # => "application/vnd.mydomain.venue.v2.collection+json"
335
343
 
336
- Venue.mime_type.view('active').to_s
344
+ Venue.mime_type.view('active').identifier
337
345
  # => "application/vnd.mydomain.venue.v2.active+json"
338
346
  ```
339
347
 
340
- ## Integrations
341
- The integrations are not loaded by default, so you need to require them:
348
+ ## API
349
+
350
+ A defined schema has the following functions available:
351
+
352
+ ### `valid?`
353
+
354
+ Example: `Venue.valid?({ foo: 'bar' })`
355
+
356
+ Allows passing in validation options as a second parameter.
357
+
358
+ ### `validate!`
359
+
360
+ Example: `Venue.validate!({ foo: 'bar' })`
361
+
362
+ Allows passing in validation options as a second parameter.
363
+
364
+ ### `validatable?`
365
+
366
+ Example: `Venue.version(42).validatable?`
367
+
368
+ Tests whether the current configuration of the schema has a validation defined.
369
+
370
+ ### `register`
371
+
372
+ Example: `Venue.register`
373
+
374
+ Registers the media type to the registry.
375
+
376
+ ### `view`
377
+
378
+ Example: `Venue.view('create')`
379
+
380
+ Returns a schema validator configured with the specified view.
381
+
382
+ ### `version`
383
+
384
+ Example: `Venue.version(42)`
385
+
386
+ Returns a schema validator configured with the specified version.
387
+
388
+ ### `suffix`
389
+
390
+ Example: `Venue.suffix(:json)`
391
+
392
+ Returns a schema validator configured with the specified suffix.
393
+
394
+ ### `identifier`
395
+
396
+ Example: `Venue.version(2).identifier` (returns `'application/vnd.application.venue.v2'`)
397
+
398
+ Returns the IANA compatible [Media Type Identifier](https://en.wikipedia.org/wiki/Media_type) for the configured schema.
399
+
400
+ ### `available_validations`
401
+
402
+ Example: `Venue.available_validations`
403
+
404
+ Returns a list of all the schemas that are defined.
405
+
406
+ ## Ensuring Your MediaTypes Work
407
+
408
+ ### Overview & Rationale
409
+
410
+ If the MediaTypes you create enforce a specification you _do not expect them to_, it will cause problems that will be very difficult to fix, as other code, which utilises your MediaType, would break when you change the specification. This is because the faulty MediaType definition will start to make other code dependent on the specification it defines. For example, consider what would happen if you release a MediaType which defines an attribute `foo` to be a `String`, and run a server which defines such a specification. Later, you realise you _actually_ wanted `foo` to be `Numeric`. What can you do?
411
+
412
+ Well, during this time, other people started to write code which conformed to the specification defined by the faulty MediaType. So, it's going to be extremely difficult to revert your mistake. For this reason, it is vital that, when using this library, your MediaTypes define the _correct_ specification.
413
+
414
+ To this end, we provide you with a few avenues to check whether MediaTypes define the specifications you actually intend by checking examples of JSON you expect to be compliant/non-compliant with the MediaType definitions you write out.
415
+
416
+ These are as follows:
417
+
418
+ 1. The library provides [two methods](README.md#media-type-checking-in-test-suites) (`assert_pass` and `assert_fail`), which allow specifying JSON fixtures that are compliant (`assert_pass`) or non-compliant (`assert_fail`).
419
+ 2. The library provides a way to validate those fixtures against the MediaType specification with the [`assert_mediatype`](README.md#media-type-checking-in-test-suites) method.
420
+ 3. The library automatically performs a MediaType's checks defined by (1) the first time an object is validated against the MediaType, and throws an error if any of the checks fail.
421
+ 4. The library provides a way to run the checks carried out by (3) on load, using the method [`assert_sane!`](README.md#validation-checks) so that an application will not run if any of the MediaType's checks don't pass.
422
+
423
+ These four options are examined in detail below.
424
+
425
+ ### MediaType Checking in Test Suites
426
+
427
+ The library provides the `assert_mediatype` method, which allows running the checks for a particular `MediaType` within Minitest with `assert_pass` and `assert_fail`.
428
+ If you are using Minitest, you can make `assert_mediatype` available by calling `include MediaTypes::Testing::Assertions` in the test class (e.g. `Minitest::Runnable`):
429
+
430
+ ```ruby
431
+ module Minitest
432
+ class Test < Minitest::Runnable
433
+ include MediaTypes::Testing::Assertions
434
+ end
435
+ end
436
+ ```
437
+
438
+ The example below demonstrates how to use `assert_pass` and `assert_fail` within a MediaType, and how to use the `assert_mediatype` method in MiniTest tests to validate them.
439
+
440
+ ```ruby
441
+ class MyMedia
442
+ include MediaTypes::Dsl
443
+
444
+ def self.organisation
445
+ 'acme'
446
+ end
447
+
448
+ use_name 'test'
449
+
450
+ validations do
451
+ # Using "any Numeric" this MediaType doesn't care what key names you use.
452
+ # However, it does care that those keys point to a Numeric value.
453
+ any Numeric
454
+
455
+ assert_pass '{"foo": 42}'
456
+ assert_pass <<-FIXTURE
457
+ { "foo": 42, "bar": 43 }
458
+ FIXTURE
459
+
460
+ # The keyword "any" means there are no required keys, so having no keys should also pass.
461
+ assert_pass '{}'
462
+
463
+ # This MediaType should not accept anything other then a Numeric value.
464
+ assert_fail <<-FIXTURE
465
+ { "foo": { "bar": "string" } }
466
+ FIXTURE
467
+ assert_fail '{"foo": {}}'
468
+ assert_fail '{"foo": null}'
469
+ assert_fail '{"foo": [42]}'
470
+ end
471
+ end
472
+
473
+ class MyMediaTest < Minitest::Test
474
+ include MediaTypes::Testing::Assertions
475
+
476
+ def test_mediatype_specification
477
+ assert_mediatype MyMedia
478
+ end
479
+ end
480
+
481
+ class MyMediaTest < Minitest::Test
482
+ include MediaTypes::Testing::Assertions
483
+
484
+ def test_mediatype_specification
485
+ assert_mediatype MyMedia
486
+ end
487
+ end
488
+
489
+ ```
490
+
491
+ ### Testing Without Minitest
492
+
493
+ If you are using another testing framework, you will not be able to use the `assert_mediatype` method. Instead, you can test your MediaTypes by using the `assert_sane!` method (documented below) and rescuing the errors it will throw when it fails. The snippet below shows an example adaptation for MiniTest, which you can use as a guide.
494
+
342
495
  ```ruby
343
- # For Rails / ActionPack
344
- require 'media_types/integrations/actionpack'
496
+ def test_mediatype(mediatype)
497
+ mediatype.assert_sane!
498
+ assert mediatype.media_type_validations.scheme.asserted_sane?
499
+ rescue MediaTypes::AssertionError => e
500
+ flunk e.message
501
+ end
502
+ end
503
+ ```
504
+
505
+ ### Validation Checks
345
506
 
346
- # For HTTP.rb
347
- require 'media_types/integrations/http'
507
+ The `assert_pass` and `assert_fail` methods take a JSON string (as shown below). The first time the `validate!` method is called on a MediaType, the assertions for that media type are run.
508
+ This is done as a last line of defence against introducing faulty MediaTypes into your software. Ideally, you want to carry out these checks on load rather than on a running application. This functionality is provided by the `assert_sane!` method, which can be called on a particular MediaType:
509
+
510
+ ```ruby
511
+ MyMedia.assert_sane!
512
+ # true
348
513
  ```
349
514
 
350
- Define a `registrations` block on your media type, indicating the symbol for the base type (`registrations :symbol do`) and inside use the registrations dsl to define which media types to register. `versions array_of_numbers` determines which versions, `suffix name` adds a suffix, `type_alias name` adds an alias and `view name, symbol` adds a view.
515
+ ### Intermediate Checks
351
516
 
352
- ```Ruby
353
- Venue.register
517
+ The fixtures provided to the `assert_pass` and `assert_fail` methods are evaluated within the context of the block they are placed in. It's therefore possible to write a test for a (complex) optional attribute, without that test cluttering the fixtures for the entire mediatype.
518
+
519
+ ```ruby
520
+ class MyMedia
521
+ include MediaTypes::Dsl
522
+
523
+ expect_string_keys
524
+
525
+ def self.organisation
526
+ 'acme'
527
+ end
528
+
529
+ use_name 'test'
530
+
531
+ validations do
532
+ attribute :foo, Hash, optional: true do
533
+ attribute :bar, Numeric
534
+
535
+ # This passes, since in this context the "bar" key is required to have a Numeric value.
536
+ assert_pass '{"bar": 42}'
537
+ end
538
+ attribute :rep, Numeric
539
+
540
+ # This passes, since the attribute "foo" is optional.
541
+ assert_pass '{"rep": 42}'
542
+ end
543
+ end
544
+ ```
545
+
546
+ ## Key Type Validation
547
+
548
+ When interacting with Ruby objects defined by your MediaType, you want to avoid getting `nil` values, just because the the wrong key type is being used (e.g. `obj['foo']` instead of `obj[:foo]`).
549
+ To this end, the library provides the ability to specify the expected type of keys in a MediaType; by default symbol keys are expected.
550
+
551
+ ### Setting Key Type Expectations
552
+
553
+ Key type expectations can be set at the module level. Each MediaType within this module will inherit the expectation set by that module.
554
+
555
+ ```ruby
556
+ module Acme
557
+ MediaTypes.expect_string_keys(self)
558
+
559
+ # The MyMedia class expects string keys, as inherited from the Acme module.
560
+ class MyMedia
561
+ include MediaTypes::Dsl
562
+
563
+ def self.organisation
564
+ 'acme'
565
+ end
566
+
567
+ use_name 'test'
568
+
569
+ validations do
570
+ any Numeric
571
+ end
572
+ end
573
+ end
574
+ ```
575
+
576
+ If you validate an object with a different key type than expected, an error will be thrown:
577
+
578
+ ```ruby
579
+ Acme::MyMedia.validate! { "something": 42 }
580
+ # => passes, because all keys are a string
581
+
582
+ Acme::MyMedia.validate! { something: 42 }
583
+ # => throws a ValidationError , because 'something' is a symbol key
354
584
  ```
355
585
 
356
- ### Rails
357
- Load the `actionpack` integration and call `.register` on all the media types you want to be available in Rails. You can do this in the `mime_types` initializer, or anywhere before your controllers are instantiated. Yes, the symbol (by default `<type>_v<version>_<suffix>`) can now be used in your `format` blocks, or as extension in the url.
586
+ ## Overriding Key Type Expectations
358
587
 
359
- Rails only has a default serializer for `application/json`, and content with your `+json` media types (or different once) will not be deserialized by default. A way to overcome this is to set the JSON parameter parser for all new symbols. `.register` gives you back an array of `Registerable` objects that responds to `#to_sym` to get that symbol.
588
+ A key type expectation set by a Module can be overridden by calling either `expect_symbol_keys` or `expect_string_keys` inside the MediaType class.
360
589
 
361
590
  ```ruby
362
- symbols = Venue.register.map(&:to_sym)
591
+ module Acme
592
+ MediaTypes.expect_string_keys(self)
593
+
594
+ class MyOverridingMedia
595
+ include MediaTypes::Dsl
596
+
597
+ def self.organisation
598
+ 'acme'
599
+ end
363
600
 
364
- original_parsers = ActionDispatch::Request.parameter_parsers
365
- new_parser = original_parsers[Mime[:json].symbol]
366
- new_parsers = original_parsers.merge(Hash[*symbols.map { |s| [s, new_parser] }])
367
- ActionDispatch::Request.parameter_parsers = new_parsers
601
+ use_name 'test'
602
+
603
+ # Expect keys to be symbols
604
+ expect_symbol_keys
605
+
606
+ validations do
607
+ any Numeric
608
+ end
609
+ end
610
+ end
611
+ ```
612
+
613
+ Now the MediaType throws an error when string keys are used.
614
+
615
+ ```ruby
616
+ Acme::MyOverridingMedia.validate! { something: 42 }
617
+ # => passes, because all keys are a symbol
618
+
619
+ Acme::MyOverridingMedia.validate! { "something": 42 }
620
+ # => throws a ValidationError , because 'something' is a string key
368
621
  ```
369
622
 
370
- If you want to validate the content-type and not have your errors be `Rack::Error` but be handled by your controllers, leave this out and add a `before_action` to your controller that deserializes + validates for you.
623
+ ### Setting The JSON Parser With The Wrong Key Type
371
624
 
372
- ### HTTP.rb
373
- Load the `http` integration and call `.register` on all media types you want to be able to serialize and deserialize. The media type validations will run both before serialization and after deserialization.
625
+ If you parse JSON with the wrong key type, as shown below, the resultant object will fail the validations.
374
626
 
375
- Currently uses `oj` under the hood and this can not be changed.
627
+ ```ruby
628
+ class MyMedia
629
+ include MediaTypes::Dsl
630
+
631
+ def self.organisation
632
+ 'acme'
633
+ end
634
+
635
+ use_name 'test'
636
+
637
+ # Expect keys to be symbols
638
+ expect_symbol_keys
639
+
640
+ validations do
641
+ any Numeric
642
+ end
643
+ end
644
+
645
+ json = JSON.parse('{"foo": {}}', { symbolize_names: false })
646
+ # If MyMedia expects symbol keys
647
+ MyMedia.valid?(json)
648
+ # Returns false
649
+ ```
376
650
 
377
651
  ## Related
378
652
 
379
- - [`MediaTypes::Serialization`](https://github.com/XPBytes/media_types-serialization): :cyclone: Add media types supported serialization using your favourite serializer
380
- - [`MediaTypes::Validation`](https://github.com/XPBytes/media_types-validation): :heavy_exclamation_mark: Response validations according to a media-type
653
+ - [`MediaTypes::Serialization`](https://github.com/XPBytes/media_types-serialization): :cyclone: Add media types supported serialization to Rails.
381
654
 
382
655
  ## Development
383
656