bcdd-result 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BCDD::Result
4
+ module Transitions
5
+ module Tracking
6
+ require_relative 'tracking/enabled'
7
+ require_relative 'tracking/disabled'
8
+
9
+ EMPTY_ARRAY = [].freeze
10
+ EMPTY_HASH = {}.freeze
11
+ EMPTY_TREE = Tree.new(nil).freeze
12
+ VERSION = 1
13
+ EMPTY = { version: VERSION, records: EMPTY_ARRAY, metadata: { duration: 0, tree_map: EMPTY_ARRAY } }.freeze
14
+
15
+ def self.instance
16
+ Config.instance.feature.enabled?(:transitions) ? Tracking::Enabled.new : Tracking::Disabled
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BCDD::Result
4
+ module Transitions
5
+ class Tree
6
+ class Node
7
+ attr_reader :id, :value, :parent, :normalizer, :children
8
+
9
+ def initialize(value, parent:, id:, normalizer:)
10
+ @normalizer = normalizer
11
+
12
+ @id = id
13
+ @value = normalizer.call(id, value)
14
+ @parent = parent
15
+
16
+ @children = []
17
+ end
18
+
19
+ def insert(value, id:)
20
+ node = self.class.new(value, parent: self, id: id, normalizer: normalizer)
21
+
22
+ @children << node
23
+
24
+ node
25
+ end
26
+
27
+ def root?
28
+ parent.nil?
29
+ end
30
+
31
+ def leaf?
32
+ children.empty?
33
+ end
34
+
35
+ def node?
36
+ !leaf?
37
+ end
38
+
39
+ def inspect
40
+ "#<#{self.class.name} id=#{id} children.size=#{children.size}>"
41
+ end
42
+ end
43
+
44
+ attr_reader :size, :root, :current
45
+
46
+ def initialize(value, normalizer: ->(_id, val) { val })
47
+ @size = 0
48
+
49
+ @root = Node.new(value, parent: nil, id: @size, normalizer: normalizer)
50
+
51
+ @current = @root
52
+ end
53
+
54
+ def root_value
55
+ root.value
56
+ end
57
+
58
+ def parent_value
59
+ current.parent&.value || root_value
60
+ end
61
+
62
+ def current_value
63
+ current.value
64
+ end
65
+
66
+ def insert(value)
67
+ @size += 1
68
+
69
+ current.insert(value, id: size)
70
+ end
71
+
72
+ def insert!(value)
73
+ @current = insert(value)
74
+ end
75
+
76
+ def move_up!(level = 1)
77
+ tap { level.times { @current = current.parent || root } }
78
+ end
79
+
80
+ def move_down!(level = 1, index: -1)
81
+ tap { level.times { current.children[index].then { |child| @current = child if child } } }
82
+ end
83
+
84
+ def move_to_root!
85
+ tap { @current = root }
86
+ end
87
+
88
+ NestedIds = ->(node) { [node.id, node.children.map(&NestedIds)] }
89
+
90
+ def nested_ids
91
+ NestedIds[root]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BCDD::Result
4
+ module Transitions
5
+ require_relative 'transitions/tree'
6
+ require_relative 'transitions/tracking'
7
+
8
+ THREAD_VAR_NAME = :bcdd_result_transitions_tracking
9
+
10
+ def self.tracking
11
+ Thread.current[THREAD_VAR_NAME] ||= Tracking.instance
12
+ end
13
+ end
14
+
15
+ def self.transitions(name: nil, desc: nil)
16
+ Transitions.tracking.start(name: name, desc: desc)
17
+
18
+ result = yield
19
+
20
+ result.is_a?(::BCDD::Result) or raise Error::UnexpectedOutcome.build(outcome: result, origin: :transitions)
21
+
22
+ Transitions.tracking.finish(result: result)
23
+
24
+ result
25
+ rescue ::Exception => e
26
+ Transitions.tracking.reset!
27
+
28
+ raise e
29
+ end
30
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module BCDD
4
4
  class Result
5
- VERSION = '0.9.0'
5
+ VERSION = '0.10.0'
6
6
  end
7
7
  end
data/lib/bcdd/result.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'result/version'
4
+ require_relative 'result/transitions'
4
5
  require_relative 'result/error'
5
6
  require_relative 'result/data'
6
7
  require_relative 'result/handler'
@@ -13,13 +14,13 @@ require_relative 'result/context'
13
14
  require_relative 'result/config'
14
15
 
15
16
  class BCDD::Result
16
- attr_accessor :unknown
17
+ attr_accessor :unknown, :transitions
17
18
 
18
19
  attr_reader :subject, :data, :type_checker, :halted
19
20
 
20
21
  protected :subject
21
22
 
22
- private :unknown, :unknown=, :type_checker
23
+ private :unknown, :unknown=, :type_checker, :transitions=
23
24
 
24
25
  def self.config
25
26
  Config.instance
@@ -40,6 +41,9 @@ class BCDD::Result
40
41
  @data = data
41
42
 
42
43
  self.unknown = true
44
+ self.transitions = Transitions::Tracking::EMPTY
45
+
46
+ Transitions.tracking.record(self)
43
47
  end
44
48
 
45
49
  def halted?
@@ -84,16 +88,12 @@ class BCDD::Result
84
88
  tap { yield(value, type) if unknown }
85
89
  end
86
90
 
87
- def and_then(method_name = nil, context = nil)
91
+ def and_then(method_name = nil, context = nil, &block)
88
92
  return self if halted?
89
93
 
90
- method_name && block_given? and raise ::ArgumentError, 'method_name and block are mutually exclusive'
91
-
92
- return call_subject_method(method_name, context) if method_name
93
-
94
- result = yield(value)
94
+ method_name && block and raise ::ArgumentError, 'method_name and block are mutually exclusive'
95
95
 
96
- ensure_result_object(result, origin: :block)
96
+ method_name ? call_and_then_subject_method(method_name, context) : call_and_then_block(block)
97
97
  end
98
98
 
99
99
  def handle
@@ -139,18 +139,35 @@ class BCDD::Result
139
139
  block.call(value, type)
140
140
  end
141
141
 
142
- def call_subject_method(method_name, context)
142
+ def call_and_then_subject_method(method_name, context_data)
143
143
  method = subject.method(method_name)
144
144
 
145
- result =
146
- case method.arity
147
- when 0 then subject.send(method_name)
148
- when 1 then subject.send(method_name, value)
149
- when 2 then subject.send(method_name, value, context)
150
- else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 2)
151
- end
145
+ Transitions.tracking.record_and_then(method, context_data, subject) do
146
+ result = call_and_then_subject_method!(method, context_data)
147
+
148
+ ensure_result_object(result, origin: :method)
149
+ end
150
+ end
151
+
152
+ def call_and_then_subject_method!(method, context_data)
153
+ case method.arity
154
+ when 0 then subject.send(method.name)
155
+ when 1 then subject.send(method.name, value)
156
+ when 2 then subject.send(method.name, value, context_data)
157
+ else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 2)
158
+ end
159
+ end
160
+
161
+ def call_and_then_block(block)
162
+ Transitions.tracking.record_and_then(:block, nil, subject) do
163
+ result = call_and_then_block!(block)
164
+
165
+ ensure_result_object(result, origin: :block)
166
+ end
167
+ end
152
168
 
153
- ensure_result_object(result, origin: :method)
169
+ def call_and_then_block!(block)
170
+ block.call(value)
154
171
  end
155
172
 
156
173
  def ensure_result_object(result, origin:)
@@ -0,0 +1,100 @@
1
+ class BCDD::Result::Config
2
+ include Singleton
3
+
4
+ ADDON: Hash[Symbol, Hash[Symbol, untyped]]
5
+ FEATURE: Hash[Symbol, Hash[Symbol, untyped]]
6
+ PATTERN_MATCHING: Hash[Symbol, Hash[Symbol, untyped]]
7
+
8
+ attr_reader addon: BCDD::Result::Config::Switcher
9
+ attr_reader feature: BCDD::Result::Config::Switcher
10
+ attr_reader constant_alias: BCDD::Result::Config::Switcher
11
+ attr_reader pattern_matching: BCDD::Result::Config::Switcher
12
+
13
+ def self.instance: -> BCDD::Result::Config
14
+
15
+ def initialize: -> void
16
+
17
+ def freeze: -> BCDD::Result::Config
18
+ def options: -> Hash[Symbol, BCDD::Result::Config::Switcher]
19
+ def to_h: -> Hash[Symbol, Hash[Symbol | String, bool]]
20
+ end
21
+
22
+ module BCDD::Result::Config::Options
23
+ def self.with_defaults: (
24
+ Hash[Symbol, Hash[Symbol, bool]],
25
+ Symbol
26
+ ) -> Hash[Symbol, bool]
27
+
28
+ def self.select: (
29
+ Hash[Symbol, Hash[Symbol, bool]],
30
+ config: Symbol,
31
+ from: Hash[Symbol, untyped]
32
+ ) -> Hash[Symbol, untyped]
33
+
34
+ def self.addon: (
35
+ map: Hash[Symbol, Hash[Symbol, bool]],
36
+ from: Hash[Symbol, Module]
37
+ ) -> Hash[Symbol, Module]
38
+ end
39
+
40
+ class BCDD::Result::Config::Switcher
41
+ private attr_reader _affects: Hash[Symbol | String, Array[String]]
42
+ private attr_reader _options: Hash[Symbol | String, bool]
43
+ private attr_reader listener: Proc
44
+
45
+ def initialize: (
46
+ options: Hash[Symbol | String, Hash[Symbol, untyped]],
47
+ ?listener: Proc
48
+ ) -> void
49
+
50
+ def freeze: -> BCDD::Result::Config::Switcher
51
+
52
+ def to_h: -> Hash[Symbol | String, bool]
53
+
54
+ def options: -> Hash[Symbol | String, Hash[Symbol, untyped]]
55
+
56
+ def enabled?: (Symbol | String) -> bool
57
+
58
+ def enable!: (*(Symbol | String)) -> Hash[Symbol | String, Hash[Symbol, untyped]]
59
+
60
+ def disable!: (*(Symbol | String)) -> Hash[Symbol | String, Hash[Symbol, untyped]]
61
+
62
+ private
63
+
64
+ def set_many: (Array[Symbol | String], to: bool) -> Hash[Symbol | String, Hash[Symbol, untyped]]
65
+
66
+ def set_one: (Symbol | String, bool) -> void
67
+
68
+ def require_option!: (Array[Symbol | String]) -> void
69
+
70
+ def validate_option!: (Symbol | String) -> void
71
+
72
+ def available_options_message: -> String
73
+ end
74
+
75
+ module BCDD::Result::Config::Addons
76
+ OPTIONS: Hash[String, Hash[Symbol, untyped]]
77
+
78
+ def self.switcher: -> BCDD::Result::Config::Switcher
79
+ end
80
+
81
+ module BCDD::Result::Config::ConstantAliases
82
+ MAPPING: Hash[String, Hash[Symbol, untyped]]
83
+ OPTIONS: Hash[String, Hash[Symbol, untyped]]
84
+ Listener: Proc
85
+
86
+ def self.switcher: -> BCDD::Result::Config::Switcher
87
+ end
88
+
89
+ module BCDD::Result::Config::Features
90
+ OPTIONS: Hash[String, Hash[Symbol, untyped]]
91
+ Listener: Proc
92
+
93
+ def self.switcher: -> BCDD::Result::Config::Switcher
94
+ end
95
+
96
+ module BCDD::Result::Config::PatternMatching
97
+ OPTIONS: Hash[String, Hash[Symbol, untyped]]
98
+
99
+ def self.switcher: -> BCDD::Result::Config::Switcher
100
+ end
@@ -0,0 +1,102 @@
1
+ class BCDD::Result::Context < BCDD::Result
2
+ EXPECTED_OUTCOME: String
3
+
4
+ SubjectMethodArity: ^(Method) -> Integer
5
+
6
+ attr_reader acc: Hash[Symbol, untyped]
7
+
8
+ def initialize: (
9
+ type: Symbol,
10
+ value: untyped,
11
+ ?subject: untyped,
12
+ ?expectations: BCDD::Result::Contract::Evaluator,
13
+ ?halted: bool
14
+ ) -> void
15
+
16
+ def and_then: (?Symbol, **untyped) ?{ (Hash[Symbol, untyped]) -> untyped } -> BCDD::Result::Context
17
+
18
+ private
19
+
20
+ def call_and_then_subject_method: (Symbol, Hash[Symbol, untyped]) -> BCDD::Result::Context
21
+ def ensure_result_object: (untyped, origin: Symbol) -> BCDD::Result::Context
22
+
23
+ def raise_unexpected_outcome_error: (BCDD::Result::Context | untyped, Symbol) -> void
24
+ end
25
+
26
+ class BCDD::Result::Context
27
+ class Success < BCDD::Result::Context
28
+ include BCDD::Result::Success::Methods
29
+
30
+ def and_expose: (Symbol, Array[Symbol], halted: bool) -> BCDD::Result::Context::Success
31
+ end
32
+
33
+ def self.Success: (Symbol, **untyped) -> BCDD::Result::Context::Success
34
+ end
35
+
36
+ class BCDD::Result::Context
37
+ class Failure < BCDD::Result::Context
38
+ include BCDD::Result::Failure::Methods
39
+
40
+ def and_expose: (Symbol, Array[Symbol], **untyped) -> BCDD::Result::Context::Failure
41
+ end
42
+
43
+ def self.Failure: (Symbol, **untyped) -> BCDD::Result::Context::Failure
44
+ end
45
+
46
+ class BCDD::Result::Context
47
+ module Mixin
48
+ Factory: singleton(BCDD::Result::Mixin::Factory)
49
+
50
+ module Methods
51
+ def Success: (Symbol, **untyped) -> BCDD::Result::Context::Success
52
+
53
+ def Failure: (Symbol, **untyped) -> BCDD::Result::Context::Failure
54
+
55
+ private
56
+
57
+ def _ResultAs: (singleton(BCDD::Result::Context), Symbol, untyped, ?halted: bool) -> untyped
58
+ end
59
+
60
+ module Addons
61
+ module Continuable
62
+ include BCDD::Result::Context::Mixin::Methods
63
+
64
+ private
65
+
66
+ def Continue: (**untyped) -> BCDD::Result::Context::Success
67
+ end
68
+
69
+ OPTIONS: Hash[Symbol, Module]
70
+
71
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
72
+ end
73
+ end
74
+
75
+ def self.mixin_module: -> singleton(BCDD::Result::Context::Mixin)
76
+
77
+ def self.result_factory: -> singleton(BCDD::Result::Context)
78
+ end
79
+
80
+ class BCDD::Result::Context::Expectations < BCDD::Result::Expectations
81
+ def self.mixin_module: -> singleton(BCDD::Result::Context::Expectations::Mixin)
82
+
83
+ def self.result_factory_without_expectations: -> singleton(BCDD::Result)
84
+
85
+ def Success: (Symbol, **untyped) -> BCDD::Result::Context::Success
86
+ def Failure: (Symbol, **untyped) -> BCDD::Result::Context::Failure
87
+ end
88
+
89
+ module BCDD::Result::Context::Expectations::Mixin
90
+ Methods: singleton(BCDD::Result::Expectations::Mixin::Methods)
91
+ Factory: singleton(BCDD::Result::Expectations::Mixin::Factory)
92
+
93
+ module Addons
94
+ module Continuable
95
+ private def Continue: (**untyped) -> BCDD::Result::Context::Success
96
+ end
97
+
98
+ OPTIONS: Hash[Symbol, Module]
99
+
100
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
101
+ end
102
+ end
@@ -0,0 +1,119 @@
1
+ module BCDD::Result::Contract
2
+ NONE: BCDD::Result::Contract::Evaluator
3
+
4
+ def self.evaluate: (
5
+ BCDD::Result::Data,
6
+ BCDD::Result::Contract::Evaluator
7
+ ) -> BCDD::Result::Contract::TypeChecker
8
+
9
+ ToEnsure: ^(Hash[Symbol, untyped] | Array[Symbol], Hash[Symbol, Hash[Symbol, bool]])
10
+ -> BCDD::Result::Contract::Interface
11
+
12
+ def self.new: (
13
+ success: Hash[Symbol, untyped] | Array[Symbol],
14
+ failure: Hash[Symbol, untyped] | Array[Symbol],
15
+ config: Hash[Symbol, Hash[Symbol, bool]]
16
+ ) -> BCDD::Result::Contract::Evaluator
17
+ end
18
+
19
+ module BCDD::Result::Contract
20
+ class TypeChecker
21
+ attr_reader result_type: Symbol
22
+ attr_reader expectations: BCDD::Result::Contract::Evaluator
23
+
24
+ def initialize: (
25
+ Symbol,
26
+ expectations: BCDD::Result::Contract::Evaluator
27
+ ) -> void
28
+
29
+ def allow?: (Array[Symbol]) -> bool
30
+ def allow_success?: (Array[Symbol]) -> bool
31
+ def allow_failure?: (Array[Symbol]) -> bool
32
+
33
+ private
34
+
35
+ def validate: (
36
+ Array[Symbol],
37
+ expected: BCDD::Result::Contract::Interface,
38
+ allow_empty: bool
39
+ ) -> bool
40
+ end
41
+ end
42
+
43
+ class BCDD::Result::Contract::Error < BCDD::Result::Error
44
+ class UnexpectedType < BCDD::Result::Contract::Error
45
+ def self.build: (type: Symbol, allowed_types: Set[Symbol])
46
+ -> BCDD::Result::Contract::Error::UnexpectedType
47
+ end
48
+
49
+ class UnexpectedValue < BCDD::Result::Contract::Error
50
+ def self.build: (type: Symbol, value: untyped, ?cause: Exception)
51
+ -> BCDD::Result::Contract::Error::UnexpectedValue
52
+ end
53
+ end
54
+
55
+ module BCDD::Result::Contract
56
+ module Interface
57
+ def ==: (BCDD::Result::Contract::Interface) -> bool
58
+
59
+ def allowed_types: -> Set[Symbol]
60
+
61
+ def type?: (Symbol) -> bool
62
+
63
+ def type!: (Symbol) -> Symbol
64
+
65
+ def type_and_value!: (BCDD::Result::Data) -> void
66
+
67
+ def !=: (untyped) -> bool
68
+ end
69
+ end
70
+
71
+ module BCDD::Result::Contract
72
+ module Disabled
73
+ extend Interface
74
+
75
+ EMPTY_SET: Set[Symbol]
76
+ end
77
+ end
78
+
79
+ module BCDD::Result::Contract
80
+ class ForTypes
81
+ include Interface
82
+
83
+ def initialize: (Array[Symbol]) -> void
84
+ end
85
+ end
86
+
87
+ module BCDD::Result::Contract
88
+ class ForTypesAndValues
89
+ include Interface
90
+
91
+ def initialize: (
92
+ Hash[Symbol, untyped],
93
+ Hash[Symbol, Hash[Symbol, bool]]
94
+ ) -> void
95
+
96
+ private
97
+
98
+ def nil_as_valid_value_checking?: -> bool
99
+ end
100
+ end
101
+
102
+ module BCDD::Result::Contract
103
+ class Evaluator
104
+ include Interface
105
+
106
+ attr_reader allowed_types: Set[Symbol]
107
+ attr_reader success: BCDD::Result::Contract::Interface
108
+ attr_reader failure: BCDD::Result::Contract::Interface
109
+
110
+ def initialize: (
111
+ BCDD::Result::Contract::Interface,
112
+ BCDD::Result::Contract::Interface
113
+ ) -> void
114
+
115
+ private
116
+
117
+ def for: (BCDD::Result::Data) -> BCDD::Result::Contract::Interface
118
+ end
119
+ end
@@ -0,0 +1,16 @@
1
+ class BCDD::Result
2
+ class Data
3
+ attr_reader kind: Symbol
4
+ attr_reader type: Symbol
5
+ attr_reader value: untyped
6
+ attr_reader to_h: Hash[Symbol, untyped]
7
+ attr_reader to_a: [Symbol, Symbol, untyped]
8
+
9
+ def initialize: (Symbol, Symbol, untyped) -> void
10
+
11
+ def inspect: -> String
12
+
13
+ alias to_ary to_a
14
+ alias to_hash to_h
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ class BCDD::Result
2
+ class Error < StandardError
3
+ def self.build: (**untyped) -> BCDD::Result::Error
4
+
5
+ class NotImplemented < BCDD::Result::Error
6
+ end
7
+
8
+ class MissingTypeArgument < BCDD::Result::Error
9
+ end
10
+
11
+ class UnexpectedOutcome < BCDD::Result::Error
12
+ def self.build: (outcome: untyped, origin: Symbol, ?expected: String)
13
+ -> BCDD::Result::Error::UnexpectedOutcome
14
+ end
15
+
16
+ class InvalidResultSubject < BCDD::Result::Error
17
+ def self.build: (given_result: BCDD::Result, expected_subject: untyped)
18
+ -> BCDD::Result::Error::InvalidResultSubject
19
+ end
20
+
21
+ class InvalidSubjectMethodArity < BCDD::Result::Error
22
+ def self.build: (subject: untyped, method: Method, max_arity: Integer)
23
+ -> BCDD::Result::Error::InvalidSubjectMethodArity
24
+ end
25
+
26
+ class UnhandledTypes < BCDD::Result::Error
27
+ def self.build: (types: Set[Symbol])
28
+ -> BCDD::Result::Error::UnhandledTypes
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,67 @@
1
+ class BCDD::Result::Expectations
2
+ def self.mixin: (
3
+ ?config: Hash[Symbol, Hash[Symbol, bool]],
4
+ ?success: Hash[Symbol, untyped] | Array[Symbol],
5
+ ?failure: Hash[Symbol, untyped] | Array[Symbol]
6
+ ) -> Module
7
+
8
+ def self.mixin!: (
9
+ ?config: Hash[Symbol, Hash[Symbol, bool]],
10
+ ?success: Hash[Symbol, untyped] | Array[Symbol],
11
+ ?failure: Hash[Symbol, untyped] | Array[Symbol]
12
+ ) -> Module
13
+
14
+ def self.mixin_module: -> singleton(BCDD::Result::Expectations::Mixin)
15
+
16
+ def self.result_factory_without_expectations: -> singleton(BCDD::Result)
17
+
18
+ def self.new: (
19
+ ?subject: untyped,
20
+ ?contract: BCDD::Result::Contract::Evaluator,
21
+ ?halted: bool,
22
+ **untyped
23
+ ) -> (BCDD::Result::Expectations | untyped)
24
+
25
+ def initialize: (
26
+ ?subject: untyped,
27
+ ?contract: BCDD::Result::Contract::Evaluator,
28
+ ?halted: bool,
29
+ **untyped
30
+ ) -> void
31
+
32
+ def Success: (Symbol, ?untyped) -> BCDD::Result::Success
33
+ def Failure: (Symbol, ?untyped) -> BCDD::Result::Failure
34
+
35
+ def with: (subject: untyped) -> BCDD::Result::Expectations
36
+
37
+ private
38
+
39
+ def _ResultAs: (singleton(BCDD::Result), Symbol, untyped) -> untyped
40
+
41
+ attr_reader subject: untyped
42
+ attr_reader contract: BCDD::Result::Contract::Evaluator
43
+ attr_reader halted: bool
44
+ end
45
+
46
+ module BCDD::Result::Expectations::Mixin
47
+ module Factory
48
+ def self.module!: -> Module
49
+ end
50
+
51
+ module Methods
52
+ BASE: String
53
+ FACTORY: String
54
+
55
+ def self.to_eval: (Hash[Symbol, untyped]) -> String
56
+ end
57
+
58
+ module Addons
59
+ module Continuable
60
+ private def Continue: (untyped) -> BCDD::Result::Success
61
+ end
62
+
63
+ OPTIONS: Hash[Symbol, Module]
64
+
65
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
66
+ end
67
+ end