{"id":17472465,"url":"https://github.com/sleeplessbyte/media-types-ruby","last_synced_at":"2025-09-22T04:31:40.847Z","repository":{"id":37677914,"uuid":"150477364","full_name":"SleeplessByte/media-types-ruby","owner":"SleeplessByte","description":":gem: Library to create media type definitions, schemes and validations","archived":false,"fork":false,"pushed_at":"2025-07-01T22:43:59.000Z","size":454,"stargazers_count":3,"open_issues_count":9,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-28T04:29:22.095Z","etag":null,"topics":["http","media-type","media-types","mime-types"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/media_types","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SleeplessByte.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-26T19:12:03.000Z","updated_at":"2025-07-01T22:44:02.000Z","dependencies_parsed_at":"2024-10-23T18:23:43.898Z","dependency_job_id":"7445f87c-9285-4c5c-9286-4669d2f57525","html_url":"https://github.com/SleeplessByte/media-types-ruby","commit_stats":{"total_commits":401,"total_committers":9,"mean_commits":44.55555555555556,"dds":0.7556109725685786,"last_synced_commit":"77c64860a2c5c1af2e3a26dad10c0826ca6297e2"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/SleeplessByte/media-types-ruby","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessByte%2Fmedia-types-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessByte%2Fmedia-types-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessByte%2Fmedia-types-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessByte%2Fmedia-types-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SleeplessByte","download_url":"https://codeload.github.com/SleeplessByte/media-types-ruby/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessByte%2Fmedia-types-ruby/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276346735,"owners_count":25626500,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-22T02:00:08.972Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","media-type","media-types","mime-types"],"created_at":"2024-10-18T17:18:13.015Z","updated_at":"2025-09-22T04:31:40.423Z","avatar_url":"https://github.com/SleeplessByte.png","language":"Ruby","readme":"# MediaTypes\n[![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)\n[![Gem Version](https://badge.fury.io/rb/media_types.svg)](https://badge.fury.io/rb/media_types)\n[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) \n[![Maintainability](https://api.codeclimate.com/v1/badges/6f2dc1fb37ecb98c4363/maintainability)](https://codeclimate.com/github/SleeplessByte/media-types-ruby/maintainability)\n\nMedia Types based on  scheme, with versioning, views, suffixes and validations.\n\nThis library makes it easy to define schemas that can be used to validate JSON objects based on their Content-Type.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'media_types'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install media_types\n\n## Usage\n\nDefine a validation:\n\n```ruby\nrequire 'media_types'\n\nmodule Acme\n  MediaTypes::set_organisation Acme, 'acme'\n\n  class FooValidator\n    include MediaTypes::Dsl\n\n    use_name 'foo'\n\n    validations do\n      attribute :foo, String\n    end\n  end\nend\n```\n\nValidate an object:\n\n```ruby\nAcme::FooValidator.validate!({ foo: 'bar' })\n```\n\n## Full example\n\n```Ruby\nrequire 'media_types'\n\nclass Venue\n  include MediaTypes::Dsl\n  \n  def self.organisation\n    'mydomain'\n  end\n  \n  use_name 'venue'\n\n  validations do\n    version 2 do\n      attribute :name, String\n      collection :location do\n        attribute :latitude, Numeric\n        attribute :longitude, Numeric\n        attribute :altitude, AllowNil(Numeric)\n      end\n\n      link :self\n      link :route, allow_nil: true\n    end\n    \n    version 1 do\n      attribute :name, String\n      attribute :coords, String, optional: :loose\n      attribute :updated_at, String\n    \n      link :self\n    end\n    \n    view 'create' do\n      collection :location do\n        attribute :latitude, Numeric\n        attribute :longitude, Numeric\n        attribute :altitude, AllowNil(Numeric)\n      end\n      \n      versions [1, 2] do |v|\n        collection :location do\n          link :extra if v \u003e 1\n          \n          attribute :latitude, Numeric\n          attribute :longitude, Numeric\n          attribute :altitude, AllowNil(Numeric)\n        end\n      end\n    end\n  end\nend\n```\n\n## Schema Definitions\n\nIf you include 'MediaTypes::Dsl' in your class you can use the following functions within a `validation do` block to define your schema:\n\n### `attribute`\n\nAdds an attribute to the schema, if a +block+ is given, uses that to test against instead of +type+\n\n| param     | type                      | description                                                                                                                                                                   |\n| --------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| key       | `Symbol`                  | the attribute name                                                                                                                                                            |\n| opts      | `Hash`                    | options to pass to `Scheme` or `Attribute`                                                                                                                                    |\n| type      | `Class`, `===`, Scheme    | The type of the value can be anything that responds to `===`,  or scheme to use if no `\u0026block` is given. Defaults to `Object` without a `\u0026block` and to Hash with a `\u0026block`. |\n| optional: | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false`                                                                                                                               |\n| \u0026block    | `Block`                   | defines the scheme of the value of this attribute                                                                                                                             |\n\n#### Add an attribute named foo, expecting a string\n```Ruby\nrequire 'media_types'\n\nclass MyMedia\n  include MediaTypes::Dsl\n\n  validations do\n    attribute :foo, String\n  end\nend\n\nMyMedia.valid?({ foo: 'my-string' })\n# =\u003e true\n```\n\n####  Add an attribute named foo, expecting nested scheme\n\n```Ruby\nclass MyMedia\n include MediaTypes::Dsl\n\n validations do\n   attribute :foo do\n     attribute :bar, String\n   end\n end\nend\n\nMyMedia.valid?({ foo: { bar: 'my-string' }})\n# =\u003e true\n```\n\n### `any`\nAllow for any key. The `\u0026block` defines the Schema for each value.\n\n| param          | type                     | description                                                                                                        |\n| -------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------ |\n| scheme         | `Scheme`, `NilClass`     | scheme to use if no `\u0026block` is given                                                                              |\n| allow_empty:   | `TrueClass`, `FalsClass` | if true, empty (no key/value present) is allowed                                                                   |\n| expected_type: | `Class`,                 | forces the validated value to have this type, defaults to `Hash`. Use `Object` if either `Hash` or `Array` is fine |\n| \u0026block         | `Block`                  | defines the scheme of the value of this attribute                                                                  |\n\n#### Add a collection named foo, expecting any key with a defined value\n```Ruby\nclass MyMedia\n include MediaTypes::Dsl\n\n validations do\n   collection :foo do\n     any do\n       attribute :bar, String\n     end\n   end\n end\nend\n\nMyMedia.valid?({ foo: [{ anything: { bar: 'my-string' }, other_thing: { bar: 'other-string' } }] })\n# =\u003e true\n```` \n\n### `not_strict`\nAllow for extra keys in the schema/collection even when passing `strict: true` to `#validate!`\n\n#### Allow for extra keys in collection\n\n```Ruby\nclass MyMedia\n include MediaTypes::Dsl\n\n validations do\n   collection :foo do\n     attribute :required, String\n     not_strict\n   end\n end\nend\n\nMyMedia.valid?({ foo: [{ required: 'test', bar: 42 }] })\n# =\u003e true\n``` \n  \n### `collection`\nExpect a collection such as an array or hash. The `\u0026block` defines the Schema for each item in that collection.\n\n| param          | type                          | description                                                                                                          |\n| -------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| key            | `Symbol`                      | key of the collection (same as `#attribute`)                                                                         |\n| scheme         | `Scheme`, `NilClass`, `Class` | scheme to use if no `\u0026block` is given or `Class` of each item in the collection                                      |\n| allow_empty:   | `TrueClass`, `FalseClass`     | if true, empty (no key/value present) is allowed                                                                     |\n| expected_type: | `Class`,                      | forces the validated value to have this type, defaults to `Array`. Use `Object` if either `Array` or `Hash` is fine. |\n| optional:      | `TrueClass`, `FalseClass`     | if true, key may be absent, defaults to `false`                                                                      |\n| \u0026block         | `Block`                       | defines the scheme of the value of this attribute                                                                    |\n\n\n#### Collection with an array of string\n```Ruby\nclass MyMedia\n include MediaTypes::Dsl\n\n validations do\n   collection :foo, String\n end\nend\n\nMyMedia.valid?({ collection: ['foo', 'bar'] })\n# =\u003e true\n```\n\n#### Collection with defined scheme\n\n```Ruby\nclass MyMedia\n include MediaTypes::Dsl\n\n validations do\n   collection :foo do\n     attribute :required, String\n     attribute :number, Numeric\n   end\n end\nend\n\nMyMedia.valid?({ foo: [{ required: 'test', number: 42 }, { required: 'other', number: 0 }] })\n# =\u003e true\n```\n\n### `link`\n\nExpect a link with a required `href: String` attribute\n\n| param      | type                      | description                                                                            |\n| ---------- | ------------------------- | -------------------------------------------------------------------------------------- |\n| key        | `Symbol`                  | key of the link (same as `#attribute`)                                                 |\n| allow_nil: | `TrueClass`, `FalseClass` | if true, value may be nil                                                              |\n| optional:  | `TrueClass`, `FalseClass` | if true, key may be absent, defaults to `false`                                        |\n| \u0026block     | `Block`                   | defines the scheme of the value of this attribute, in addition to the `href` attribute |\n\n#### Links as defined in HAL, JSON-Links and other specs\n```Ruby\nclass MyMedia\n  include MediaTypes::Dsl\n\n  validations do\n    link :self\n    link :image\n  end\nend\n\nMyMedia.valid?({ _links: { self: { href: 'https://example.org/s' }, image: { href: 'https://image.org/i' }} })\n# =\u003e true\n```\n\n#### Link with extra attributes\n```Ruby\nclass MyMedia\n include MediaTypes::Dsl\n\n validations do\n   link :image do\n     attribute :templated, TrueClass\n   end\n end\nend\n\nMyMedia.valid?({ _links: { image: { href: 'https://image.org/{md5}', templated: true }} })\n# =\u003e true\n```\n\n## Validation\nIf your type has a validations, you can now use this media type for validation:\n\n```Ruby\nVenue.valid?({\n  #...\n})\n# =\u003e true if valid, false otherwise\n\nVenue.validate!({\n  # /*...*/ \n})\n# =\u003e raises if it's not valid\n```\n\nIf an array is passed, check the scheme for each value, unless the scheme is defined as expecting a hash:\n```Ruby\nexpected_hash = Scheme.new(expected_type: Hash) { attribute(:foo) }\nexpected_object = Scheme.new { attribute(:foo) } \n\nexpected_hash.valid?({ foo: 'string' })\n# =\u003e true\n\nexpected_hash.valid?([{ foo: 'string' }])\n# =\u003e false\n\n\nexpected_object.valid?({ foo: 'string' })\n# =\u003e true\n\nexpected_object.valid?([{ foo: 'string' }])\n# =\u003e true\n```\n\n## Formatting for headers\nAny media type object can be converted in valid string to be used with `Content-Type` or `Accept`:\n\n```Ruby\nVenue.mime_type.identifier\n# =\u003e \"application/vnd.mydomain.venue.v2+json\"\n\nVenue.mime_type.version(1).identifier\n# =\u003e \"application/vnd.mydomain.venue.v1+json\"\n\nVenue.mime_type.to_s(0.2)\n# =\u003e \"application/vnd.mydomain.venue.v2+json; q=0.2\"\n\nVenue.mime_type.collection.identifier\n# =\u003e \"application/vnd.mydomain.venue.v2.collection+json\"\n\nVenue.mime_type.view('active').identifier\n# =\u003e \"application/vnd.mydomain.venue.v2.active+json\"\n```\n\n## API\n\nA defined schema has the following functions available:\n\n### `valid?`\n\nExample: `Venue.valid?({ foo: 'bar' })`\n\nAllows passing in validation options as a second parameter.\n\n### `validate!`\n\nExample: `Venue.validate!({ foo: 'bar' })`\n\nAllows passing in validation options as a second parameter.\n\n### `validatable?`\n\nExample: `Venue.version(42).validatable?`\n\nTests whether the current configuration of the schema has a validation defined.\n\n### `register`\n\nExample: `Venue.register`\n\nRegisters the media type to the registry.\n\n### `view`\n\nExample: `Venue.view('create')`\n\nReturns a schema validator configured with the specified view.\n\n### `version`\n\nExample: `Venue.version(42)`\n\nReturns a schema validator configured with the specified version.\n\n### `suffix`\n\nExample: `Venue.suffix(:json)`\n\nReturns a schema validator configured with the specified suffix.\n\n### `identifier`\n\nExample: `Venue.version(2).identifier` (returns `'application/vnd.application.venue.v2'`)\n\nReturns the IANA compatible [Media Type Identifier](https://en.wikipedia.org/wiki/Media_type) for the configured schema.\n\n### `available_validations`\n\nExample: `Venue.available_validations`\n\nReturns a list of all the schemas that are defined.\n\n## Ensuring Your MediaTypes Work\n\n### Overview \u0026 Rationale\n\nIf 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?\n\nWell, 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.\n\nTo 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.\n\nThese are as follows:\n\n1. 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`).\n2. 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.\n3. 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.\n4. 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.\n\nThese four options are examined in detail below.\n\n### MediaType Checking in Test Suites\n\nThe library provides the `assert_mediatype` method, which allows running the checks for a particular `MediaType` within Minitest with `assert_pass` and `assert_fail`.\nIf you are using Minitest, you can make `assert_mediatype` available by calling `include MediaTypes::Testing::Assertions` in the test class (e.g. `Minitest::Runnable`):\n\n```ruby\nmodule Minitest\n  class Test \u003c Minitest::Runnable\n    include MediaTypes::Testing::Assertions\n  end\nend\n```\n\nThe 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.\n\n```ruby\nclass MyMedia\n  include MediaTypes::Dsl\n\n  def self.organisation\n    'acme'\n  end\n\n  use_name 'test'\n\n  validations do\n    # Using \"any Numeric\" this MediaType doesn't care what key names you use.\n    # However, it does care that those keys point to a Numeric value.\n    any Numeric\n\n    assert_pass '{\"foo\": 42}'\n    assert_pass \u003c\u003c-FIXTURE\n    { \"foo\": 42, \"bar\": 43 }\n    FIXTURE\n\n    # The keyword \"any\" means there are no required keys, so having no keys should also pass.\n    assert_pass '{}'\n\n    # This MediaType should not accept anything other then a Numeric value.\n    assert_fail \u003c\u003c-FIXTURE\n    { \"foo\": { \"bar\": \"string\" } }\n    FIXTURE\n    assert_fail '{\"foo\": {}}'\n    assert_fail '{\"foo\": null}', loose: true\n    assert_fail '{\"foo\": [42]}', loose: false\n  end\nend\n\nclass MyMediaTest \u003c Minitest::Test\n  include MediaTypes::Testing::Assertions\n\n  def test_mediatype_specification\n    assert_mediatype MyMedia\n  end\nend\n\nclass MyMediaTest \u003c Minitest::Test\n  include MediaTypes::Testing::Assertions\n\n  def test_mediatype_specification\n    assert_mediatype MyMedia\n  end\nend\n\n```\n\n### Testing Without Minitest\n\nIf 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.\n\n```ruby\n def test_mediatype(mediatype)\n      mediatype.assert_sane!\n      assert mediatype.media_type_validations.scheme.asserted_sane?\n    rescue MediaTypes::AssertionError =\u003e e\n      flunk e.message\n    end\n  end\n```\n\n### Validation Checks\n\nThe `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.\nThis 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:\n\n```ruby\nMyMedia.assert_sane!\n# true\n```\n\n### Intermediate Checks\n\nThe 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.\n\n```ruby\nclass MyMedia\n  include MediaTypes::Dsl\n\n  expect_string_keys\n\n  def self.organisation\n    'acme'\n  end\n\n  use_name 'test'\n\n  validations do\n    attribute :foo, Hash, optional: true do\n      attribute :bar, Numeric\n\n      # This passes, since in this context the \"bar\" key is required to have a Numeric value. \n      assert_pass '{\"bar\": 42}'\n    end\n    attribute :rep, Numeric\n\n    # This passes, since the attribute \"foo\" is optional.\n    assert_pass '{\"rep\": 42}'\n  end\nend\n```\n\n## Key Type Validation\n\nWhen 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]`).\nTo this end, the library provides the ability to specify the expected type of keys in a MediaType; by default symbol keys are expected.\n\n### Setting Key Type Expectations\n\nKey type expectations can be set at the module level. Each MediaType within this module will inherit the expectation set by that module.\n\n```ruby\nmodule Acme\n  MediaTypes.expect_string_keys(self)\n\n  # The MyMedia class expects string keys, as inherited from the Acme module.\n  class MyMedia\n    include MediaTypes::Dsl\n\n    def self.organisation\n      'acme'\n    end\n\n    use_name 'test'\n\n    validations do\n      any Numeric\n    end\n  end\nend\n```\n\nIf you validate an object with a different key type than expected, an error will be thrown:\n\n```ruby\n  Acme::MyMedia.validate! { \"something\": 42 }\n  # =\u003e passes, because all keys are a string\n\n  Acme::MyMedia.validate! { something: 42 }\n  # =\u003e throws a ValidationError , because 'something' is a symbol key\n```\n\n## Overriding Key Type Expectations\n\nA key type expectation set by a Module can be overridden by calling either `expect_symbol_keys` or `expect_string_keys` inside the MediaType class.\n\n```ruby\nmodule Acme\n  MediaTypes.expect_string_keys(self)\n\n  class MyOverridingMedia\n    include MediaTypes::Dsl\n\n    def self.organisation\n      'acme'\n    end\n\n    use_name 'test'\n    \n    # Expect keys to be symbols\n    expect_symbol_keys\n\n    validations do\n      any Numeric\n    end\n  end\nend\n```\n\nNow the MediaType throws an error when string keys are used.\n\n```ruby\n  Acme::MyOverridingMedia.validate! { something: 42 }\n  # =\u003e passes, because all keys are a symbol\n\n  Acme::MyOverridingMedia.validate! { \"something\": 42 }\n  # =\u003e throws a ValidationError , because 'something' is a string key\n```\n\n### Setting The JSON Parser With The Wrong Key Type\n\nIf you parse JSON with the wrong key type, as shown below, the resultant object will fail the validations.\n\n```ruby\n  class MyMedia\n    include MediaTypes::Dsl\n\n    def self.organisation\n      'acme'\n    end\n\n    use_name 'test'\n    \n    # Expect keys to be symbols\n    expect_symbol_keys\n\n    validations do\n      any Numeric\n    end\n  end\n\n  json = JSON.parse('{\"foo\": {}}', { symbolize_names: false })\n  # If MyMedia expects symbol keys\n  MyMedia.valid?(json)\n  # Returns false\n```\n\n## Related\n\n- [`MediaTypes::Serialization`](https://github.com/XPBytes/media_types-serialization): :cyclone: Add media types supported serialization to Rails.\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, call `bundle exec rake release` to create a new git tag, push git commits and tags, and\npush the `.gem` file to rubygems.org.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at [SleeplessByte/media-types-ruby](https://github.com/SleeplessByte/media-types-ruby)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsleeplessbyte%2Fmedia-types-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsleeplessbyte%2Fmedia-types-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsleeplessbyte%2Fmedia-types-ruby/lists"}