Class: Respect::Schema

Inherits:
Object
  • Object
show all
Includes:
DocHelper
Defined in:
lib/respect/schema.rb

Overview

Base class for all object schema.

A schema defines the expected structure and format for a given object. It is similar in spirit to json-schemajson-schema.org(json-schema.org/) specification but uses Ruby DSL as definition. Using the DSL is not mandatory since you can also defines a schema using its own methods.

Almost all Schema sub-classes has an associated statement available in the DSL for defining it. This statement is named after the class name (see Schema.statement_name). However all sub-classes do not have a statement associated (see schema_for).

You can define such a schema using the Schema.define method. The #validate method allow you to check whether the given object is valid according to this schema. Various options can be passed to the schema when initializing it.

While validating an object the schema build a sanitized version of this object including all the validated part. The value presents in this sanitized object have generally a type specific to the contents they represents. For instance, a URI would be represented as a string in the original object but as a URI object in the sanitized object. There is a she-bang version of the validation method which update the value of the given object in-place with the value from the sanitized object if the validation succeeded.

You can pass several options when creating a Schema:

required

whether this property associated to this schema is required in the hash schema (true by default).

default

the default value to use for the associated property if it is not present. Setting a default value make the property optional. (nil by default)

doc

the documentation of this schema (nil by default). A documentation is composed of a title followed by an empty line and an optional long description. If set to false, then this schema is considered as an implementation details that should not be publicly documented. Thus, it will not be dumped as json-schema.org.

allow_nil

whether the schema accept nil as validation value. (false by default). This option is not supported yet by the json-schema.org standard.

These options applies to all schema sub-classes.

In addition to these options, you can configure any defined Validator. Validators are run during validation process by certain schema class like IntegerSchema, StringSchema, etc... They are mostly non-containers schema. In the following code the GreaterThanValidator will be run at validation time with the value o:

IntegerSchema.define greater_than: 0

This class is abstract. You cannot instantiate it directly. Use one of its sub-classes instead.

Direct Known Subclasses

AnySchema, ArraySchema, BooleanSchema, CompositeSchema, HashSchema, NullSchema, NumericSchema, StringSchema

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from DocHelper

#description, #title

Constructor Details

- (Schema) initialize(options = {})

Create a new schema using the given options.



127
128
129
130
# File 'lib/respect/schema.rb', line 127

def initialize(options = {})
  @sanitized_object = nil
  @options = self.class.default_options.merge(options)
end

Instance Attribute Details

- (Object) last_error (readonly)

Return the last validation error that happens during the validation process. (set by #validate?). Reset each time #validate? is called.



197
198
199
# File 'lib/respect/schema.rb', line 197

def last_error
  @last_error
end

- (Object) options (readonly)

Returns the hash of options.



142
143
144
# File 'lib/respect/schema.rb', line 142

def options
  @options
end

- (Object) sanitized_object

Returns the sanitized object. It is nil as long as you have not validated any object. It is overwritten every times you call #validate. If the validation failed it will be reset to nil.



139
140
141
# File 'lib/respect/schema.rb', line 139

def sanitized_object
  @sanitized_object
end

Class Method Details

+ (Object) def_class

Return the definition class symbol for this schema class or nil if there is no class (see def_class_name)



99
100
101
# File 'lib/respect/schema.rb', line 99

def def_class
  self.def_class_name.safe_constantize
end

+ (Object) def_class_name

Return the associated def class name for this class. Example:

ArraySchema.def_class_name  #=> "ArrayDef"
HashSchema.def_class_name #=> "HashDef"
Schema.def_class_name       #=> "SchemaDef"


89
90
91
92
93
94
95
# File 'lib/respect/schema.rb', line 89

def def_class_name
  if self == Schema
    "Respect::SchemaDef"
  else
    self.name.sub(/Schema$/, 'Def')
  end
end

+ (Object) default_options

Return the default options for this schema class. If you override this method in sub-classes, call super and merge the result with your default options.



115
116
117
118
119
120
121
122
# File 'lib/respect/schema.rb', line 115

def default_options
  {
    required: true,
    default: nil,
    doc: nil,
    allow_nil: false,
  }.freeze
end

+ (Object) define(*args, &block)

If a corresponding def class exists for this class (see def_class) it defines a new schema by evaluating the given block in the context of this definition class. It behaves as an alias for new if no block is given.

If there is no associated def class the block is passed to the constructor.



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/respect/schema.rb', line 71

def define(*args, &block)
  def_class = self.def_class
  if def_class
    if block
      def_class.eval(*args, &block)
    else
      self.new(*args)
    end
  else
    self.new(*args, &block)
  end
end

+ (Object) statement_name

Build a statement name from this class name.

Example:

Schema.statement_name                 #=> "schema"
HashSchema.statement_name             #=> "hash"


108
109
110
# File 'lib/respect/schema.rb', line 108

def statement_name
  self.name.underscore.sub(/^.*\//, '').sub(/_schema$/, '')
end

Instance Method Details

- (Object) ==(other)

Two schema are equal if they have the same type and the set of options. Sub-class definition may include more attributes.



275
276
277
# File 'lib/respect/schema.rb', line 275

def ==(other)
  self.class == other.class && @options == other.options
end

- (Boolean) allow_nil?

Returns whether this schema accept nil as validation value.

Returns:

  • (Boolean)


178
179
180
# File 'lib/respect/schema.rb', line 178

def allow_nil?
  !!@options[:allow_nil]
end

- (Object) default

Returns the default value used when this schema is missing.



168
169
170
# File 'lib/respect/schema.rb', line 168

def default
  @options[:default]
end

- (Object) documentation Also known as: doc

Returns the documentation of this schema.



145
146
147
# File 'lib/respect/schema.rb', line 145

def documentation
  @options[:doc]
end

- (Boolean) documented?

Returns whether this schema must be documented (i.e. not ignored when dumped).

Returns:

  • (Boolean)


153
154
155
# File 'lib/respect/schema.rb', line 153

def documented?
  @options[:doc] != false
end

- (Boolean) has_default?

Returns whether this schema has a default value defined.

Returns:

  • (Boolean)


173
174
175
# File 'lib/respect/schema.rb', line 173

def has_default?
  @options[:default] != nil
end

- (Object) initialize_copy(other)



132
133
134
# File 'lib/respect/schema.rb', line 132

def initialize_copy(other)
  @options = other.options.dup
end

- (Object) inspect

Returns a string containing a human-readable representation of this schema.



232
233
234
235
236
237
238
# File 'lib/respect/schema.rb', line 232

def inspect
  "#<%s:0x%x %s>" % [
    self.class.name,
    self.object_id,
    instance_variables.map{|v| "#{v}=#{instance_variable_get(v).inspect}" }.join(", ")
  ]
end

- (Object) non_default_options

Return the options with no default value. (Useful when writing a dumper)



258
259
260
# File 'lib/respect/schema.rb', line 258

def non_default_options
  @options.select{|opt, value| value != self.class.default_options[opt] }
end

- (Boolean) optional?

Whether this schema is optional.

Returns:

  • (Boolean)


163
164
165
# File 'lib/respect/schema.rb', line 163

def optional?
  !required?
end

- (Boolean) required?

Whether this schema is required. (opposite of optional?)

Returns:

  • (Boolean)


158
159
160
# File 'lib/respect/schema.rb', line 158

def required?
  @options[:required] && !has_default?
end

- (Object) sanitize!(object)

Sanitize the given object in-place if it validates this schema. The sanitized object is returned. ValidationError is raised on error.



221
222
223
224
# File 'lib/respect/schema.rb', line 221

def sanitize!(object)
  validate(object)
  sanitize_object!(object)
end

- (Object) sanitize_object!(object)

A shortcut for Respect.sanitize_object!.



227
228
229
# File 'lib/respect/schema.rb', line 227

def sanitize_object!(object)
  Respect.sanitize_object!(object, self.sanitized_object)
end

- (Object) to_h(format = :org3)

Convert this schema to a hash representation following the given format.



264
265
266
267
268
269
270
271
# File 'lib/respect/schema.rb', line 264

def to_h(format = :org3)
  case format
  when :org3
    Org3Dumper.new(self).dump
  else
    raise ArgumentError, "unknown format '#{format}'"
  end
end

- (Object) to_json(format = :org3)

Serialize this schema to a JSON string following the given format.



247
248
249
250
251
252
253
254
# File 'lib/respect/schema.rb', line 247

def to_json(format = :org3)
  case format
  when :org3
    self.to_h(:org3).to_json
  else
    raise ArgumentError, "unknown format '#{format}'"
  end
end

- (Object) to_s

Returns a string containing ruby code defining this schema. Theoretically, you can evaluate it and get the same schema afterward.



242
243
244
# File 'lib/respect/schema.rb', line 242

def to_s
  DslDumper.new(self).dump
end

- (Object) validate(object)

Raise a ValidationError if the given object is not validated by this schema. Returns true otherwise. A sanitized version of the object is built during this process and you can access it via #sanitized_object. Rewrite it in sub-classes.

Raises:

  • (NoMethodError)


203
204
205
# File 'lib/respect/schema.rb', line 203

def validate(object)
  raise NoMethodError, "override me in sub-classes"
end

- (Object) validate!(object)

Return true or false whether this schema validates the given object. If it does object is updated in-place with the sanitized value. This method does not raise a ValidationError. You can access the error using #last_error.



211
212
213
214
215
216
217
# File 'lib/respect/schema.rb', line 211

def validate!(object)
  valid = validate?(object)
  if valid
    sanitize_object!(object)
  end
  valid
end

- (Boolean) validate?(object)

Return whether the given object validates this schema. You can get the validation error via #last_error.

Returns:

  • (Boolean)


184
185
186
187
188
189
190
191
192
# File 'lib/respect/schema.rb', line 184

def validate?(object)
  begin
    validate(object)
    true
  rescue ValidationError => e
    @last_error = e
    false
  end
end