Thy is a minimal, strict runtime type system for Ruby.
There are many existing runtime type systems for Ruby, most notably, dry-types. Here's why you might prefer Thy:
- Thy encourages strictness. Avoiding type coercion as much as possible is an excellent way to keep your code predictable and straightforward.
- Minimalism. Thy's API only has what's needed, nothing more.
- No Any type. Don't worry, if you have to, you can get it!
Add this line to your application's Gemfile:
gem'thy'And then execute:
$ bundle
Or install it yourself as:
$ gem install thy
It's recommended not to use Thy directly (although you can), but to create a module
for working with types, and to include Thy::Types into it.
moduleTypesincludeThy::TypesendTo run a type check, call check on a type you need. For example:
moduleTypesincludeThy::TypesendTypes::String.check('a string').success?# => trueTypes::Integer.check(3.14).success?# => falseIf a check fails, a message describing the failure is provided:
Types::String.check(3).failure?# => trueTypes::String.check(3).message# => "Expected a String, but got: 3"Thy allows you to define custom types via Thy::Type's constructor:
moduleTypesincludeThy::TypesNonZeroValue=Thy::Type.new{ |value| value != 0}endmoduleTypesincludeThy::TypesNonZeroValue=Thy::Type.newdo |value|
value != 0 || Thy::Result::Failure.new("Expected #{value.inspect} to be nonzero")endendExtending existing types is simple, too:
moduleTypesincludeThy::TypesNonZeroInteger=Thy.refine_type(Integer,Thy::Type.new{ |v| v != 0})endYou can use Thy types directly, for example, if you'd like to play around in the console:
Thy::String.check('Bob').success?# => truePositiveInteger=Thy.refine_type(Integer,Thy::Type.new{ |v| v > 0})# StringThy::String.check('Bob').success?# => true# SymbolThy::Symbol.check(:bob).success?# => true# NumericThy::Numeric.check(3.14).success?# => trueThy::Numeric.check(0).success?# => true# FloatThy::Float.check(3.14).success?# => true# IntegerThy::Integer.check(3).success?# => true# BooleanThy::Boolean.check(true).success?# => trueThy::Boolean.check(false).success?# => true# TimeThy::Time.check(Time.now).success?# => trueThy::Time.check(0).success?# => false# DateTimerequire'date'Thy::DateTime.check(DateTime.now).success?# => trueThy::DateTime.check(0).success?# => false# nilThy::Nil.check(nil).success?# => trueThy::Nil.check(0).success?# => false# ArrayThy::Array(Thy::Integer).check([1,2,3]).success?# => trueThy::Array(Thy::String).check(%w[hihellosup]).success?# => true# HashThy::Hash({name: Thy::String,age: Thy::Integer,}).check({name: 'Bob',age: 18}).success?# => true# MapThy::Map(Thy::Symbol,Thy::Integer).check({a: 1,b: 2}).success?# => true# VariantThy::Variant(Thy::Integer,Thy::Float).check(3.14).success?# => trueThy::Variant(Thy::Integer,Thy::Float).check(3).success?# => true# EnumThy::Enum('USD','EUR').check('EUR').success?# => true# OptionThy::Option(Thy::String).check('a string').success?# => trueThy::Option(Thy::String).check(nil).success?# => true# InstanceOfclassA;endclassB < A;endclassC;endThy::InstanceOf(A).check(B.new).success?# => trueThy::InstanceOf(A).check(C.new).success?# => false# ClassExtendingThy::ClassExtending(A).check(B).success?# => trueThy::ClassExtending(A).check(C).success?# => false# UntypedArrayThy::UntypedArray.check(1,'string',3.14).success?# => true# UntypedHashThy::UntypedHash.check({a: 1,b: 'c',1=>true}).success?# => trueBug reports and pull requests are welcome on GitHub at https://github.com/akxcv/thy.
The gem is available as open source under the terms of the MIT License.