Ruby-media-types

From Delft Solutions
Jump to navigation Jump to search

The repository for the media_types gem currently lives at GitHub: sleeplessbyte/media-types-ruby.

MediaTypes

Media Types based on scheme, with versioning, views, suffixes and validations.

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 }]

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

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