literal 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +100 -54
- data/lib/literal/data.rb +24 -11
- data/lib/literal/data_property.rb +16 -0
- data/lib/literal/data_structure.rb +60 -0
- data/lib/literal/enum.rb +176 -0
- data/lib/literal/errors/argument_error.rb +5 -0
- data/lib/literal/errors/error.rb +4 -0
- data/lib/literal/errors/type_error.rb +10 -0
- data/lib/literal/null.rb +9 -0
- data/lib/literal/object.rb +5 -0
- data/lib/literal/properties/data_schema.rb +9 -0
- data/lib/literal/properties/schema.rb +118 -0
- data/lib/literal/properties.rb +91 -0
- data/lib/literal/property.rb +196 -0
- data/lib/literal/struct.rb +8 -34
- data/lib/literal/types/any_type.rb +10 -3
- data/lib/literal/types/array_type.rb +10 -9
- data/lib/literal/types/boolean_type.rb +18 -1
- data/lib/literal/types/callable_type.rb +12 -0
- data/lib/literal/types/class_type.rb +10 -9
- data/lib/literal/types/constraint_type.rb +16 -0
- data/lib/literal/types/descendant_type.rb +13 -0
- data/lib/literal/types/enumerable_type.rb +10 -9
- data/lib/literal/types/falsy_type.rb +12 -0
- data/lib/literal/types/float_type.rb +7 -10
- data/lib/literal/types/frozen_type.rb +14 -0
- data/lib/literal/types/hash_type.rb +11 -10
- data/lib/literal/types/integer_type.rb +7 -10
- data/lib/literal/types/interface_type.rb +11 -9
- data/lib/literal/types/intersection_type.rb +20 -0
- data/lib/literal/types/json_data_type.rb +21 -0
- data/lib/literal/types/lambda_type.rb +12 -0
- data/lib/literal/types/map_type.rb +16 -0
- data/lib/literal/types/never_type.rb +12 -0
- data/lib/literal/types/nilable_type.rb +14 -0
- data/lib/literal/types/not_type.rb +14 -0
- data/lib/literal/types/procable_type.rb +12 -0
- data/lib/literal/types/range_type.rb +20 -0
- data/lib/literal/types/set_type.rb +10 -9
- data/lib/literal/types/string_type.rb +10 -0
- data/lib/literal/types/symbol_type.rb +10 -0
- data/lib/literal/types/truthy_type.rb +12 -0
- data/lib/literal/types/tuple_type.rb +12 -9
- data/lib/literal/types/union_type.rb +43 -9
- data/lib/literal/types/void_type.rb +12 -0
- data/lib/literal/types.rb +195 -54
- data/lib/literal/version.rb +1 -1
- data/lib/literal.rb +28 -10
- data/lib/literal.test.rb +5 -0
- metadata +41 -19
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -29
- data/Rakefile +0 -12
- data/lib/literal/attributes.rb +0 -33
- data/lib/literal/initializer.rb +0 -11
- data/lib/literal/model.rb +0 -22
- data/literal.gemspec +0 -37
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Literal::Properties
|
4
|
+
autoload :Schema, "literal/properties/schema"
|
5
|
+
autoload :DataSchema, "literal/properties/data_schema"
|
6
|
+
|
7
|
+
include Literal::Types
|
8
|
+
|
9
|
+
def prop(name, type, kind = :keyword, reader: false, writer: false, predicate: false, default: nil, &coercion)
|
10
|
+
if default && !(Proc === default || default.frozen?)
|
11
|
+
raise Literal::ArgumentError.new("The default must be a frozen object or a Proc.")
|
12
|
+
end
|
13
|
+
|
14
|
+
unless Literal::Property::VISIBILITY_OPTIONS.include?(reader)
|
15
|
+
raise Literal::ArgumentError.new("The reader must be one of #{Literal::Property::VISIBILITY_OPTIONS.map(&:inspect).join(', ')}.")
|
16
|
+
end
|
17
|
+
|
18
|
+
unless Literal::Property::VISIBILITY_OPTIONS.include?(writer)
|
19
|
+
raise Literal::ArgumentError.new("The writer must be one of #{Literal::Property::VISIBILITY_OPTIONS.map(&:inspect).join(', ')}.")
|
20
|
+
end
|
21
|
+
|
22
|
+
unless Literal::Property::VISIBILITY_OPTIONS.include?(predicate)
|
23
|
+
raise Literal::ArgumentError.new("The predicate must be one of #{Literal::Property::VISIBILITY_OPTIONS.map(&:inspect).join(', ')}.")
|
24
|
+
end
|
25
|
+
|
26
|
+
if reader && :class == name
|
27
|
+
raise Literal::ArgumentError.new(
|
28
|
+
"The `:class` property should not be defined as a reader because it breaks Ruby's `Object#class` method, which Literal itself depends on.",
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
unless Literal::Property::KIND_OPTIONS.include?(kind)
|
33
|
+
raise Literal::ArgumentError.new("The kind must be one of #{Literal::Property::KIND_OPTIONS.map(&:inspect).join(', ')}.")
|
34
|
+
end
|
35
|
+
|
36
|
+
property = __literal_property_class__.new(
|
37
|
+
name:,
|
38
|
+
type:,
|
39
|
+
kind:,
|
40
|
+
reader:,
|
41
|
+
writer:,
|
42
|
+
predicate:,
|
43
|
+
default:,
|
44
|
+
coercion:,
|
45
|
+
)
|
46
|
+
|
47
|
+
literal_properties << property
|
48
|
+
__define_literal_methods__(property)
|
49
|
+
include(__literal_extension__)
|
50
|
+
end
|
51
|
+
|
52
|
+
def literal_properties
|
53
|
+
return @literal_properties if defined?(@literal_properties)
|
54
|
+
|
55
|
+
if superclass.is_a?(Literal::Properties)
|
56
|
+
@literal_properties = superclass.literal_properties.dup
|
57
|
+
else
|
58
|
+
@literal_properties = Literal::Properties::Schema.new
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def __literal_property_class__
|
65
|
+
Literal::Property
|
66
|
+
end
|
67
|
+
|
68
|
+
def __define_literal_methods__(new_property)
|
69
|
+
__literal_extension__.module_eval(
|
70
|
+
__generate_literal_methods__(new_property),
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def __literal_extension__
|
75
|
+
if defined?(@__literal_extension__)
|
76
|
+
@__literal_extension__
|
77
|
+
else
|
78
|
+
@__literal_extension__ = Module.new
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def __generate_literal_methods__(new_property, buffer = +"")
|
83
|
+
buffer << "# frozen_string_literal: true\n"
|
84
|
+
literal_properties.generate_initializer(buffer)
|
85
|
+
literal_properties.generate_to_h(buffer)
|
86
|
+
new_property.generate_writer_method(buffer) if new_property.writer
|
87
|
+
new_property.generate_reader_method(buffer) if new_property.reader
|
88
|
+
new_property.generate_predicate_method(buffer) if new_property.predicate
|
89
|
+
buffer
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Literal::Property
|
4
|
+
ORDER = { :positional => 0, :* => 1, :keyword => 2, :** => 3, :& => 4 }.freeze
|
5
|
+
RUBY_KEYWORDS = %i[alias and begin break case class def do else elsif end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield].to_h { |k| [k, "__#{k}__"] }.freeze
|
6
|
+
|
7
|
+
VISIBILITY_OPTIONS = Set[false, :private, :protected, :public].freeze
|
8
|
+
KIND_OPTIONS = Set[:positional, :*, :keyword, :**, :&].freeze
|
9
|
+
|
10
|
+
include Comparable
|
11
|
+
|
12
|
+
def initialize(name:, type:, kind:, reader:, writer:, predicate:, default:, coercion:)
|
13
|
+
@name = name
|
14
|
+
@type = type
|
15
|
+
@kind = kind
|
16
|
+
@reader = reader
|
17
|
+
@writer = writer
|
18
|
+
@predicate = predicate
|
19
|
+
@default = default
|
20
|
+
@coercion = coercion
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :name, :type, :kind, :reader, :writer, :predicate, :default, :coercion
|
24
|
+
|
25
|
+
def optional?
|
26
|
+
default? || @type === nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def required?
|
30
|
+
!optional?
|
31
|
+
end
|
32
|
+
|
33
|
+
def keyword?
|
34
|
+
@kind == :keyword
|
35
|
+
end
|
36
|
+
|
37
|
+
def positional?
|
38
|
+
@kind == :positional
|
39
|
+
end
|
40
|
+
|
41
|
+
def splat?
|
42
|
+
@kind == :*
|
43
|
+
end
|
44
|
+
|
45
|
+
def double_splat?
|
46
|
+
@kind == :**
|
47
|
+
end
|
48
|
+
|
49
|
+
def block?
|
50
|
+
@kind == :&
|
51
|
+
end
|
52
|
+
|
53
|
+
def default?
|
54
|
+
nil != @default
|
55
|
+
end
|
56
|
+
|
57
|
+
def <=>(other)
|
58
|
+
ORDER[@kind] <=> ORDER[other.kind]
|
59
|
+
end
|
60
|
+
|
61
|
+
def coerce(value, context:)
|
62
|
+
context.instance_exec(value, &@coercion)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ruby_keyword?
|
66
|
+
!!RUBY_KEYWORDS[@name]
|
67
|
+
end
|
68
|
+
|
69
|
+
def escaped_name
|
70
|
+
RUBY_KEYWORDS[@name] || @name.name
|
71
|
+
end
|
72
|
+
|
73
|
+
def default_value
|
74
|
+
case @default
|
75
|
+
when Proc then @default.call
|
76
|
+
else @default
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def check(value)
|
81
|
+
Literal.check(value, @type)
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_reader_method(buffer = +"")
|
85
|
+
buffer <<
|
86
|
+
(@reader ? @reader.name : "public") <<
|
87
|
+
" def " <<
|
88
|
+
@name.name <<
|
89
|
+
"\nvalue = @" <<
|
90
|
+
@name.name <<
|
91
|
+
"\nvalue\nend\n"
|
92
|
+
end
|
93
|
+
|
94
|
+
if Literal::TYPE_CHECKS_DISABLED
|
95
|
+
def generate_writer_method(buffer = +"")
|
96
|
+
buffer <<
|
97
|
+
(@writer ? @writer.name : "public") <<
|
98
|
+
" def " <<
|
99
|
+
@name.name <<
|
100
|
+
"=(value)\n" <<
|
101
|
+
"@#{@name.name} = value\nend\n"
|
102
|
+
end
|
103
|
+
else # type checks are enabled
|
104
|
+
def generate_writer_method(buffer = +"")
|
105
|
+
buffer <<
|
106
|
+
(@writer ? @writer.name : "public") <<
|
107
|
+
" def " <<
|
108
|
+
@name.name <<
|
109
|
+
"=(value)\n" <<
|
110
|
+
"self.class.literal_properties[:" <<
|
111
|
+
@name.name <<
|
112
|
+
"].check(value)\n" <<
|
113
|
+
"@#{@name.name} = value\nend\n"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def generate_predicate_method(buffer = +"")
|
118
|
+
buffer <<
|
119
|
+
(@predicate ? @predicate.name : "public") <<
|
120
|
+
" def " <<
|
121
|
+
@name.name <<
|
122
|
+
"?\n" <<
|
123
|
+
"!!@" <<
|
124
|
+
@name.name <<
|
125
|
+
"\n" <<
|
126
|
+
"end\n"
|
127
|
+
end
|
128
|
+
|
129
|
+
def generate_initializer_handle_property(buffer = +"")
|
130
|
+
buffer << "# " << @name.name << "\n" <<
|
131
|
+
"property = properties[:" << @name.name << "]\n"
|
132
|
+
|
133
|
+
if @kind == :keyword && ruby_keyword?
|
134
|
+
generate_initializer_escape_keyword(buffer)
|
135
|
+
end
|
136
|
+
|
137
|
+
if default?
|
138
|
+
generate_initializer_assign_default(buffer)
|
139
|
+
end
|
140
|
+
|
141
|
+
if @coercion
|
142
|
+
generate_initializer_coerce_property(buffer)
|
143
|
+
end
|
144
|
+
|
145
|
+
unless Literal::TYPE_CHECKS_DISABLED
|
146
|
+
generate_initializer_check_type(buffer)
|
147
|
+
end
|
148
|
+
|
149
|
+
generate_initializer_assign_value(buffer)
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def generate_initializer_escape_keyword(buffer = +"")
|
155
|
+
buffer <<
|
156
|
+
escaped_name <<
|
157
|
+
" = binding.local_variable_get(:" <<
|
158
|
+
@name.name <<
|
159
|
+
")\n"
|
160
|
+
end
|
161
|
+
|
162
|
+
def generate_initializer_coerce_property(buffer = +"")
|
163
|
+
buffer <<
|
164
|
+
escaped_name <<
|
165
|
+
"= property.coerce(" <<
|
166
|
+
escaped_name <<
|
167
|
+
", context: self)\n"
|
168
|
+
end
|
169
|
+
|
170
|
+
def generate_initializer_assign_default(buffer = +"")
|
171
|
+
buffer <<
|
172
|
+
"if " <<
|
173
|
+
((@kind == :&) ? "nil" : "Literal::Null") <<
|
174
|
+
" == " <<
|
175
|
+
escaped_name <<
|
176
|
+
"\n" <<
|
177
|
+
escaped_name <<
|
178
|
+
" = property.default_value\nend\n"
|
179
|
+
end
|
180
|
+
|
181
|
+
def generate_initializer_check_type(buffer = +"")
|
182
|
+
buffer <<
|
183
|
+
"unless property.type === " << escaped_name << "\n" <<
|
184
|
+
"raise Literal::TypeError.expected(" << escaped_name << ", to_be_a: property.type)\n" <<
|
185
|
+
"end\n"
|
186
|
+
end
|
187
|
+
|
188
|
+
def generate_initializer_assign_value(buffer = +"")
|
189
|
+
buffer <<
|
190
|
+
"@" <<
|
191
|
+
@name.name <<
|
192
|
+
" = " <<
|
193
|
+
escaped_name <<
|
194
|
+
"\n"
|
195
|
+
end
|
196
|
+
end
|
data/lib/literal/struct.rb
CHANGED
@@ -1,35 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def self.__attributes__
|
11
|
-
return @__attributes__ if defined?(@__attributes__)
|
12
|
-
@__attributes__ = superclass.is_a?(self) ? superclass.__attributes__.dup : []
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.attribute(name, type, writer: :private)
|
16
|
-
__attributes__ << name
|
17
|
-
|
18
|
-
writer_name = :"#{name}="
|
19
|
-
|
20
|
-
define_method writer_name do |value|
|
21
|
-
raise Literal::TypeError, "Expected #{name}: `#{value.inspect}` to be: `#{type.inspect}`." unless type === value
|
22
|
-
@attributes[name] = value
|
23
|
-
end
|
24
|
-
|
25
|
-
define_method name do
|
26
|
-
@attributes[name]
|
27
|
-
end
|
28
|
-
|
29
|
-
name
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_h
|
33
|
-
@attributes.dup
|
34
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Literal::Struct < Literal::DataStructure
|
4
|
+
class << self
|
5
|
+
def prop(name, type, kind = :keyword, reader: :public, writer: :public, default: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
end
|
35
9
|
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
1
4
|
class Literal::Types::ArrayType
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
+
def initialize(type)
|
6
|
+
@type = type
|
7
|
+
end
|
5
8
|
|
6
|
-
|
7
|
-
"Array(#{@type.inspect})"
|
8
|
-
end
|
9
|
+
def inspect = "_Array(#{@type.inspect})"
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def ===(value)
|
12
|
+
Array === value && value.all? { |item| @type === item }
|
13
|
+
end
|
13
14
|
end
|
@@ -1 +1,18 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module Literal::Types::BooleanType
|
5
|
+
COERCION = proc { |value| !!value }
|
6
|
+
|
7
|
+
def self.inspect = "_Boolean"
|
8
|
+
|
9
|
+
def self.===(value)
|
10
|
+
true == value || false == value
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_proc
|
14
|
+
COERCION
|
15
|
+
end
|
16
|
+
|
17
|
+
freeze
|
18
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
1
4
|
class Literal::Types::ClassType
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
+
def initialize(type)
|
6
|
+
@type = type
|
7
|
+
end
|
5
8
|
|
6
|
-
|
7
|
-
"Class(#{@type.name})"
|
8
|
-
end
|
9
|
+
def inspect = "_Class(#{@type.name})"
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def ===(value)
|
12
|
+
Class === value && (value == @type || value < @type)
|
13
|
+
end
|
13
14
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
class Literal::Types::ConstraintType
|
5
|
+
def initialize(*object_constraints, **property_constraints)
|
6
|
+
@object_constraints = object_constraints
|
7
|
+
@property_constraints = property_constraints
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect = "_Constraint(#{@object_constraints.inspect}, #{@property_constraints.inspect})"
|
11
|
+
|
12
|
+
def ===(value)
|
13
|
+
@object_constraints.all? { |t| t === value } &&
|
14
|
+
@property_constraints.all? { |a, t| t === value.public_send(a) }
|
15
|
+
end
|
16
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
1
4
|
class Literal::Types::EnumerableType
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
+
def initialize(type)
|
6
|
+
@type = type
|
7
|
+
end
|
5
8
|
|
6
|
-
|
7
|
-
"Enumerable(#{@type.inspect})"
|
8
|
-
end
|
9
|
+
def inspect = "_Enumerable(#{@type.inspect})"
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def ===(value)
|
12
|
+
Enumerable === value && value.all? { |item| @type === item }
|
13
|
+
end
|
13
14
|
end
|
@@ -1,13 +1,10 @@
|
|
1
|
-
|
2
|
-
def initialize(range)
|
3
|
-
@range = range
|
4
|
-
end
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
# @api private
|
4
|
+
class Literal::Types::FloatType < Literal::Types::ConstraintType
|
5
|
+
def inspect = "_Float(#{@range})"
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def ===(value)
|
8
|
+
Float === value && super
|
9
|
+
end
|
13
10
|
end
|
@@ -1,14 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
1
4
|
class Literal::Types::HashType
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
def initialize(key_type, value_type)
|
6
|
+
@key_type = key_type
|
7
|
+
@value_type = value_type
|
8
|
+
end
|
6
9
|
|
7
|
-
|
8
|
-
"Hash(#{@key_type.inspect}, #{@value_type.inspect})"
|
9
|
-
end
|
10
|
+
def inspect = "_Hash(#{@key_type.inspect}, #{@value_type.inspect})"
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def ===(value)
|
13
|
+
Hash === value && value.all? { |k, v| @key_type === k && @value_type === v }
|
14
|
+
end
|
14
15
|
end
|
@@ -1,13 +1,10 @@
|
|
1
|
-
|
2
|
-
def initialize(range)
|
3
|
-
@range = range
|
4
|
-
end
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
# @api private
|
4
|
+
class Literal::Types::IntegerType < Literal::Types::ConstraintType
|
5
|
+
def inspect = "_Integer(#{@range})"
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def ===(value)
|
8
|
+
Integer === value && super
|
9
|
+
end
|
13
10
|
end
|
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
1
4
|
class Literal::Types::InterfaceType
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
+
def initialize(*methods)
|
6
|
+
raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
|
7
|
+
@methods = methods
|
8
|
+
end
|
5
9
|
|
6
|
-
|
7
|
-
"Interface(#{@methods.map(&:inspect).join(", ")})"
|
8
|
-
end
|
10
|
+
def inspect = "_Interface(#{@methods.map(&:inspect).join(', ')})"
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
def ===(value)
|
13
|
+
@methods.all? { |m| value.respond_to?(m) }
|
14
|
+
end
|
13
15
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
class Literal::Types::IntersectionType
|
5
|
+
def initialize(*types)
|
6
|
+
raise Literal::ArgumentError.new("_Intersection type must have at least one type.") if types.size < 1
|
7
|
+
|
8
|
+
@types = types
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect = "_Intersection(#{@types.map(&:inspect).join(', ')})"
|
12
|
+
|
13
|
+
def ===(value)
|
14
|
+
@types.all? { |type| type === value }
|
15
|
+
end
|
16
|
+
|
17
|
+
def nil?
|
18
|
+
@types.all?(&:nil?)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module Literal::Types::JSONDataType
|
5
|
+
def self.inspect = "_JSONData"
|
6
|
+
|
7
|
+
def self.===(value)
|
8
|
+
case value
|
9
|
+
when String, Integer, Float, true, false, nil
|
10
|
+
true
|
11
|
+
when Hash
|
12
|
+
value.all? { |k, v| String === k && self === v }
|
13
|
+
when Array
|
14
|
+
value.all?(self)
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
freeze
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
class Literal::Types::MapType
|
5
|
+
def initialize(**shape)
|
6
|
+
@shape = shape
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"_Map(#{@shape.inspect})"
|
11
|
+
end
|
12
|
+
|
13
|
+
def ===(other)
|
14
|
+
@shape.all? { |k, t| t === other[k] }
|
15
|
+
end
|
16
|
+
end
|