sorbet-runtime 0.0.1.pre.prealpha → 0.4.4253
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/sorbet-runtime.rb +100 -0
- data/lib/types/_types.rb +245 -0
- data/lib/types/abstract_utils.rb +50 -0
- data/lib/types/boolean.rb +8 -0
- data/lib/types/compatibility_patches.rb +37 -0
- data/lib/types/configuration.rb +368 -0
- data/lib/types/generic.rb +23 -0
- data/lib/types/helpers.rb +31 -0
- data/lib/types/interface_wrapper.rb +158 -0
- data/lib/types/private/abstract/data.rb +36 -0
- data/lib/types/private/abstract/declare.rb +39 -0
- data/lib/types/private/abstract/hooks.rb +43 -0
- data/lib/types/private/abstract/validate.rb +128 -0
- data/lib/types/private/casts.rb +22 -0
- data/lib/types/private/class_utils.rb +102 -0
- data/lib/types/private/decl_state.rb +18 -0
- data/lib/types/private/error_handler.rb +37 -0
- data/lib/types/private/methods/_methods.rb +344 -0
- data/lib/types/private/methods/call_validation.rb +1177 -0
- data/lib/types/private/methods/decl_builder.rb +275 -0
- data/lib/types/private/methods/modes.rb +18 -0
- data/lib/types/private/methods/signature.rb +196 -0
- data/lib/types/private/methods/signature_validation.rb +232 -0
- data/lib/types/private/mixins/mixins.rb +27 -0
- data/lib/types/private/runtime_levels.rb +41 -0
- data/lib/types/private/types/not_typed.rb +23 -0
- data/lib/types/private/types/string_holder.rb +26 -0
- data/lib/types/private/types/void.rb +33 -0
- data/lib/types/profile.rb +27 -0
- data/lib/types/props/_props.rb +165 -0
- data/lib/types/props/constructor.rb +20 -0
- data/lib/types/props/custom_type.rb +84 -0
- data/lib/types/props/decorator.rb +826 -0
- data/lib/types/props/errors.rb +8 -0
- data/lib/types/props/optional.rb +73 -0
- data/lib/types/props/plugin.rb +15 -0
- data/lib/types/props/pretty_printable.rb +106 -0
- data/lib/types/props/serializable.rb +376 -0
- data/lib/types/props/type_validation.rb +98 -0
- data/lib/types/props/utils.rb +49 -0
- data/lib/types/props/weak_constructor.rb +30 -0
- data/lib/types/runtime_profiled.rb +36 -0
- data/lib/types/sig.rb +28 -0
- data/lib/types/struct.rb +8 -0
- data/lib/types/types/base.rb +141 -0
- data/lib/types/types/class_of.rb +38 -0
- data/lib/types/types/enum.rb +42 -0
- data/lib/types/types/fixed_array.rb +60 -0
- data/lib/types/types/fixed_hash.rb +59 -0
- data/lib/types/types/intersection.rb +36 -0
- data/lib/types/types/noreturn.rb +25 -0
- data/lib/types/types/proc.rb +51 -0
- data/lib/types/types/self_type.rb +31 -0
- data/lib/types/types/simple.rb +33 -0
- data/lib/types/types/type_member.rb +7 -0
- data/lib/types/types/type_parameter.rb +23 -0
- data/lib/types/types/type_template.rb +7 -0
- data/lib/types/types/type_variable.rb +31 -0
- data/lib/types/types/typed_array.rb +20 -0
- data/lib/types/types/typed_enumerable.rb +141 -0
- data/lib/types/types/typed_enumerator.rb +22 -0
- data/lib/types/types/typed_hash.rb +29 -0
- data/lib/types/types/typed_range.rb +22 -0
- data/lib/types/types/typed_set.rb +22 -0
- data/lib/types/types/union.rb +59 -0
- data/lib/types/types/untyped.rb +25 -0
- data/lib/types/utils.rb +223 -0
- metadata +122 -15
@@ -0,0 +1,232 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Private::Methods::SignatureValidation
|
5
|
+
Methods = T::Private::Methods
|
6
|
+
Modes = Methods::Modes
|
7
|
+
|
8
|
+
def self.validate(signature)
|
9
|
+
if signature.method_name == :initialize && signature.method.owner.is_a?(Class)
|
10
|
+
# Constructors are special. They look like overrides in terms of a super_method existing,
|
11
|
+
# but in practice, you never call them polymorphically. Conceptually, they're standard
|
12
|
+
# methods (this is consistent with how they're treated in other languages, e.g. Java)
|
13
|
+
if signature.mode != Modes.standard
|
14
|
+
raise "Constructor methods should always be declared using `sig`."
|
15
|
+
end
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
super_method = signature.method.super_method
|
20
|
+
|
21
|
+
if super_method && super_method.owner != signature.method.owner
|
22
|
+
Methods.maybe_run_sig_block_for_method(super_method)
|
23
|
+
super_signature = Methods.signature_for_method(super_method)
|
24
|
+
|
25
|
+
# If the super_method has any kwargs we can't build a
|
26
|
+
# Signature for it, so we'll just skip validation in that case.
|
27
|
+
if !super_signature && !super_method.parameters.select {|kind, _| kind == :rest || kind == :kwrest}.empty?
|
28
|
+
nil
|
29
|
+
else
|
30
|
+
# super_signature can be nil when we're overriding a method (perhaps a builtin) that didn't use
|
31
|
+
# one of the method signature helpers. Use an untyped signature so we can still validate
|
32
|
+
# everything but types.
|
33
|
+
#
|
34
|
+
# We treat these signatures as overridable, that way people can use `.override` with
|
35
|
+
# overrides of builtins. In the future we could try to distinguish when the method is a
|
36
|
+
# builtin and treat non-builtins as non-overridable (so you'd be forced to declare them with
|
37
|
+
# `.overridable`).
|
38
|
+
#
|
39
|
+
super_signature ||= Methods::Signature.new_untyped(method: super_method)
|
40
|
+
|
41
|
+
validate_override_mode(signature, super_signature)
|
42
|
+
validate_override_shape(signature, super_signature)
|
43
|
+
validate_override_types(signature, super_signature)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
validate_non_override_mode(signature)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private_class_method def self.pretty_mode(signature)
|
51
|
+
if signature.mode == Modes.overridable_implementation
|
52
|
+
'.overridable.implementation'
|
53
|
+
else
|
54
|
+
".#{signature.mode}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.validate_override_mode(signature, super_signature)
|
59
|
+
case signature.mode
|
60
|
+
when *Modes::OVERRIDE_MODES
|
61
|
+
if !Modes::OVERRIDABLE_MODES.include?(super_signature.mode)
|
62
|
+
raise "You declared `#{signature.method_name}` as #{pretty_mode(signature)}, but the method it overrides is not declared as `overridable`.\n" \
|
63
|
+
" Parent definition: #{method_loc_str(super_signature.method)}\n" \
|
64
|
+
" Child definition: #{method_loc_str(signature.method)}\n"
|
65
|
+
end
|
66
|
+
when *Modes::IMPLEMENT_MODES
|
67
|
+
if super_signature.mode != Modes.abstract
|
68
|
+
raise "You declared `#{signature.method_name}` as #{pretty_mode(signature)}, but the method it overrides is not declared as abstract.\n" \
|
69
|
+
" Either mark #{super_signature.method_name} as `abstract.` in the parent: #{method_loc_str(super_signature.method)}\n" \
|
70
|
+
" ... or mark #{signature.method_name} as `override.` in the child: #{method_loc_str(signature.method)}\n"
|
71
|
+
end
|
72
|
+
when *Modes::NON_OVERRIDE_MODES
|
73
|
+
if super_signature.mode == Modes.standard
|
74
|
+
# Peaceful
|
75
|
+
elsif super_signature.mode == Modes.abstract
|
76
|
+
raise "You must use `.implementation` when overriding the abstract method `#{signature.method_name}`.\n" \
|
77
|
+
" Abstract definition: #{method_loc_str(super_signature.method)}\n" \
|
78
|
+
" Implementation definition: #{method_loc_str(signature.method)}\n"
|
79
|
+
elsif super_signature.mode != Modes.untyped
|
80
|
+
raise "You must use `.override` when overriding the existing method `#{signature.method_name}`.\n" \
|
81
|
+
" Parent definition: #{method_loc_str(super_signature.method)}\n" \
|
82
|
+
" Child definition: #{method_loc_str(signature.method)}\n"
|
83
|
+
end
|
84
|
+
else
|
85
|
+
raise "Unexpected mode: #{signature.mode}. Please report to #dev-productivity."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.validate_non_override_mode(signature)
|
90
|
+
case signature.mode
|
91
|
+
when Modes.override
|
92
|
+
raise "You marked `#{signature.method_name}` as #{pretty_mode(signature)}, but that method doesn't already exist in this class/module to be overriden.\n" \
|
93
|
+
" Either check for typos and for missing includes or super classes to make the parent method shows up\n" \
|
94
|
+
" ... or remove #{pretty_mode(signature)} here: #{method_loc_str(signature.method)}\n"
|
95
|
+
when Modes.standard, *Modes::NON_OVERRIDE_MODES
|
96
|
+
# Peaceful
|
97
|
+
nil
|
98
|
+
when *Modes::IMPLEMENT_MODES
|
99
|
+
raise "You marked `#{signature.method_name}` as #{pretty_mode(signature)}, but it doesn't match up with a corresponding abstract method.\n" \
|
100
|
+
" Either check for typos and for missing includes or super classes to make the parent method shows up\n" \
|
101
|
+
" ... or remove #{pretty_mode(signature)} here: #{method_loc_str(signature.method)}\n"
|
102
|
+
else
|
103
|
+
raise "Unexpected mode: #{signature.mode}. Please report to #dev-productivity."
|
104
|
+
end
|
105
|
+
|
106
|
+
owner = signature.method.owner
|
107
|
+
if (signature.mode == Modes.abstract || Modes::OVERRIDABLE_MODES.include?(signature.mode)) &&
|
108
|
+
owner.singleton_class?
|
109
|
+
# Given a singleton class, we can check if it belongs to a
|
110
|
+
# module by looking at its superclass; given `module M`,
|
111
|
+
# `M.singleton_class.superclass == Module`, which is not true
|
112
|
+
# for any class.
|
113
|
+
if owner.superclass == Module
|
114
|
+
raise "Defining an overridable class method (via #{pretty_mode(signature)}) " \
|
115
|
+
"on a module is not allowed. Class methods on " \
|
116
|
+
"modules do not get inherited and thus cannot be overridden. For help, ask in " \
|
117
|
+
"#dev-productivity."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.validate_override_shape(signature, super_signature)
|
123
|
+
return if signature.override_allow_incompatible
|
124
|
+
return if super_signature.mode == Modes.untyped
|
125
|
+
|
126
|
+
method_name = signature.method_name
|
127
|
+
mode_verb = super_signature.mode == Modes.abstract ? 'implements' : 'overrides'
|
128
|
+
|
129
|
+
if !signature.has_rest && signature.arg_count < super_signature.arg_count
|
130
|
+
raise "Your definition of `#{method_name}` must accept at least #{super_signature.arg_count} " \
|
131
|
+
"positional arguments to be compatible with the method it #{mode_verb}: " \
|
132
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if !signature.has_rest && super_signature.has_rest
|
136
|
+
raise "Your definition of `#{method_name}` must have `*#{super_signature.rest_name}` " \
|
137
|
+
"to be compatible with the method it #{mode_verb}: " \
|
138
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
139
|
+
end
|
140
|
+
|
141
|
+
if signature.req_arg_count > super_signature.req_arg_count
|
142
|
+
raise "Your definition of `#{method_name}` must have no more than #{super_signature.req_arg_count} " \
|
143
|
+
"required argument(s) to be compatible with the method it #{mode_verb}: " \
|
144
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
145
|
+
end
|
146
|
+
|
147
|
+
if !signature.has_keyrest
|
148
|
+
# O(nm), but n and m are tiny here
|
149
|
+
missing_kwargs = super_signature.kwarg_names - signature.kwarg_names
|
150
|
+
if !missing_kwargs.empty?
|
151
|
+
raise "Your definition of `#{method_name}` is missing these keyword arg(s): #{missing_kwargs} " \
|
152
|
+
"which are defined in the method it #{mode_verb}: " \
|
153
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
if !signature.has_keyrest && super_signature.has_keyrest
|
158
|
+
raise "Your definition of `#{method_name}` must have `**#{super_signature.keyrest_name}` " \
|
159
|
+
"to be compatible with the method it #{mode_verb}: " \
|
160
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# O(nm), but n and m are tiny here
|
165
|
+
extra_req_kwargs = signature.req_kwarg_names - super_signature.req_kwarg_names
|
166
|
+
if !extra_req_kwargs.empty?
|
167
|
+
raise "Your definition of `#{method_name}` has extra required keyword arg(s) " \
|
168
|
+
"#{extra_req_kwargs} relative to the method it #{mode_verb}, making it incompatible: " \
|
169
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
170
|
+
end
|
171
|
+
|
172
|
+
if super_signature.block_name && !signature.block_name
|
173
|
+
raise "Your definition of `#{method_name}` must accept a block parameter to be compatible " \
|
174
|
+
"with the method it #{mode_verb}: " \
|
175
|
+
"#{base_override_loc_str(signature, super_signature)}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.validate_override_types(signature, super_signature)
|
180
|
+
return if signature.override_allow_incompatible
|
181
|
+
return if super_signature.mode == Modes.untyped
|
182
|
+
return unless [signature, super_signature].all? do |sig|
|
183
|
+
sig.check_level == :always || (sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
|
184
|
+
end
|
185
|
+
mode_noun = super_signature.mode == Modes.abstract ? 'implementation' : 'override'
|
186
|
+
|
187
|
+
# arg types must be contravariant
|
188
|
+
super_signature.arg_types.zip(signature.arg_types).each_with_index do |((_super_name, super_type), (name, type)), index|
|
189
|
+
if !super_type.subtype_of?(type)
|
190
|
+
raise "Incompatible type for arg ##{index + 1} (`#{name}`) in #{mode_noun} of method " \
|
191
|
+
"`#{signature.method_name}`:\n" \
|
192
|
+
"* Base: `#{super_type}` (in #{method_loc_str(super_signature.method)})\n" \
|
193
|
+
"* #{mode_noun.capitalize}: `#{type}` (in #{method_loc_str(signature.method)})\n" \
|
194
|
+
"(The types must be contravariant.)"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# kwarg types must be contravariant
|
199
|
+
super_signature.kwarg_types.each do |name, super_type|
|
200
|
+
type = signature.kwarg_types[name]
|
201
|
+
if !super_type.subtype_of?(type)
|
202
|
+
raise "Incompatible type for arg `#{name}` in #{mode_noun} of method `#{signature.method_name}`:\n" \
|
203
|
+
"* Base: `#{super_type}` (in #{method_loc_str(super_signature.method)})\n" \
|
204
|
+
"* #{mode_noun.capitalize}: `#{type}` (in #{method_loc_str(signature.method)})\n" \
|
205
|
+
"(The types must be contravariant.)"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# return types must be covariant
|
210
|
+
if !signature.return_type.subtype_of?(super_signature.return_type)
|
211
|
+
raise "Incompatible return type in #{mode_noun} of method `#{signature.method_name}`:\n" \
|
212
|
+
"* Base: `#{super_signature.return_type}` (in #{method_loc_str(super_signature.method)})\n" \
|
213
|
+
"* #{mode_noun.capitalize}: `#{signature.return_type}` (in #{method_loc_str(signature.method)})\n" \
|
214
|
+
"(The types must be covariant.)"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
private_class_method def self.base_override_loc_str(signature, super_signature)
|
219
|
+
mode_noun = super_signature.mode == Modes.abstract ? 'Implementation' : 'Override'
|
220
|
+
"\n * Base definition: in #{method_loc_str(super_signature.method)}" \
|
221
|
+
"\n * #{mode_noun}: in #{method_loc_str(signature.method)}"
|
222
|
+
end
|
223
|
+
|
224
|
+
private_class_method def self.method_loc_str(method)
|
225
|
+
if method.source_location
|
226
|
+
loc = method.source_location.join(':')
|
227
|
+
else
|
228
|
+
loc = "<unknown location>"
|
229
|
+
end
|
230
|
+
"#{method.owner} at #{loc}"
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Private
|
5
|
+
module MixesInClassMethods
|
6
|
+
def included(other)
|
7
|
+
mod = Abstract::Data.get(self, :class_methods_mixin)
|
8
|
+
other.extend(mod)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Mixins
|
14
|
+
def self.declare_mixes_in_class_methods(mixin, class_methods)
|
15
|
+
if mixin.is_a?(Class)
|
16
|
+
raise "Classes cannot be used as mixins, and so mixes_in_class_methods cannot be used on a Class."
|
17
|
+
end
|
18
|
+
|
19
|
+
if Abstract::Data.key?(mixin, :class_methods_mixin)
|
20
|
+
raise "mixes_in_class_methods can only be used once per module."
|
21
|
+
end
|
22
|
+
|
23
|
+
mixin.singleton_class.include(MixesInClassMethods)
|
24
|
+
Abstract::Data.set(mixin, :class_methods_mixin, class_methods)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# Used in `sig.checked(level)` to determine when runtime type checking
|
5
|
+
# is enabled on a method.
|
6
|
+
module T::Private::RuntimeLevels
|
7
|
+
LEVELS = [
|
8
|
+
# Validate every call in every environment
|
9
|
+
:always,
|
10
|
+
# Validate in tests, but not in production
|
11
|
+
:tests,
|
12
|
+
# Don't even validate in tests, b/c too expensive,
|
13
|
+
# or b/c we fully trust the static typing
|
14
|
+
:never,
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
@check_tests = false
|
18
|
+
@wrapped_tests_with_validation = false
|
19
|
+
|
20
|
+
def self.check_tests?
|
21
|
+
# Assume that this code path means that some `sig.checked(:tests)`
|
22
|
+
# has been wrapped (or not wrapped) already, which is a trapdoor
|
23
|
+
# for toggling `@check_tests`.
|
24
|
+
@wrapped_tests_with_validation = true
|
25
|
+
|
26
|
+
@check_tests
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.enable_checking_in_tests
|
30
|
+
if !@check_tests && @wrapped_tests_with_validation
|
31
|
+
raise "Toggle `:tests`-level runtime type checking earlier. " \
|
32
|
+
"There are already some methods wrapped with `sig.checked(:tests)`." \
|
33
|
+
end
|
34
|
+
|
35
|
+
_toggle_checking_tests(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self._toggle_checking_tests(checked)
|
39
|
+
@check_tests = checked
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# A placeholder for when an untyped thing must provide a type.
|
5
|
+
# Raises an exception if it is ever used for validation.
|
6
|
+
class T::Private::Types::NotTyped < T::Types::Base
|
7
|
+
ERROR_MESSAGE = "Validation is being done on a `NotTyped`. Please report to #dev-productivity."
|
8
|
+
|
9
|
+
# @override Base
|
10
|
+
def name
|
11
|
+
"<NOT-TYPED>"
|
12
|
+
end
|
13
|
+
|
14
|
+
# @override Base
|
15
|
+
def valid?(obj)
|
16
|
+
raise ERROR_MESSAGE
|
17
|
+
end
|
18
|
+
|
19
|
+
# @override Base
|
20
|
+
private def subtype_of_single?(other)
|
21
|
+
raise ERROR_MESSAGE
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# Holds a string. Useful for showing type aliases in error messages
|
5
|
+
class T::Private::Types::StringHolder < T::Types::Base
|
6
|
+
attr_reader :string
|
7
|
+
|
8
|
+
def initialize(string)
|
9
|
+
@string = string
|
10
|
+
end
|
11
|
+
|
12
|
+
# @override Base
|
13
|
+
def name
|
14
|
+
string
|
15
|
+
end
|
16
|
+
|
17
|
+
# @override Base
|
18
|
+
def valid?(obj)
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
# @override Base
|
23
|
+
private def subtype_of_single?(other)
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# A marking class for when methods return void.
|
5
|
+
# Should never appear in types directly.
|
6
|
+
class T::Private::Types::Void < T::Types::Base
|
7
|
+
ERROR_MESSAGE = "Validation is being done on an `Void`. Please report to #dev-productivity."
|
8
|
+
|
9
|
+
# The actual return value of `.void` methods.
|
10
|
+
#
|
11
|
+
# Uses `Module.new` because in Ruby an anonymous module will inherit the name
|
12
|
+
# of the constant it's assigned to. This gives it a readable name someone
|
13
|
+
# examines it in Pry or with `#inspect` like:
|
14
|
+
#
|
15
|
+
# T::Private::Types::Void::VOID
|
16
|
+
#
|
17
|
+
VOID = Module.new.freeze
|
18
|
+
|
19
|
+
# @override Base
|
20
|
+
def name
|
21
|
+
"<VOID>"
|
22
|
+
end
|
23
|
+
|
24
|
+
# @override Base
|
25
|
+
def valid?(obj)
|
26
|
+
raise ERROR_MESSAGE
|
27
|
+
end
|
28
|
+
|
29
|
+
# @override Base
|
30
|
+
private def subtype_of_single?(other)
|
31
|
+
raise ERROR_MESSAGE
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module T::Profile
|
5
|
+
SAMPLE_RATE = 101 # 1 out of that many typechecks will be measured
|
6
|
+
class <<self
|
7
|
+
attr_accessor :typecheck_duration
|
8
|
+
attr_accessor :typecheck_samples
|
9
|
+
attr_accessor :typecheck_sample_attempts
|
10
|
+
def typecheck_duration_estimate
|
11
|
+
total_typechecks = typecheck_samples * SAMPLE_RATE + (SAMPLE_RATE - typecheck_sample_attempts)
|
12
|
+
typechecks_measured = typecheck_samples * SAMPLE_RATE
|
13
|
+
typecheck_duration * SAMPLE_RATE * 1.0 * total_typechecks / typechecks_measured
|
14
|
+
end
|
15
|
+
|
16
|
+
def typecheck_count_estimate
|
17
|
+
typecheck_samples * SAMPLE_RATE
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@typecheck_duration = 0
|
22
|
+
@typecheck_samples = 0
|
23
|
+
@typecheck_sample_attempts = SAMPLE_RATE
|
24
|
+
end
|
25
|
+
end
|
26
|
+
self.reset
|
27
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
# A mixin for defining typed properties (attributes).
|
5
|
+
# To get serialization methods (to/from JSON-style hashes), add T::Props::Serializable.
|
6
|
+
# To get a constructor based on these properties, inherit from T::Struct.
|
7
|
+
module T::Props
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
#####
|
11
|
+
# CAUTION: This mixin is used in hundreds of classes; we want to keep its surface area as narrow
|
12
|
+
# as possible and avoid polluting (and possibly conflicting with) the classes that use it.
|
13
|
+
#
|
14
|
+
# It currently has *zero* instance methods; let's try to keep it that way.
|
15
|
+
# For ClassMethods (below), try to add things to T::Props::Decorator instead unless you are sure
|
16
|
+
# it needs to be exposed here.
|
17
|
+
#####
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
extend T::Sig
|
21
|
+
extend T::Helpers
|
22
|
+
|
23
|
+
def props; decorator.props; end
|
24
|
+
def plugins; @plugins ||= []; end
|
25
|
+
|
26
|
+
def decorator_class; Decorator; end
|
27
|
+
|
28
|
+
def decorator; @decorator ||= decorator_class.new(self); end
|
29
|
+
def reload_decorator!; @decorator = decorator_class.new(self); end
|
30
|
+
|
31
|
+
# @!method self.prop(name, type, opts={})
|
32
|
+
#
|
33
|
+
# Define a new property. See {file:README.md} for some concrete
|
34
|
+
# examples.
|
35
|
+
#
|
36
|
+
# Defining a property defines a method with the same name as the
|
37
|
+
# property, that returns the current value, and a `prop=` method
|
38
|
+
# to set its value. Properties will be inherited by subclasses of
|
39
|
+
# a document class.
|
40
|
+
#
|
41
|
+
# @param name [Symbol] The name of this property
|
42
|
+
# @param cls [Class,T::Props::CustomType] (String) The type of this
|
43
|
+
# property. If the type is itself a {Document} subclass, this
|
44
|
+
# property will be recursively serialized/deserialized.
|
45
|
+
# @param rules [Hash] Options to control this property's behavior.
|
46
|
+
# @option rules [T::Boolean,Symbol] :optional If `true`, this property
|
47
|
+
# is never required to be set before an instance is serialized.
|
48
|
+
# If `:on_load` (default), when this property is missing or nil, a
|
49
|
+
# new model cannot be saved, and an existing model can only be
|
50
|
+
# saved if the property was already missing when it was loaded.
|
51
|
+
# If `false`, when the property is missing/nil after deserialization, it
|
52
|
+
# will be set to the default value (as defined by the `default` or
|
53
|
+
# `factory` option) or will raise if they are not present.
|
54
|
+
# Deprecated: For {Model}s, if `:optional` is set to the special value
|
55
|
+
# `:existing`, the property can be saved as nil even if it was
|
56
|
+
# deserialized with a non-nil value. (Deprecated because there should
|
57
|
+
# never be a need for this behavior; the new behavior of non-optional
|
58
|
+
# properties should be sufficient.)
|
59
|
+
# @option rules [Array] :enum An array of legal values; The
|
60
|
+
# property is required to take on one of those values.
|
61
|
+
# @option rules [T::Boolean] :dont_store If true, this property will
|
62
|
+
# not be saved on the hash resulting from {#serialize}
|
63
|
+
# @option rules [Object] :ifunset A value to be returned if this
|
64
|
+
# property is requested but has never been set (is set to
|
65
|
+
# `nil`). It is applied at property-access time, and never saved
|
66
|
+
# back onto the object or into the database.
|
67
|
+
#
|
68
|
+
# ``:ifunset`` is considered **DEPRECATED** and should not be used
|
69
|
+
# in new code, in favor of just setting a default value.
|
70
|
+
# @option rules [Model, Symbol, Proc] :foreign A model class that this
|
71
|
+
# property is a reference to. Passing `:foreign` will define a
|
72
|
+
# `:"#{name}_"` method, that will load and return the
|
73
|
+
# corresponding foreign model.
|
74
|
+
#
|
75
|
+
# A symbol can be passed to avoid load-order dependencies; It
|
76
|
+
# will be lazily resolved relative to the enclosing module of the
|
77
|
+
# defining class.
|
78
|
+
#
|
79
|
+
# A callable (proc or method) can be passed to dynamically specify the
|
80
|
+
# foreign model. This will be passed the object instance so that other
|
81
|
+
# properties of the object can be used to determine the relevant model
|
82
|
+
# class. It should return a string/symbol class name or the foreign model
|
83
|
+
# class directly.
|
84
|
+
#
|
85
|
+
# @option rules [Object] :default A default value that will be set
|
86
|
+
# by {#initialize} if none is provided in the initialization
|
87
|
+
# hash. This will not affect objects loaded by {.from_hash}.
|
88
|
+
# @option rules [Proc] :factory A `Proc` that will be called to
|
89
|
+
# generate an initial value for this prop on {#initialize}, if
|
90
|
+
# none is providede.
|
91
|
+
# @option rules [T::Boolean] :immutable If true, this prop cannot be
|
92
|
+
# modified after an instance is created or loaded from a hash.
|
93
|
+
# @option rules [Class,T::Props::CustomType] :array If set, specifies the
|
94
|
+
# type of individual members of a prop with `type` `Array`. This
|
95
|
+
# allows for typechecking of arrays, and also for recursive
|
96
|
+
# serialization of arrays of sub-{Document}s.
|
97
|
+
# @option rules [T::Boolean] :override It is an error to redeclare a
|
98
|
+
# `prop` that has already been declared (including on a
|
99
|
+
# superclass), unless `:override` is set to `true`.
|
100
|
+
# @option rules [Symbol, Array] :redaction A redaction directive that may
|
101
|
+
# be passed to Chalk::Tools::RedactionUtils.redact_with_directive to
|
102
|
+
# sanitize this parameter for display. Will define a
|
103
|
+
# `:"#{name}_redacted"` method, which will return the value in sanitized
|
104
|
+
# form.
|
105
|
+
#
|
106
|
+
# @return [void]
|
107
|
+
def prop(name, cls, rules={})
|
108
|
+
cls = T::Utils.coerce(cls) if !cls.is_a?(Module)
|
109
|
+
decorator.prop_defined(name, cls, rules)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @!method validate_prop_value(propname, value)
|
113
|
+
#
|
114
|
+
# Validates the value of the specified prop. This method allows the caller to
|
115
|
+
# validate a value for a prop without having to set the data on the instance.
|
116
|
+
# Throws if invalid.
|
117
|
+
#
|
118
|
+
# @param prop [Symbol]
|
119
|
+
# @param val [Object]
|
120
|
+
# @return [void]
|
121
|
+
def validate_prop_value(prop, val)
|
122
|
+
decorator.validate_prop_value(prop, val)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Needs to be documented
|
126
|
+
def plugin(mod)
|
127
|
+
decorator.plugin(mod)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Shorthand helper to define a `prop` with `immutable => true`
|
131
|
+
sig {params(name: T.any(Symbol, String), cls_or_args: T.untyped, args: T::Hash[Symbol, T.untyped]).void}
|
132
|
+
def const(name, cls_or_args, args={})
|
133
|
+
if (cls_or_args.is_a?(Hash) && cls_or_args.key?(:immutable)) || args.key?(:immutable)
|
134
|
+
raise ArgumentError.new("Cannot pass 'immutable' argument when using 'const' keyword to define a prop")
|
135
|
+
end
|
136
|
+
|
137
|
+
if cls_or_args.is_a?(Hash)
|
138
|
+
self.prop(name, cls_or_args.merge(immutable: true))
|
139
|
+
else
|
140
|
+
self.prop(name, cls_or_args, args.merge(immutable: true))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def included(child)
|
145
|
+
decorator.model_inherited(child)
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
149
|
+
def prepended(child)
|
150
|
+
decorator.model_inherited(child)
|
151
|
+
super
|
152
|
+
end
|
153
|
+
|
154
|
+
def extended(child)
|
155
|
+
decorator.model_inherited(child.singleton_class)
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
def inherited(child)
|
160
|
+
decorator.model_inherited(child)
|
161
|
+
super
|
162
|
+
end
|
163
|
+
end
|
164
|
+
mixes_in_class_methods(ClassMethods)
|
165
|
+
end
|