MediaTypes
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.
Installation
Add this line to your application's Gemfile:
gem 'media_types'
And then execute:
$ bundle
Or install it yourself as:
$ gem install media_types
Usage
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
end
Validate an object:
Acme::FooValidator.validate!({ foo: 'bar' })
Full example
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
end
Schema Definitions
If you include 'MediaTypes::Dsl' in your class you can use the following functions within a validation do
block to define your schema:
attribute
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 |
Add an attribute named foo, expecting a string
require 'media_types'
class MyMedia
include MediaTypes::Dsl
validations do
attribute :foo, String
end
end
MyMedia.valid?({ foo: 'my-string' })
# => true
Add an attribute named foo, expecting nested scheme
class MyMedia
include MediaTypes::Dsl
validations do
attribute :foo do
attribute :bar, String
end
end
end
MyMedia.valid?({ foo: { bar: 'my-string' }})
# => true
any
Allow 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 |
Add a collection named foo, expecting any key with a defined value
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' } }] })
# => true
not_strict
Allow for extra keys in the schema/collection even when passing strict: true
to #validate!
Allow for extra keys in collection
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 }] })
# => true
collection
Expect 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 |
Collection with an array of string
class MyMedia
include MediaTypes::Dsl
validations do
collection :foo, String
end
end
MyMedia.valid?({ collection: ['foo', 'bar'] })
# => true
Collection with defined scheme
class 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 }] })
# => true
link
Expect 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 |
Links as defined in HAL, JSON-Links and other specs
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' }} })
# => true
Link with extra attributes
class 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 }} })
# => true
Validation
If 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 valid
If 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' }])
# => true
Formatting for headers
Any 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"
API
A defined schema has the following functions available:
valid?
Example: Venue.valid?({ foo: 'bar' })
Allows passing in validation options as a second parameter.
validate!
Example: Venue.validate!({ foo: 'bar' })
Allows passing in validation options as a second parameter.
validatable?
Example: Venue.version(42).validatable?
Tests wether the current configuration of the schema has a validation defined.
register
Example: Venue.register
Registers the media type to the registry.
view
Example: Venue.view('create')
Returns a schema validator configured with the specified view.
version
Example: Venue.version(42)
Returns a schema validator configured with the specified version.
suffix
Example: Venue.suffix(:json)
Returns a schema validator configured with the specified suffix.
identifier
Example: Venue.version(2).identifier
(returns 'application/vnd.application.venue.v2'
)
Returns the IANA compatible Media Type Identifier for the configured schema.
available_validations
Example: Venue.available_validations
Returns a list of all the schemas that are defined.
Related
MediaTypes::Serialization
: :cyclone: Add media types supported serialization using your favourite serializerMediaTypes::Validation
: :heavy_exclamation_mark: Response validations according to a media-type
Development
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.
Contributing
Bug reports and pull requests are welcome on GitHub at SleeplessByte/media-types-ruby