Runtime assertions for Ruby literal.fun
ruby
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve types and test coverage for types

+178 -87
+37 -38
lib/literal/types.rb
··· 32 32 autoload :TupleType, "literal/types/tuple_type" 33 33 autoload :VoidType, "literal/types/void_type" 34 34 35 - # Matches any value except `nil`. Use `_Nilable(_Any)` to match any value including `nil`. 35 + # Matches any value except `nil`. Use `_Nilable(_Any)` or `_Void` to match any value including `nil`. 36 36 def _Any 37 37 Literal::Types::AnyType 38 38 end 39 39 40 40 # Matches if the value is an `Array` and all the elements match the given type. 41 - def _Array(type) 42 - Literal::Types::ArrayType.new(type) 41 + def _Array(...) 42 + Literal::Types::ArrayType.new(...) 43 43 end 44 44 45 45 # Matches if the value is `true` or `false`. ··· 48 48 end 49 49 50 50 # Matches if the value responds to `#call`. 51 - def _Callable(type = nil) 51 + def _Callable 52 52 Literal::Types::CallableType 53 53 end 54 54 55 55 # Matches if the value either the given class or a subclass of it. 56 - def _Class(type) 57 - Literal::Types::ClassType.new(type) 56 + def _Class(...) 57 + Literal::Types::ClassType.new(...) 58 58 end 59 59 60 60 # Similar to `_Intersection`, but allows you to specify attribute constraints as keyword arguments. 61 61 # @example 62 62 # _Constraint(Array, size: 1..3) 63 - def _Constraint(*constraints, **attributes) 64 - Literal::Types::ConstraintType.new(*constraints, **attributes) 63 + def _Constraint(...) 64 + Literal::Types::ConstraintType.new(...) 65 65 end 66 66 67 67 # Matches if the value is a descendant of the given class. 68 - def _Descendant(type) 69 - Literal::Types::DescendantType.new(type) 68 + def _Descendant(...) 69 + Literal::Types::DescendantType.new(...) 70 70 end 71 71 72 72 #  Matches if the value is an `Enumerable` and all its elements match the given type. 73 - def _Enumerable(type) 74 - Literal::Types::EnumerableType.new(type) 73 + def _Enumerable(...) 74 + Literal::Types::EnumerableType.new(...) 75 75 end 76 76 77 77 # Matches *"falsy"* values (`nil` and `false`). ··· 82 82 # Matches if the value is a `Float` and matches the given constraint. 83 83 # You could use a `Range`, for example, as a constraint. 84 84 # If you don't need a constraint, use `Float` instead of `_Float`. 85 - def _Float(constraint) 86 - Literal::Types::FloatType.new(constraint) 85 + def _Float(...) 86 + Literal::Types::FloatType.new(...) 87 87 end 88 88 89 89 # Matches if the value is *frozen*. 90 - def _Frozen(type) 91 - Literal::Types::FrozenType.new(type) 90 + def _Frozen(...) 91 + Literal::Types::FrozenType.new(...) 92 92 end 93 93 94 94 # Matches if the value is a `Hash` and all the keys and values match the given types. 95 - def _Hash(key_type, value_type) 96 - Literal::Types::HashType.new(key_type, value_type) 95 + def _Hash(...) 96 + Literal::Types::HashType.new(...) 97 97 end 98 98 99 99 # Matches if the value is an `Integer` and matches the given constraint. ··· 101 101 # If you don't need a constraint, use `Integer` instead of `_Integer`. 102 102 # @example 103 103 # attribute :age, _Integer(18..127) 104 - def _Integer(constraint) 105 - Literal::Types::IntegerType.new(constraint) 104 + def _Integer(...) 105 + Literal::Types::IntegerType.new(...) 106 106 end 107 107 108 108 # Matches if the value responds to all the given methods. ··· 130 130 end 131 131 132 132 def _Map(**shape) 133 - Literal::Types::MapType 133 + Literal::Types::MapType.new(**shape) 134 134 end 135 135 136 136 # Never matches any value. ··· 139 139 end 140 140 141 141 # Matches if the value is either `nil` or the given type. 142 - def _Nilable(type) 143 - Literal::Types::NilableType.new(type) 142 + def _Nilable(...) 143 + Literal::Types::NilableType.new(...) 144 144 end 145 145 146 146 # Matches if the given type is *not* matched. 147 - def _Not(type) 148 - Literal::Types::NotType.new(type) 147 + def _Not(...) 148 + Literal::Types::NotType.new(...) 149 149 end 150 150 151 151 # Matches if the value is a `Proc` or responds to `#to_proc`. ··· 154 154 end 155 155 156 156 # Matches if the value is a `Range` of the given type. 157 - def _Range(type) 158 - Literal::Types::RangeType.new(type) 157 + def _Range(...) 158 + Literal::Types::RangeType.new(...) 159 159 end 160 160 161 161 # Matches if the value is a `Set` and all the elements match the given type. 162 - def _Set(type) 163 - Literal::Types::SetType.new(type) 162 + def _Set(...) 163 + Literal::Types::SetType.new(...) 164 164 end 165 165 166 166 # Ensures a value matches the given shape of a Hash 167 - def _Shape(*constraints, **shape) 168 - Literal::Types::ShapeType.new(*constraints, **shape) 167 + def _Shape(...) 168 + Literal::Types::ShapeType.new(...) 169 169 end 170 170 171 - # Matches if the value is a `String` and matches the given constraint. 172 - # You could use a `Regexp`, for example, as a constraint. 173 - # If you don't need a constraint, use `String` instead of `_String`. 174 - def _String(constraint) 175 - Literal::Types::StringType.new(constraint) 171 + # Matches if the value is a `String` and matches the given constraints. 172 + # If you don't need any constraints, use `String` instead of `_String`. 173 + def _String(...) 174 + Literal::Types::StringType.new(...) 176 175 end 177 176 178 177 # Matches if the value is a `Symbol` and matches the given constraint. 179 - def _Symbol(constraint) 180 - Literal::Types::SymbolType.new(constraint) 178 + def _Symbol(...) 179 + Literal::Types::SymbolType.new(...) 181 180 end 182 181 183 182 # Matches *"truthy"* values (anything except `nil` and `false`).
+126
lib/literal/types.test.rb
··· 150 150 refute type === "string" 151 151 end 152 152 153 + test "_JSONData" do 154 + assert _JSONData === "string" 155 + assert _JSONData === 42 156 + assert _JSONData === 3.14 157 + assert _JSONData === true 158 + assert _JSONData === false 159 + assert _JSONData === nil 160 + assert _JSONData === { "key" => "value", "number" => 42 } 161 + assert _JSONData === [1, "two", 3.0, true, false, nil] 162 + 163 + assert _JSONData === { "nested_array" => [1, "two", { "key" => "value" }] } 164 + assert _JSONData === { "nested_hash" => { "key1" => "value1", "key2" => [1, 2, 3] } } 165 + assert _JSONData === [{ "key" => "value" }, [1, 2, 3], "string"] 166 + 167 + refute _JSONData === Object.new 168 + refute _JSONData === Set.new 169 + refute _JSONData === { key: "value" } 170 + refute _JSONData === [1, :symbol, "three"] 171 + refute _JSONData === { "nested_array" => [1, :symbol, { "key" => "value" }] } 172 + refute _JSONData === { "nested_hash" => { "key1" => "value1", "key2" => [1, :symbol, 3] } } 173 + refute _JSONData === [{ "key" => :value }, [1, 2, 3], "string"] 174 + end 175 + 176 + test "_Lambda" do 177 + assert _Lambda === -> {} 178 + assert _Lambda === -> (arg) { arg } 179 + assert _Lambda === lambda {} 180 + 181 + refute _Lambda === proc {} 182 + end 183 + 184 + test "_Map" do 185 + map = _Map(name: String, age: Integer) 186 + 187 + assert map === { name: "Alice", age: 42 } 188 + assert map === { name: "Bob", age: 18 } 189 + 190 + refute map === { name: "Alice", age: "42" } 191 + refute map === { name: "Bob", age: nil } 192 + refute map === { name: "Charlie" } 193 + refute map === { age: 30 } 194 + end 195 + 153 196 test "_Never" do 154 197 Fixtures::Objects.each do |object| 155 198 refute _Never === object 156 199 end 157 200 158 201 refute _Never === nil 202 + end 203 + 204 + test "_Nilable" do 205 + type = _Nilable(String) 206 + 207 + assert type === "string" 208 + assert type === nil 209 + 210 + refute type === 42 211 + refute type === :symbol 212 + refute type === [] 213 + end 214 + 215 + test "_Not" do 216 + assert _Not(Integer) === "string" 217 + 218 + refute _Not(Integer) === 18 219 + end 220 + 221 + test "_Procable" do 222 + assert _Procable === proc {} 223 + assert _Procable === -> {} 224 + assert _Procable === method(:puts) 225 + 226 + refute _Procable === "string" 227 + refute _Procable === 42 228 + refute _Procable === nil 229 + end 230 + 231 + test "_Range" do 232 + assert _Range(Integer) === (1..10) 233 + assert _Range(Float) === (1.0..10.0) 234 + assert _Range(String) === ("a".."z") 235 + 236 + refute _Range(Integer) === (1.0..10.0) 237 + end 238 + 239 + test "_Set" do 240 + assert _Set(String) === Set["a", "b", "c"] 241 + assert _Set(Integer) === Set[1, 2, 3] 242 + 243 + refute _Set(String) === Set[1, "a", :symbol] 244 + refute _Set(Integer) === Set["a", "b", "c"] 245 + refute _Set(String) === ["a", "b", "c"] 246 + end 247 + 248 + test "_String" do 249 + assert _String(_Interface(:to_s)) === "string" 250 + assert _String(size: 6) === "string" 251 + 252 + refute _String(_Interface(:non_existing_method)) === "string" 253 + refute _String(size: 5) === "string" 254 + end 255 + 256 + test "_Symbol" do 257 + assert _Symbol(_Interface(:to_sym)) === :symbol 258 + assert _Symbol(size: 6) === :symbol 259 + 260 + refute _Symbol(_Interface(:non_existing_method)) === :symbol 261 + refute _Symbol(size: 5) === :symbol 262 + end 263 + 264 + test "_Truthy" do 265 + truthy_objects = Fixtures::Objects - Set[false, nil] 266 + falsy_objects = Set[false, nil] 267 + 268 + truthy_objects.each do |object| 269 + assert _Truthy === object 270 + end 271 + 272 + falsy_objects.each do |object| 273 + refute _Truthy === object 274 + end 275 + end 276 + 277 + test "_Tuple" do 278 + assert _Tuple(String, Integer) === ["a", 1] 279 + assert _Tuple(Symbol, Float) === [:symbol, 3.14] 280 + 281 + refute _Tuple(String, Integer) === [1, "a"] 282 + refute _Tuple(String, Integer) === ["a", 1, 2] 283 + refute _Tuple(String, Integer) === ["a"] 284 + refute _Tuple(String, Integer) === nil 159 285 end 160 286 161 287 test "_Void" do
+6 -7
lib/literal/types/constraint_type.rb
··· 2 2 3 3 # @api private 4 4 class Literal::Types::ConstraintType 5 - def initialize(*constraints, **attributes) 6 - @constraints = constraints 7 - @attributes = attributes 5 + def initialize(*object_constraints, **attribute_constraints) 6 + @object_constraints = object_constraints 7 + @attribute_constraints = attribute_constraints 8 8 end 9 9 10 - def inspect = "_Constraint(#{@constraints.inspect}, #{@attributes.inspect})" 10 + def inspect = "_Constraint(#{@object_constraints.inspect}, #{@attribute_constraints.inspect})" 11 11 12 12 def ===(value) 13 - @constraints.all? { |c| c === value } && @attributes.all? do |method, type| 14 - type === value.public_send(method) 15 - end 13 + @object_constraints.all? { |t| t === value } && 14 + @attribute_constraints.all? { |a, t| t === value.public_send(a) } 16 15 end 17 16 end
+2 -6
lib/literal/types/float_type.rb
··· 1 1 # frozen_string_literal: true 2 2 3 3 # @api private 4 - class Literal::Types::FloatType 5 - def initialize(range) 6 - @range = range 7 - end 8 - 4 + class Literal::Types::FloatType < Literal::Types::ConstraintType 9 5 def inspect = "_Float(#{@range})" 10 6 11 7 def ===(value) 12 - Float === value && @range === value 8 + Float === value && super 13 9 end 14 10 end
+2 -6
lib/literal/types/integer_type.rb
··· 1 1 # frozen_string_literal: true 2 2 3 3 # @api private 4 - class Literal::Types::IntegerType 5 - def initialize(range) 6 - @range = range 7 - end 8 - 4 + class Literal::Types::IntegerType < Literal::Types::ConstraintType 9 5 def inspect = "_Integer(#{@range})" 10 6 11 7 def ===(value) 12 - Integer === value && @range === value 8 + Integer === value && super 13 9 end 14 10 end
-17
lib/literal/types/shape_type.rb
··· 1 - # frozen_string_literal: true 2 - 3 - # @api private 4 - class Literal::Types::ShapeType 5 - def initialize(*constraints, **shape) 6 - @constraints = constraints 7 - @shape = shape 8 - end 9 - 10 - def inspect 11 - "_Shape(#{@shape.inspect})" 12 - end 13 - 14 - def ===(other) 15 - @constraints.all? { |c| c === other } && @shape.all? { |k, t| t === other[k] } && other.keys.all? { |k| @shape.key?(k) } 16 - end 17 - end
+2 -6
lib/literal/types/string_type.rb
··· 1 1 # frozen_string_literal: true 2 2 3 3 # @api private 4 - class Literal::Types::StringType 5 - def initialize(constraint) 6 - @constraint = constraint 7 - end 8 - 4 + class Literal::Types::StringType < Literal::Types::ConstraintType 9 5 def inspect = "_String(#{@constraint.inspect})" 10 6 11 7 def ===(value) 12 - String === value && @constraint === value 8 + String === value && super 13 9 end 14 10 end
+2 -6
lib/literal/types/symbol_type.rb
··· 1 1 # frozen_string_literal: true 2 2 3 3 # @api private 4 - class Literal::Types::SymbolType 5 - def initialize(constraint) 6 - @constraint = constraint 7 - end 8 - 4 + class Literal::Types::SymbolType < Literal::Types::ConstraintType 9 5 def inspect = "_Symbol(#{@constraint.inspect})" 10 6 11 7 def ===(value) 12 - Symbol === value && @constraint === value 8 + Symbol === value && super 13 9 end 14 10 end
+1 -1
lib/literal/types/void_type.rb
··· 8 8 9 9 def inspect = "_Void" 10 10 11 - def ===(_value) 11 + def ===(_) 12 12 true 13 13 end 14 14 end