Update to Eventide's Schema Library Attribute Type Checking and a Breaking Change
Eventide’s Schema library introduces a new protocol for attribute type checking with version 2.5.0.0, which is available immediately.
Incompatibility with Prior Versions
Along with the new capabilities for type checking, the strict type checking mode for attributes has been removed, and the strict
argument of the attribute
macro is no longer supported. If you use the strict
argument in any of your schema class attribute definitions, your schema class definitions will fail to load.
Previous Behavior
In schema class definitions using v2.4.0.0 (and earlier), attributes were made type-safe by declaring the attribute with an optional type:
class SomeClass
include Schema
attribute :animal, Animal
end
The animal
attribute could be assigned an instance of the Animal
class, or any subclass of the Animal
class.
class Animal
end
class Dog < Animal
end
some_object = SomeClass.new
some_object.animal = Animal.new
some_object.animal = Dog.new
To restrict a typed attribute to only accept instances of the attribute’s type, and to reject subclasses, the optional strict
attribute could be used as part of the attribute declaration:
class SomeClass
include Schema
attribute :animal, Animal, strict: true
end
some_object = SomeClass.new
some_object.animal = Animal.new
some_object.animal = Dog.new
# => Schema::Attribute::TypeError
With strict attribute typing, polymorphism was not supported. The default behavior was permissive of subclasses unless specifically restricted.
New Implementation
In the latest version of the Schema library, the type-checking remains permissive by default, but the type checking is entirely customizable, allowing any kind of type checking to be specified, including being impermissive of polymorphism.
The new protocol requires a module named TypeCheck
to be implemented in the type’s namespace:
class PositiveNumber
module TypeCheck
def self.call(type, val)
return true if val.nil?
return false unless val.is_a?(Numeric)
val > 0
end
end
end
When a class implements the TypeCheck
protocol, it will be executed to prove that the value assigned to the attribute conforms to the type checking implementation:
class SomeClass
include Schema
attribute :amount, PositiveNumber
end
some_object = SomeClass.new
some_object.amount = 123
# => 123
some_object.amount = -1
# => Schema::Attribute::Error
It’s important to note that the only type checking that will be performed will be the logic implemented in the type’s TypeCheck
module. When the protocol is implemented, the developer must take full control over all aspects of the type check. The Schema library is entirely “hands-off” with regards to type checking when a type implements the TypeCheck
protocol. Therefore, as seen in the above example, checking the fundamentals, like whether the value is nil or whether it’s an instance of some type, is entirely the responsibility of the particular TypeCheck
implementation.
To implement the strict type checking behavior of v2.4.0.0 (and earlier), the TypeCheck
protocol implementation would assert that the type of the value is exactly the type expected:
class Animal
module TypeCheck
def self.call(type, val)
return true if val.nil?
val.instance_of?(Animal)
end
end
end
Optional Type Checking
Optional type checking for attributes remains unchanged with this latest release. If an attribute is declared without a type, an instance of any class can be assigned to the attribute:
class SomeClass
include Schema
attribute :name
end
some_object = SomeClass.new
some_object.name = 'Some Name'
# => "Some Name"
some_object.name = 123
# => 123
Available Immediately
The v2.5.0.0 release of the Schema library is available as of the publication of this article. And again, schema class definitions that use attributes with the strict
attribute must be updated to use the TypeCheck
protocol.