···11-# frozen_string_literal: true
22-33-class Literal::Hash
44- def initialize(value, key_type:, value_type:)
55- @value = value
66- @key_type = key_type
77- @value_type = value_type
88- end
99-1010- attr_accessor :value, :key_type, :value_type
1111-1212- def []=(key, value)
1313- if @key_type === key && @value_type === value
1414- @value[key] = value
1515- else
1616- raise Literal::TypeError
1717- end
1818- end
1919-end
-24
lib/literal/hash_type.rb
···11-# frozen_string_literal: true
22-33-# @api private
44-class Literal::HashType
55- def initialize(key_type, value_type)
66- @key_type = key_type
77- @value_type = value_type
88- end
99-1010- def inspect = "Hash(#{@key_type}, #{@value_type})"
1111-1212- def ===(value)
1313- case value
1414- when Literal::Hash
1515- @key_type == value.key_type && @value_type == value.value_type
1616- else
1717- false
1818- end
1919- end
2020-2121- def new(value)
2222- Literal::Hash.new(value, key_type: @key_type, value_type: @value_type)
2323- end
2424-end
+109-106
lib/literal/types.rb
···77 autoload :CallableType, "literal/types/callable_type"
88 autoload :ClassType, "literal/types/class_type"
99 autoload :ConstraintType, "literal/types/constraint_type"
1010- autoload :DependantType, "literal/types/dependant_type"
1010+ autoload :DescendantType, "literal/types/descendant_type"
1111 autoload :EnumerableType, "literal/types/enumerable_type"
1212 autoload :FalsyType, "literal/types/falsy_type"
1313- autoload :LambdaType, "literal/types/lambda_type"
1313+ autoload :FloatType, "literal/types/float_type"
1414 autoload :FrozenType, "literal/types/frozen_type"
1515- autoload :JSONDataType, "literal/types/json_data_type"
1515+ autoload :HashType, "literal/types/hash_type"
1616+ autoload :IntegerType, "literal/types/integer_type"
1617 autoload :InterfaceType, "literal/types/interface_type"
1818+ autoload :IntersectionType, "literal/types/intersection_type"
1919+ autoload :JSONDataType, "literal/types/json_data_type"
2020+ autoload :LambdaType, "literal/types/lambda_type"
2121+ autoload :MapType, "literal/types/map_type"
2222+ autoload :NeverType, "literal/types/never_type"
2323+ autoload :NilableType, "literal/types/nilable_type"
1724 autoload :NotType, "literal/types/not_type"
2525+ autoload :ProcableType, "literal/types/procable_type"
2626+ autoload :RangeType, "literal/types/range_type"
1827 autoload :SetType, "literal/types/set_type"
1919- autoload :TupleType, "literal/types/tuple_type"
2020- autoload :IntegerType, "literal/types/integer_type"
2121- autoload :TruthyType, "literal/types/truthy_type"
2222- autoload :FloatType, "literal/types/float_type"
2323- autoload :HashType, "literal/types/hash_type"
2828+ autoload :ShapeType, "literal/types/shape_type"
2429 autoload :StringType, "literal/types/string_type"
2525- autoload :NeverType, "literal/types/never_type"
2626-2727- # Matches if *any* given type is matched.
2828- def _Union(*types)
2929- raise Literal::ArgumentError.new("_Union type must have at least one type.") if types.size < 1
3030+ autoload :SymbolType, "literal/types/symbol_type"
3131+ autoload :TruthyType, "literal/types/truthy_type"
3232+ autoload :TupleType, "literal/types/tuple_type"
3333+ autoload :VoidType, "literal/types/void_type"
30343131- Literal::Union.new(*types)
3535+ # Matches any value except `nil`. Use `_Nilable(_Any)` to match any value including `nil`.
3636+ def _Any
3737+ Literal::Types::AnyType
3238 end
33393434- # Matches if *all* given types are matched.
3535- def _Intersection(*types)
3636- raise Literal::ArgumentError.new("_Intersection type must have at least one type.") if types.size < 1
4040+ # Matches if the value is an `Array` and all the elements match the given type.
4141+ def _Array(type)
4242+ Literal::Types::ArrayType.new(type)
4343+ end
37443838- Literal::Types::IntersectionType.new(*types)
4545+ # Matches if the value is `true` or `false`.
4646+ def _Boolean
4747+ Literal::Types::BooleanType
3948 end
40494141- # Matches if the given predicates are truthy.
4242- def _Is(*predicates)
4343- raise Literal::ArgumentError.new("_Is type must have at least one predicate.") if predicates.size < 1
5050+ # Matches if the value responds to `#call`.
5151+ def _Callable(type = nil)
5252+ Literal::Types::CallableType
5353+ end
44544545- Literal::Types::IsType.new(*predicates)
5555+ # Matches if the value either the given class or a subclass of it.
5656+ def _Class(type)
5757+ Literal::Types::ClassType.new(type)
4658 end
47594848- # Matches if the value is an `Array` and all the elements match the given type.
4949- def _Array(type)
5050- Literal::Types::ArrayType.new(type)
6060+ # Similar to `_Intersection`, but allows you to specify attribute constraints as keyword arguments.
6161+ # @example
6262+ # _Constraint(Array, size: 1..3)
6363+ def _Constraint(*constraints, **attributes)
6464+ Literal::Types::ConstraintType.new(*constraints, **attributes)
5165 end
52665353- # Matches if the value is a `Set` and all the elements match the given type.
5454- def _Set(type)
5555- Literal::Types::SetType.new(type)
6767+ # Matches if the value is a descendant of the given class.
6868+ def _Descendant(type)
6969+ Literal::Types::DescendantType.new(type)
5670 end
57715858- # Matches if the value is an `Enumerable` and all the elements match the given type.
7272+ # Matches if the value is an `Enumerable` and all its elements match the given type.
5973 def _Enumerable(type)
6074 Literal::Types::EnumerableType.new(type)
6175 end
62767777+ # Matches *"falsy"* values (`nil` and `false`).
7878+ def _Falsy
7979+ Literal::Types::FalsyType
8080+ end
8181+8282+ # Matches if the value is a `Float` and matches the given constraint.
8383+ # You could use a `Range`, for example, as a constraint.
8484+ # If you don't need a constraint, use `Float` instead of `_Float`.
8585+ def _Float(constraint)
8686+ Literal::Types::FloatType.new(constraint)
8787+ end
8888+8989+ # Matches if the value is *frozen*.
9090+ def _Frozen(type)
9191+ Literal::Types::FrozenType.new(type)
9292+ end
9393+6394 # Matches if the value is a `Hash` and all the keys and values match the given types.
6495 def _Hash(key_type, value_type)
6596 Literal::Types::HashType.new(key_type, value_type)
6697 end
67989999+ # Matches if the value is an `Integer` and matches the given constraint.
100100+ # You could use a `Range`, for example, as a constraint.
101101+ # If you don't need a constraint, use `Integer` instead of `_Integer`.
102102+ # @example
103103+ # attribute :age, _Integer(18..127)
104104+ def _Integer(constraint)
105105+ Literal::Types::IntegerType.new(constraint)
106106+ end
107107+68108 # Matches if the value responds to all the given methods.
69109 def _Interface(*methods)
70110 raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
···72112 Literal::Types::InterfaceType.new(*methods)
73113 end
741147575- # Matches if the value is either `nil` or the given type.
7676- def _Nilable(type)
7777- Literal::Types::NilableType.new(type)
115115+ # Matches if *all* given types are matched.
116116+ def _Intersection(*types)
117117+ raise Literal::ArgumentError.new("_Intersection type must have at least one type.") if types.size < 1
118118+119119+ Literal::Types::IntersectionType.new(*types)
78120 end
791218080- # Matches any value except `nil`. Use `_Nilable(_Any)` to match any value including `nil`.
8181- def _Any
8282- Literal::Types::AnyType
122122+ # Ensures the value is valid JSON data (i.e. it came from JSON.parse).
123123+ def _JSONData
124124+ Literal::Types::JSONDataType
83125 end
841268585- # Matches if the value is `true` or `false`.
8686- def _Boolean
8787- Literal::Types::BooleanType
127127+ # Matches if the value is a `Proc` and `#lambda?` returns truthy.
128128+ def _Lambda
129129+ Literal::Types::LambdaType
88130 end
891319090- # Matches if the value either the given class or a subclass of it.
9191- def _Class(type)
9292- Literal::Types::ClassType.new(type)
132132+ def _Map(**shape)
133133+ Literal::Types::MapType
93134 end
941359595- # Matches if the value is an `Array` and each element matches the given types in order.
9696- def _Tuple(*types)
9797- raise Literal::ArgumentError.new("_Tuple type must have at least one type.") if types.size < 1
136136+ # Never matches any value.
137137+ def _Never
138138+ Literal::Types::NeverType
139139+ end
981409999- Literal::Types::TupleType.new(*types)
141141+ # Matches if the value is either `nil` or the given type.
142142+ def _Nilable(type)
143143+ Literal::Types::NilableType.new(type)
100144 end
101145102102- # Matches if the value is an `Integer` and matches the given constraint.
103103- # You could use a `Range`, for example, as a constraint.
104104- # If you don't need a constraint, use `Integer` instead of `_Integer`.
105105- # @example
106106- # attribute :age, _Integer(18..127)
107107- def _Integer(constraint)
108108- Literal::Types::IntegerType.new(constraint)
146146+ # Matches if the given type is *not* matched.
147147+ def _Not(type)
148148+ Literal::Types::NotType.new(type)
109149 end
110150111111- # Matches if the value is a `Float` and matches the given constraint.
112112- # You could use a `Range`, for example, as a constraint.
113113- # If you don't need a constraint, use `Float` instead of `_Float`.
114114- def _Float(constraint)
115115- Literal::Types::FloatType.new(constraint)
151151+ # Matches if the value is a `Proc` or responds to `#to_proc`.
152152+ def _Procable
153153+ Literal::Types::ProcableType
116154 end
117155118156 # Matches if the value is a `Range` of the given type.
···120158 Literal::Types::RangeType.new(type)
121159 end
122160123123- # Matches if the value responds to `#call`.
124124- def _Callable(type = nil)
125125- Literal::Types::CallableType
161161+ # Matches if the value is a `Set` and all the elements match the given type.
162162+ def _Set(type)
163163+ Literal::Types::SetType.new(type)
126164 end
127165128128- # Matches if the value is a `Proc` or responds to `#to_proc`.
129129- def _Procable
130130- Literal::Types::ProcableType
131131- end
132132-133133- # Matches if the value is a `Proc` and `#lambda?` returns truthy.
134134- def _Lambda
135135- Literal::Types::LambdaType
166166+ # Ensures a value matches the given shape of a Hash
167167+ def _Shape(*constraints, **shape)
168168+ Literal::Types::ShapeType.new(*constraints, **shape)
136169 end
137170138171 # Matches if the value is a `String` and matches the given constraint.
···147180 Literal::Types::SymbolType.new(constraint)
148181 end
149182150150- # Matches if the given type is *not* matched.
151151- def _Not(type)
152152- Literal::Types::NotType.new(type)
153153- end
154154-155183 # Matches *"truthy"* values (anything except `nil` and `false`).
156184 def _Truthy
157185 Literal::Types::TruthyType
158186 end
159187160160- # Matches *"falsy"* values (`nil` and `false`).
161161- def _Falsy
162162- Literal::Types::FalsyType
163163- end
188188+ # Matches if the value is an `Array` and each element matches the given types in order.
189189+ def _Tuple(*types)
190190+ raise Literal::ArgumentError.new("_Tuple type must have at least one type.") if types.size < 1
164191165165- # Ensures a value matches the given shape of a Hash
166166- def _Shape(*constraints, **shape)
167167- Literal::Types::ShapeType.new(*constraints, **shape)
192192+ Literal::Types::TupleType.new(*types)
168193 end
169194170170- # Ensures the value is valid JSON data (i.e. it came from JSON.parse).
171171- def _JSONData
172172- Literal::Types::JSONDataType
173173- end
195195+ # Matches if *any* given type is matched.
196196+ def _Union(*types)
197197+ raise Literal::ArgumentError.new("_Union type must have at least one type.") if types.size < 1
174198175175- # Matches if the value is *frozen*.
176176- def _Frozen(type)
177177- Literal::Types::FrozenType.new(type)
178178- end
179179-180180- # Similar to `_Intersection`, but allows you to specify attribute constraints as keyword arguments.
181181- # @example
182182- # _Constraint(Array, size: 1..3)
183183- def _Constraint(*constraints, **attributes)
184184- Literal::Types::ConstraintType.new(*constraints, **attributes)
199199+ Literal::Union.new(*types)
185200 end
186201187202 def _Void
188203 Literal::Types::VoidType
189189- end
190190-191191- def _Map(**shape)
192192- Literal::Types::MapType
193193- end
194194-195195- def _Never
196196- Literal::Types::NeverType
197197- end
198198-199199- def _Descendant(type)
200200- Literal::Types::DescendantType.new(type)
201204 end
202205end
···11-# frozen_string_literal: true
22-33-include Literal::Types
44-55-test "matches any type apart from nil" do
66- Fixtures::Objects.each do |object|
77- assert AnyType === object
88- end
99-1010- refute AnyType === nil
1111-end
+2-8
lib/literal/types/array_type.rb
···8899 def inspect = "_Array(#{@type.inspect})"
10101111- if Literal::EXPENSIVE_TYPE_CHECKS
1212- def ===(value)
1313- Array === value && value.all? { |item| @type === item }
1414- end
1515- else
1616- def ===(value)
1717- Array === value && (value.empty? || @type === value[0])
1818- end
1111+ def ===(value)
1212+ Array === value && value.all? { |item| @type === item }
1913 end
2014end
-16
lib/literal/types/boolean_type.test.rb
···11-# frozen_string_literal: true
22-33-include Literal::Types
44-55-Booleans = Set[true, false]
66-OtherObjects = Fixtures::Objects - Booleans
77-88-test do
99- Booleans.each do |value|
1010- assert BooleanType === value
1111- end
1212-1313- OtherObjects.each do |value|
1414- refute BooleanType === value
1515- end
1616-end
+2-8
lib/literal/types/enumerable_type.rb
···8899 def inspect = "_Enumerable(#{@type.inspect})"
10101111- if Literal::EXPENSIVE_TYPE_CHECKS
1212- def ===(value)
1313- Enumerable === value && value.all? { |item| @type === item }
1414- end
1515- else
1616- def ===(value)
1717- Enumerable === value && (value.empty? || @type === value.first)
1818- end
1111+ def ===(value)
1212+ Enumerable === value && value.all? { |item| @type === item }
1913 end
2014end
-16
lib/literal/types/falsy_type.test.rb
···11-# frozen_string_literal: true
22-33-include Literal::Types
44-55-FalsyObjects = Set[false, nil]
66-TruthyObject = Fixtures::Objects - FalsyObjects
77-88-test do
99- FalsyObjects.each do |object|
1010- assert FalsyType === object
1111- end
1212-1313- TruthyObject.each do |object|
1414- refute FalsyType === object
1515- end
1616-end
+2-8
lib/literal/types/hash_type.rb
···991010 def inspect = "_Hash(#{@key_type.inspect}, #{@value_type.inspect})"
11111212- if Literal::EXPENSIVE_TYPE_CHECKS
1313- def ===(value)
1414- Hash === value && value.all? { |k, v| @key_type === k && @value_type === v }
1515- end
1616- else
1717- def ===(value)
1818- Hash === value && (value.empty? || (@key_type === value.each_key.first && @value_type === value.each_value.first))
1919- end
1212+ def ===(value)
1313+ Hash === value && value.all? { |k, v| @key_type === k && @value_type === v }
2014 end
2115end