General

Lutaml::Model provides comprehensive validation for data models using the validate and validate! methods.

  • The validate method returns an errors array containing all validation errors. This method is used for checking the validity of the model silently.

  • The validate! method raises a Lutaml::Model::ValidationError that contains all the validation errors. This method is used for forceful validation of the model through raising an error.

Validation types

Lutaml::Model supports the following validation types:

Collection validation

The collection option validates collection size ranges.

attribute :items, :string, collection: (1..)  # At least one item
attribute :tags, :string, collection: (0..5)  # Up to 5 tags

Value enumeration validation

The values option validates that an attribute value is one of a fixed set of values.

attribute :status, :string, values: %w[draft published archived]

Choice validation

The choice directive validates that attributes within a defined range are present.

choice(min: 1, max: 2) do
  attribute :email, :string
  attribute :phone, :string
end

Pattern validation

The pattern option validates string values against regular expressions.

attribute :email, :string, pattern: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i

Required attributes

The required: true option validates that an attribute is present.

attribute :name, :string, required: true

Validation examples

The following class will validate the name is present and degree_settings attributes to ensure that it has at least one element and that the description attribute is one of the values in the set [one, two, three].

class Klin < Lutaml::Model::Serializable
  attribute :name, :string, required: true
  attribute :degree_settings, :integer, collection: (1..)
  attribute :description, :string, values: %w[one two three]
  attribute :id, :integer
  attribute :age, :integer

  choice(min: 1, max: 1) do
    choice(min: 1, max: 2) do
      attribute :prefix, :string
      attribute :forename, :string
    end

    attribute :nick_name, :string
  end

  xml do
    map_element 'name', to: :name
    map_attribute 'degree_settings', to: :degree_settings
  end
end

klin = Klin.new(name: "Klin", degree_settings: [100, 200, 300], description: "one", prefix: "Ben")
klin.validate
# => []

klin = Klin.new(name: "Klin", degree_settings: [], description: "four", prefix: "Ben", nick_name: "Smith")
klin.validate
# => [
#      #<Lutaml::Model::CollectionSizeError: degree_settings must have at least 1 element>,
#      #<Lutaml::Model::ValueError: description must be one of [one, two, three]>,
#      #<Lutaml::Model::ChoiceUpperBoundError: Attribute count exceeds the upper bound>
#    ]

e = klin.validate!
# => Lutaml::Model::ValidationError: [
#      degree_settings must have at least 1 element,
#      description must be one of [one, two, three],
#      Attribute count exceeds the upper bound
#    ]
e.errors
# => [
#     #<Lutaml::Model::CollectionSizeError: degree_settings must have at least 1 element>,
#     #<Lutaml::Model::ValueError: description must be one of [one, two, three]>,
#     #<Lutaml::Model::ChoiceUpperBoundError: Attribute count exceeds the upper bound>
#     #<Lutaml::Model::ChoiceLowerBoundError: Attribute count is less than lower bound>
#   ]

klin = Klin.new(degree_settings: [100, 200, 300], description: "one", prefix: "Ben")
klin.validate
# => [
#      #<Lutaml::Model::RequiredAttributeMissingError: Missing required attribute: name>
#    ]

Custom validation

To add custom validation, override the validate method in the model class. Additional errors should be added to the errors array.

The following class validates the degree_settings attribute when the type is glass to ensure that the value is less than 1300.

class Klin < Lutaml::Model::Serializable
  attribute :name, :string
  attribute :type, :string, values: %w[glass ceramic]
  attribute :degree_settings, :integer, collection: (1..)

  def validate
    errors = super
    if type == "glass" && degree_settings.any? { |d| d > 1300 }
      errors << Lutaml::Model::Error.new("Degree settings for glass must be less than 1300")
    end
  end
end

klin = Klin.new(name: "Klin", type: "glass", degree_settings: [100, 200, 1400])
klin.validate
# => [#<Lutaml::Model::Error: Degree settings for glass must be less than 1300>]