Media Types based on scheme, with versioning, views, suffixes and validations. Integrations available for Rails / ActionPack and http.rb.
This library makes it easy to define schemas that can be used to validate JSON objects based on their Content-Type.
Add this line to your application's Gemfile:
gem 'media_types'And then execute:
$ bundle
Or install it yourself as:
$ gem install media_types
Define a validation:
require 'media_types'
module Acme
MediaTypes::set_organisation Acme, 'acme'
class FooValidator
include MediaTypes::Dsl
use_name 'foo'
validations do
attribute :foo, String
end
end
endValidate an object:
Acme::FooValidator.validate!({ foo: 'bar' })require 'media_types'
class Venue
include MediaTypes::Dsl
def self.organisation
'mydomain'
end
use_name 'venue'
validations do
version 2 do
attribute :name, String
collection :location do
attribute :latitude, Numeric
attribute :longitude, Numeric
attribute :altitude, AllowNil(Numeric)
end
link :self
link :route, allow_nil: true
end
version 1 do
attribute :name, String
attribute :coords, String
attribute :updated_at, String
link :self
end
view 'create' do
collection :location do
attribute :latitude, Numeric
attribute :longitude, Numeric
attribute :altitude, AllowNil(Numeric)
end
version 1 do
collection :location do
attribute :latitude, Numeric
attribute :longitude, Numeric
attribute :altitude, AllowNil(Numeric)
end
end
end
end
endIf you include 'MediaTypes::Dsl' in your class you can use the following functions within a validation do block to define your schema:
Adds an attribute to the schema, if a +block+ is given, uses that to test against instead of +type+
| param | type | description |
|---|---|---|
| key | Symbol |
the attribute name |
| opts | Hash |
options to pass to Scheme or Attribute |
| 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. |
| optional: | TrueClass, FalseClass |
if true, key may be absent, defaults to false |
| &block | Block |
defines the scheme of the value of this attribute |
require 'media_types'
class MyMedia
include MediaTypes::Dsl
validations do
attribute :foo, String
end
end
MyMedia.valid?({ foo: 'my-string' })
# => trueclass MyMedia
include MediaTypes::Dsl
validations do
attribute :foo do
attribute :bar, String
end
end
end
MyMedia.valid?({ foo: { bar: 'my-string' }})
# => trueAllow for any key. The &block defines the Schema for each value.
| param | type | description |
|---|---|---|
| scheme | Scheme, NilClass |
scheme to use if no &block is given |
| allow_empty: | TrueClass, FalsClass |
if true, empty (no key/value present) is allowed |
| expected_type: | Class, |
forces the validated value to have this type, defaults to Hash. Use Object if either Hash or Array is fine |
| &block | Block |
defines the scheme of the value of this attribute |
class MyMedia
include MediaTypes::Dsl
validations do
collection :foo do
any do
attribute :bar, String
end
end
end
end
MyMedia.valid?({ foo: [{ anything: { bar: 'my-string' }, other_thing: { bar: 'other-string' } }] })
# => trueAllow for extra keys in the schema/collection even when passing strict: true to #validate!
class MyMedia
include MediaTypes::Dsl
validations do
collection :foo do
attribute :required, String
not_strict
end
end
end
MyMedia.valid?({ foo: [{ required: 'test', bar: 42 }] })
# => trueExpect a collection such as an array or hash. The &block defines the Schema for each item in that collection.
| param | type | description |
|---|---|---|
| key | Symbol |
key of the collection (same as #attribute) |
| scheme | Scheme, NilClass, Class |
scheme to use if no &block is given or Class of each item in the |
| allow_empty: | TrueClass, FalseClass |
if true, empty (no key/value present) is allowed |
| expected_type: | Class, |
forces the validated value to have this type, defaults to Array. Use Object if either Array or Hash is fine. |
| optional: | TrueClass, FalseClass |
if true, key may be absent, defaults to false |
| &block | Block |
defines the scheme of the value of this attribute |
class MyMedia
include MediaTypes::Dsl
validations do
collection :foo, String
end
end
MyMedia.valid?({ collection: ['foo', 'bar'] })
# => trueclass MyMedia
include MediaTypes::Dsl
validations do
collection :foo do
attribute :required, String
attribute :number, Numeric
end
end
end
MyMedia.valid?({ foo: [{ required: 'test', number: 42 }, { required: 'other', number: 0 }] })
# => trueExpect a link with a required href: String attribute
| param | type | description |
|---|---|---|
| key | Symbol |
key of the link (same as #attribute) |
| allow_nil: | TrueClass, FalseClass |
if true, value may be nil |
| optional: | TrueClass, FalseClass |
if true, key may be absent, defaults to false |
| &block | Block |
defines the scheme of the value of this attribute, in addition to the href attribute |
class MyMedia
include MediaTypes::Dsl
validations do
link :_self
link :image
end
end
MyMedia.valid?({ _links: { self: { href: 'https://example.org/s' }, image: { href: 'https://image.org/i' }} })
# => trueclass MyMedia
include MediaTypes::Dsl
validations do
link :image do
attribute :templated, TrueClass
end
end
end
MyMedia.valid?({ _links: { image: { href: 'https://image.org/{md5}', templated: true }} })
# => trueIf your type has a validations, you can now use this media type for validation:
Venue.valid?({
#...
})
# => true if valid, false otherwise
Venue.validate!({
# /*...*/
})
# => raises if it's not validIf an array is passed, check the scheme for each value, unless the scheme is defined as expecting a hash:
expected_hash = Scheme.new(expected_type: Hash) { attribute(:foo) }
expected_object = Scheme.new { attribute(:foo) }
expected_hash.valid?({ foo: 'string' })
# => true
expected_hash.valid?([{ foo: 'string' }])
# => false
expected_object.valid?({ foo: 'string' })
# => true
expected_object.valid?([{ foo: 'string' }])
# => trueAny media type object can be converted in valid string to be used with Content-Type or Accept:
Venue.mime_type.identifier
# => "application/vnd.mydomain.venue.v2+json"
Venue.mime_type.version(1).identifier
# => "application/vnd.mydomain.venue.v1+json"
Venue.mime_type.to_s(0.2)
# => "application/vnd.mydomain.venue.v2+json; q=0.2"
Venue.mime_type.collection.identifier
# => "application/vnd.mydomain.venue.v2.collection+json"
Venue.mime_type.view('active').identifier
# => "application/vnd.mydomain.venue.v2.active+json"A defined schema has the following functions available:
Example: Venue.valid?({ foo: 'bar' })
Allows passing in validation options as a second parameter.
Example: Venue.validate!({ foo: 'bar' })
Allows passing in validation options as a second parameter.
Example: Venue.version(42).validatable?
Tests wether the current configuration of the schema has a validation defined.
Example: Venue.register
Registers the media type to the registry.
Example: Venue.view('create')
Returns a schema validator configured with the specified view.
Example: Venue.version(42)
Returns a schema validator configured with the specified version.
Example: Venue.suffix(:json)
Returns a schema validator configured with the specified suffix.
Example: Venue.version(2).identifier (returns 'application/vnd.application.venue.v2')
Returns the IANA compatible Media Type Identifier for the configured schema.
Example: Venue.available_validations
Returns a list of all the schemas that are defined.
MediaTypes::Serialization: π Add media types supported serialization using your favourite serializerMediaTypes::Validation: β Response validations according to a media-type
After 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.
To 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
push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at SleeplessByte/media-types-ruby