Class: MediaTypes::Scheme

Inherits:
Object
  • Object
show all
Defined in:
lib/media_types/scheme.rb,
lib/media_types/scheme/links.rb,
lib/media_types/scheme/any_of.rb,
lib/media_types/scheme/allow_nil.rb,
lib/media_types/scheme/attribute.rb,
lib/media_types/scheme/not_strict.rb,
lib/media_types/scheme/missing_validation.rb,
lib/media_types/scheme/validation_options.rb,
lib/media_types/scheme/enumeration_context.rb,
lib/media_types/scheme/enumeration_of_type.rb

Overview

Media Type Schemes can validate content to a media type, by itself. Used by the validations dsl.

Examples:

A scheme to test against


class MyMedia
  include MediaTypes::Dsl

  validations do
    attribute :foo do
      collection :bar, String
    end
    attribute :number, Numeric
  end
end

MyMedia.valid?({ foo: { bar: ['test'] }, number: 42 })
#=> true

See Also:

Defined Under Namespace

Classes: Attribute, CaseEqualityWithList, CaseEqualityWithNil, CollectionTypeError, EmptyOutputError, EnumerationContext, EnumerationOfType, ExhaustedOutputError, Links, MissingValidation, NotStrict, StrictValidationError, ValidationError, ValidationOptions

Instance Method Summary collapse

Constructor Details

#initialize(allow_empty: false, force: nil) ⇒ Scheme

Creates a new scheme

Parameters:

  • allow_empty (TrueClass, FalseClass) (defaults to: false)

    if true allows to be empty, if false raises EmptyOutputError if empty

  • force (NilClass, Class) (defaults to: nil)

    forces the type to be this type, if given

See Also:



60
61
62
63
64
65
66
# File 'lib/media_types/scheme.rb', line 60

def initialize(allow_empty: false, force: nil)
  self.validations = {}
  self.allow_empty = allow_empty
  self.force = force

  validations.default = MissingValidation.new
end

Instance Method Details

#AllowNil(klazz) ⇒ CaseEqualityWithNil

noinspection RubyInstanceMethodNamingConvention

Allows the wrapped klazz to be nil

Parameters:

  • klazz (Class)

    the class that it must be the if it is not NilClass

Returns:



21
22
23
# File 'lib/media_types/scheme/allow_nil.rb', line 21

def AllowNil(klazz) # rubocop:disable Naming/MethodName
  CaseEqualityWithNil.new(klazz)
end

#any(scheme = nil, force: ::Hash, allow_empty: false, &block) ⇒ Object

Allow for any key.

The +&block+ defines the Schema for each value.

Examples:

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

Parameters:

  • scheme (Scheme, NilClass) (defaults to: nil)

    scheme to use if no &block is given

  • allow_empty (TrueClass, FalseClass) (defaults to: false)

    if true, empty (no key/value present) is allowed

  • force (Class) (defaults to: ::Hash)

    forces the validated object to have this type

See Also:



210
211
212
213
214
215
216
217
218
219
# File 'lib/media_types/scheme.rb', line 210

def any(scheme = nil, force: ::Hash, allow_empty: false, &block)
  unless block_given?
    return validations.default = scheme
  end

  scheme = Scheme.new(allow_empty: allow_empty, force: force)
  scheme.instance_exec(&block)

  validations.default = scheme
end

#AnyOf(*klazzes) ⇒ CaseEqualityWithList

noinspection RubyInstanceMethodNamingConvention

Allows it to be any of the wrapped klazzes

Parameters:

  • klazzes (Array<Class>)

    the classes that are valid for it

Returns:



21
22
23
# File 'lib/media_types/scheme/any_of.rb', line 21

def AnyOf(*klazzes) # rubocop:disable Naming/MethodName
  CaseEqualityWithList.new(klazzes)
end

#attribute(key, type = String, **opts, &block) ⇒ Object

Adds an attribute to the schema

If a +block+ is given, uses that to test against instead of +type+

Examples:

Add an attribute named foo, expecting a string


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

Parameters:

  • key (Symbol)

    the attribute name

  • opts (Hash)

    options to pass to Scheme or Attribute

  • type (Class, #===, Scheme) (defaults to: String)

    The type of the value, can be anything that responds to #===, or scheme to use if no &block is given. Defaults to String without a &block and to Hash with a &block.

See Also:



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/media_types/scheme.rb', line 171

def attribute(key, type = String, **opts, &block)
  if block_given?
    return collection(key, force: ::Hash, **opts, &block)
  end

  if type.is_a?(Scheme)
    return validations[key] = type
  end

  validations[key] = Attribute.new(type, **opts, &block)
end

#collection(key, scheme = nil, allow_empty: false, force: Array, &block) ⇒ Object

Expect a collection such as an array or hash.

The +block+ defines the Schema for each item in that collection.

Examples:

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

Parameters:

  • key (Symbol)

    key of the collection (same as #attribute)

  • scheme (NilClass, Scheme, Class) (defaults to: nil)

    scheme to use if no &block is given, or type of each item in collection

  • allow_empty (TrueClass, FalseClass) (defaults to: false)

    if true accepts 0 items in an enumerable

  • force (Class) (defaults to: Array)

    forces the value of this collection to be this type, defaults to Array.

See Also:



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/media_types/scheme.rb', line 286

def collection(key, scheme = nil, allow_empty: false, force: Array, &block)
  unless block_given?
    if scheme.is_a?(Scheme)
      return validations[key] = scheme
    end

    return validations[key] = EnumerationOfType.new(
      scheme,
      enumeration_type: force,
      allow_empty: allow_empty
    )
  end

  scheme = Scheme.new(allow_empty: allow_empty, force: force)
  scheme.instance_exec(&block)

  validations[key] = scheme
end

Expect a link

Examples:

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

See Also:



339
340
341
342
343
344
345
# File 'lib/media_types/scheme.rb', line 339

def link(*args, **opts, &block)
  validations.fetch(:_links) do
    Links.new.tap do |links|
      validations[:_links] = links
    end
  end.link(*args, **opts, &block)
end

#not_strictObject

Allow for extra keys in the schema/collection even when passing strict: true to #validate!

Examples:

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

See Also:



242
243
244
# File 'lib/media_types/scheme.rb', line 242

def not_strict
  validations.default = NotStrict.new
end

#valid?(output, **opts) ⇒ TrueClass, FalseClass

Checks if the output is valid

Parameters:

  • output (#each)

    the output to test against

  • opts (Hash)

    the options as defined below

  • exhaustive (Hash)

    a customizable set of options

  • strict (Hash)

    a customizable set of options

Returns:

  • (TrueClass, FalseClass)

    true if valid, false otherwise



78
79
80
81
82
83
84
# File 'lib/media_types/scheme.rb', line 78

def valid?(output, **opts)
  validate(output, **opts)
rescue ExhaustedOutputError
  !opts.fetch(:exhaustive) { true }
rescue ValidationError
  false
end

#validate(output, options = nil, **opts) ⇒ TrueClass

Validates the output and raises on certain validation errors

Parameters:

  • output (#each)

    output to validate

  • opts (Hash)

    a customizable set of options

  • opts[Array<String>] (Hash)

    a customizable set of options

Options Hash (**opts):

  • exhaustive (TrueClass, FalseClass)

    if true, the entire schema needs to be consumed

  • strict (TrueClass, FalseClass)

    if true, no extra keys may be present in output

Returns:

  • (TrueClass)

Raises:

  • ExhaustedOutputError

  • StrictValidationError

  • EmptyOutputError

  • CollectionTypeError

  • ValidationError

See Also:



104
105
106
107
108
109
110
# File 'lib/media_types/scheme.rb', line 104

def validate(output, options = nil, **opts)
  options ||= ValidationOptions.new(**opts)

  catch(:end) do
    validate!(output, options, context: nil)
  end
end

#validate!(output, call_options, **_opts) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/media_types/scheme.rb', line 115

def validate!(output, call_options, **_opts)
  empty_guard!(output, call_options)

  exhaustive_guard!(validations.keys, call_options) do |mark|
    all?(output, call_options) do |key, value, options:, context:|
      mark.call(key)

      validations[key].validate!(
        value,
        options.trace(key),
        context: context
      )
    end
  end
end