domainic-attributer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/LICENSE +21 -0
  4. data/README.md +396 -0
  5. data/lib/domainic/attributer/attribute/callback.rb +68 -0
  6. data/lib/domainic/attributer/attribute/coercer.rb +93 -0
  7. data/lib/domainic/attributer/attribute/mixin/belongs_to_attribute.rb +68 -0
  8. data/lib/domainic/attributer/attribute/signature.rb +338 -0
  9. data/lib/domainic/attributer/attribute/validator.rb +128 -0
  10. data/lib/domainic/attributer/attribute.rb +256 -0
  11. data/lib/domainic/attributer/attribute_set.rb +208 -0
  12. data/lib/domainic/attributer/class_methods.rb +247 -0
  13. data/lib/domainic/attributer/dsl/attribute_builder/option_parser.rb +247 -0
  14. data/lib/domainic/attributer/dsl/attribute_builder.rb +233 -0
  15. data/lib/domainic/attributer/dsl/initializer.rb +130 -0
  16. data/lib/domainic/attributer/dsl/method_injector.rb +97 -0
  17. data/lib/domainic/attributer/dsl.rb +5 -0
  18. data/lib/domainic/attributer/instance_methods.rb +65 -0
  19. data/lib/domainic/attributer/undefined.rb +44 -0
  20. data/lib/domainic/attributer.rb +114 -0
  21. data/lib/domainic-attributer.rb +3 -0
  22. data/sig/domainic/attributer/attribute/callback.rbs +48 -0
  23. data/sig/domainic/attributer/attribute/coercer.rbs +59 -0
  24. data/sig/domainic/attributer/attribute/mixin/belongs_to_attribute.rbs +46 -0
  25. data/sig/domainic/attributer/attribute/signature.rbs +223 -0
  26. data/sig/domainic/attributer/attribute/validator.rbs +83 -0
  27. data/sig/domainic/attributer/attribute.rbs +150 -0
  28. data/sig/domainic/attributer/attribute_set.rbs +134 -0
  29. data/sig/domainic/attributer/class_methods.rbs +151 -0
  30. data/sig/domainic/attributer/dsl/attribute_builder/option_parser.rbs +130 -0
  31. data/sig/domainic/attributer/dsl/attribute_builder.rbs +156 -0
  32. data/sig/domainic/attributer/dsl/initializer.rbs +91 -0
  33. data/sig/domainic/attributer/dsl/method_injector.rbs +66 -0
  34. data/sig/domainic/attributer/dsl.rbs +1 -0
  35. data/sig/domainic/attributer/instance_methods.rbs +53 -0
  36. data/sig/domainic/attributer/undefined.rbs +14 -0
  37. data/sig/domainic/attributer.rbs +69 -0
  38. data/sig/domainic-attributer.rbs +1 -0
  39. data/sig/manifest.yaml +2 -0
  40. metadata +89 -0
@@ -0,0 +1,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'domainic/attributer/attribute_set'
4
+ require 'domainic/attributer/dsl/attribute_builder'
5
+ require 'domainic/attributer/dsl/method_injector'
6
+ require 'domainic/attributer/undefined'
7
+
8
+ module Domainic
9
+ module Attributer
10
+ # A module providing class-level methods for attribute definition.
11
+ #
12
+ # This module extends classes that include Domainic::Attributer with methods for
13
+ # defining and managing attributes. It supports two types of attributes:
14
+ # 1. Arguments - Positional parameters that must be provided in a specific order
15
+ # 2. Options - Named parameters that can be provided in any order
16
+ #
17
+ # @example Defining arguments and options
18
+ # class Person
19
+ # include Domainic::Attributer
20
+ #
21
+ # argument :name, ->(value) { value.is_a?(String) }
22
+ # argument :age do |value|
23
+ # value.is_a?(Integer) && value >= 0
24
+ # end
25
+ #
26
+ # option :email, ->(value) { value.is_a?(String) }, default: nil
27
+ # option :role do |value|
28
+ # %w[admin user guest].include?(value)
29
+ # end
30
+ # end
31
+ #
32
+ # @author {https://aaronmallen.me Aaron Allen}
33
+ # @since 0.1.0
34
+ module ClassMethods
35
+ # @rbs @__attributes__: AttributeSet
36
+
37
+ # Define a positional argument attribute.
38
+ #
39
+ # Arguments are required by default and must be provided in the order they are defined.
40
+ # They can be type-validated and configured with additional options like defaults
41
+ # and visibility.
42
+ #
43
+ # @param attribute_name [String, Symbol] the name of the attribute
44
+ # @param type_validator [Proc, Object, nil] optional validation handler for type checking
45
+ # @param options [Hash] additional configuration options
46
+ #
47
+ # @option options [Array<Proc>, Proc] :callbacks handlers for attribute change events (priority over :callback,
48
+ # :on_change)
49
+ # @option options [Array<Proc>, Proc] :callback alias for :callbacks
50
+ # @option options [Array<Proc, Symbol>, Proc, Symbol] :coerce handlers for value coercion (priority over
51
+ # :coercers, :coerce_with)
52
+ # @option options [Array<Proc, Symbol>, Proc, Symbol] :coercers alias for :coerce
53
+ # @option options [Array<Proc, Symbol>, Proc, Symbol] :coerce_with alias for :coerce
54
+ # @option options [Object] :default the default value (priority over :default_generator, :default_value)
55
+ # @option options [Object] :default_generator alias for :default
56
+ # @option options [Object] :default_value alias for :default
57
+ # @option options [String] :desc short description (overridden by :description)
58
+ # @option options [String] :description description text
59
+ # @option options [Boolean] :non_nil require non-nil values (priority over :non_null, :non_nullable, :not_nil,
60
+ # :not_nilable, :not_null, :not_nullable)
61
+ # @option options [Boolean] :non_null alias for :non_nil
62
+ # @option options [Boolean] :non_nullable alias for :non_nil
63
+ # @option options [Boolean] :not_nil alias for :non_nil
64
+ # @option options [Boolean] :not_nilable alias for :non_nil
65
+ # @option options [Boolean] :not_null alias for :non_nil
66
+ # @option options [Boolean] :not_nullable alias for :non_nil
67
+ # @option options [Boolean] :null inverse of :non_nil
68
+ # @option options [Array<Proc>, Proc] :on_change alias for :callbacks
69
+ # @option options [Boolean] :optional whether attribute is optional (overridden by :required)
70
+ # @option options [Integer] :position specify order position
71
+ # @option options [Symbol] :read read visibility (:public, :protected, :private) (priority over :read_access,
72
+ # :reader)
73
+ # @option options [Symbol] :read_access alias for :read
74
+ # @option options [Symbol] :reader alias for :read
75
+ # @option options [Boolean] :required whether attribute is required
76
+ # @option options [Array<Object>, Object] :validate validators for the attribute (priority over :validate_with,
77
+ # :validators)
78
+ # @option options [Array<Object>, Object] :validate_with alias for :validate
79
+ # @option options [Array<Object>, Object] :validators alias for :validate
80
+ # @option options [Symbol] :write_access write visibility (:public, :protected, :private) (priority over :writer)
81
+ # @option options [Symbol] :writer alias for :write_access
82
+ #
83
+ # @yield [DSL::AttributeBuilder] optional configuration block
84
+ # @return [void]
85
+ # @rbs (
86
+ # String | Symbol attribute_name,
87
+ # ?Attribute::Validator::handler type_validator,
88
+ # ?callbacks: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
89
+ # ?callback: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
90
+ # ?coerce: Array[Attribute::Coercer::handler] | Attribute::Coercer::handler,
91
+ # ?coercers: Array[Attribute::Coercer::handler],
92
+ # ?coerce_with: [Attribute::Coercer::handler] | Attribute::Coercer::handler,
93
+ # ?default: untyped,
94
+ # ?default_generator: untyped,
95
+ # ?default_value: untyped,
96
+ # ?desc: String?,
97
+ # ?description: String,
98
+ # ?non_nil: bool,
99
+ # ?non_null: bool,
100
+ # ?non_nullable: bool,
101
+ # ?not_nil: bool,
102
+ # ?not_nilable: bool,
103
+ # ?not_null: bool,
104
+ # ?not_nullable: bool,
105
+ # ?null: bool,
106
+ # ?on_change: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
107
+ # ?optional: bool,
108
+ # ?position: Integer?,
109
+ # ?read: Attribute::Signature::visibility_symbol,
110
+ # ?read_access: Attribute::Signature::visibility_symbol,
111
+ # ?reader: Attribute::Signature::visibility_symbol,
112
+ # ?required: bool,
113
+ # ?validate: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
114
+ # ?validate_with: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
115
+ # ?validators: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
116
+ # ?write_access: Attribute::Signature::visibility_symbol,
117
+ # ?writer: Attribute::Signature::visibility_symbol,
118
+ # ) ?{ (?) [self: DSL::AttributeBuilder] -> void } -> void
119
+ def argument(attribute_name, type_validator = Undefined, **options, &)
120
+ position = __attributes__.count { |_, attribute| attribute.signature.argument? }
121
+
122
+ attribute = DSL::AttributeBuilder.new(
123
+ self, attribute_name, :argument, type_validator, **options.merge(position:), &
124
+ ).build!
125
+
126
+ __attributes__.add(attribute)
127
+ DSL::MethodInjector.inject!(self, attribute)
128
+ end
129
+
130
+ # Define a named option attribute.
131
+ #
132
+ # Options are optional by default and can be provided in any order. They can be
133
+ # type-validated and configured with additional options like defaults and visibility.
134
+ #
135
+ # @overload option(attribute_name, type_validator = Undefined, **options, &block)
136
+ # @param attribute_name [String, Symbol] the name of the attribute
137
+ # @param type_validator [Proc, Object, nil] optional validation handler for type checking
138
+ # @param options [Hash] additional configuration options
139
+ #
140
+ # @option options [Array<Proc>, Proc] :callbacks handlers for attribute change events (priority over :callback,
141
+ # :on_change)
142
+ # @option options [Array<Proc>, Proc] :callback alias for :callbacks
143
+ # @option options [Array<Proc, Symbol>, Proc, Symbol] :coerce handlers for value coercion (priority over
144
+ # :coercers, :coerce_with)
145
+ # @option options [Array<Proc, Symbol>, Proc, Symbol] :coercers alias for :coerce
146
+ # @option options [Array<Proc, Symbol>, Proc, Symbol] :coerce_with alias for :coerce
147
+ # @option options [Object] :default the default value (priority over :default_generator, :default_value)
148
+ # @option options [Object] :default_generator alias for :default
149
+ # @option options [Object] :default_value alias for :default
150
+ # @option options [String] :desc short description (overridden by :description)
151
+ # @option options [String] :description description text
152
+ # @option options [Boolean] :non_nil require non-nil values (priority over :non_null, :non_nullable, :not_nil,
153
+ # :not_nilable, :not_null, :not_nullable)
154
+ # @option options [Boolean] :non_null alias for :non_nil
155
+ # @option options [Boolean] :non_nullable alias for :non_nil
156
+ # @option options [Boolean] :not_nil alias for :non_nil
157
+ # @option options [Boolean] :not_nilable alias for :non_nil
158
+ # @option options [Boolean] :not_null alias for :non_nil
159
+ # @option options [Boolean] :not_nullable alias for :non_nil
160
+ # @option options [Boolean] :null inverse of :non_nil
161
+ # @option options [Array<Proc>, Proc] :on_change alias for :callbacks
162
+ # @option options [Boolean] :optional whether attribute is optional (overridden by :required)
163
+ # @option options [Integer] :position specify order position
164
+ # @option options [Symbol] :read read visibility (:public, :protected, :private) (priority over :read_access,
165
+ # :reader)
166
+ # @option options [Symbol] :read_access alias for :read
167
+ # @option options [Symbol] :reader alias for :read
168
+ # @option options [Boolean] :required whether attribute is required
169
+ # @option options [Array<Object>, Object] :validate validators for the attribute (priority over :validate_with,
170
+ # :validators)
171
+ # @option options [Array<Object>, Object] :validate_with alias for :validate
172
+ # @option options [Array<Object>, Object] :validators alias for :validate
173
+ # @option options [Symbol] :write_access write visibility (:public, :protected, :private)
174
+ # (priority over :writer)
175
+ # @option options [Symbol] :writer alias for :write_access
176
+ #
177
+ # @yield [DSL::AttributeBuilder] optional configuration block
178
+ # @return [void]
179
+ #
180
+ # @yield [DSL::AttributeBuilder] optional configuration block
181
+ # @return [void]
182
+ # @rbs (
183
+ # String | Symbol attribute_name,
184
+ # ?Attribute::Validator::handler type_validator,
185
+ # ?callbacks: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
186
+ # ?callback: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
187
+ # ?coerce: Array[Attribute::Coercer::handler] | Attribute::Coercer::handler,
188
+ # ?coercers: Array[Attribute::Coercer::handler],
189
+ # ?coerce_with: [Attribute::Coercer::handler] | Attribute::Coercer::handler,
190
+ # ?default: untyped,
191
+ # ?default_generator: untyped,
192
+ # ?default_value: untyped,
193
+ # ?desc: String?,
194
+ # ?description: String,
195
+ # ?non_nil: bool,
196
+ # ?non_null: bool,
197
+ # ?non_nullable: bool,
198
+ # ?not_nil: bool,
199
+ # ?not_nilable: bool,
200
+ # ?not_null: bool,
201
+ # ?not_nullable: bool,
202
+ # ?null: bool,
203
+ # ?on_change: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
204
+ # ?optional: bool,
205
+ # ?position: Integer?,
206
+ # ?read: Attribute::Signature::visibility_symbol,
207
+ # ?read_access: Attribute::Signature::visibility_symbol,
208
+ # ?reader: Attribute::Signature::visibility_symbol,
209
+ # ?required: bool,
210
+ # ?validate: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
211
+ # ?validate_with: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
212
+ # ?validators: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
213
+ # ?write_access: Attribute::Signature::visibility_symbol,
214
+ # ?writer: Attribute::Signature::visibility_symbol,
215
+ # ) ?{ (?) [self: DSL::AttributeBuilder] -> void } -> void
216
+ def option(attribute_name, ...)
217
+ attribute = DSL::AttributeBuilder.new(self, attribute_name, :option, ...).build! # steep:ignore
218
+
219
+ __attributes__.add(attribute)
220
+ DSL::MethodInjector.inject!(self, attribute)
221
+ end
222
+
223
+ private
224
+
225
+ # Handle class inheritance for attributes.
226
+ #
227
+ # Ensures that subclasses inherit a copy of their parent's attributes while
228
+ # maintaining proper ownership relationships.
229
+ #
230
+ # @param subclass [Class] the inheriting class
231
+ # @return [void]
232
+ # @rbs (Class | Module subclass) -> void
233
+ def inherited(subclass)
234
+ super
235
+ subclass.instance_variable_set(:@__attributes__, __attributes__.dup_with_base(subclass))
236
+ end
237
+
238
+ # Get the attribute set for this class.
239
+ #
240
+ # @return [AttributeSet] the set of attributes defined for this class
241
+ # @rbs () -> AttributeSet
242
+ def __attributes__
243
+ @__attributes__ ||= AttributeSet.new(self)
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Domainic
4
+ module Attributer
5
+ module DSL
6
+ class AttributeBuilder
7
+ # A class responsible for parsing and normalizing attribute options.
8
+ #
9
+ # This class handles the conversion of flexible DSL options into a normalized
10
+ # format for attribute creation. It supports multiple ways of specifying common
11
+ # options (like visibility, nullability, validation) and consolidates them
12
+ # into a consistent internal representation.
13
+ #
14
+ # @author {https://aaronmallen.me Aaron Allen}
15
+ # @since 0.1.0
16
+ class OptionParser
17
+ # @rbs!
18
+ # type options = {
19
+ # ?callbacks: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
20
+ # ?callback: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
21
+ # ?coerce: Array[Attribute::Coercer::handler] | Attribute::Coercer::handler,
22
+ # ?coercers: Array[Attribute::Coercer::handler],
23
+ # ?coerce_with: [Attribute::Coercer::handler] | Attribute::Coercer::handler,
24
+ # ?default: untyped,
25
+ # ?default_generator: untyped,
26
+ # ?default_value: untyped,
27
+ # ?desc: String?,
28
+ # ?description: String,
29
+ # ?non_nil: bool,
30
+ # ?non_null: bool,
31
+ # ?non_nullable: bool,
32
+ # ?not_nil: bool,
33
+ # ?not_nilable: bool,
34
+ # ?not_null: bool,
35
+ # ?not_nullable: bool,
36
+ # ?null: bool,
37
+ # ?on_change: Array[Attribute::Callback::handler] | Attribute::Callback::handler,
38
+ # ?optional: bool,
39
+ # ?position: Integer?,
40
+ # ?read: Attribute::Signature::visibility_symbol,
41
+ # ?read_access: Attribute::Signature::visibility_symbol,
42
+ # ?reader: Attribute::Signature::visibility_symbol,
43
+ # ?required: bool,
44
+ # ?validate: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
45
+ # ?validate_with: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
46
+ # ?validators: Array[Attribute::Validator::handler] | Attribute::Validator::handler,
47
+ # ?write_access: Attribute::Signature::visibility_symbol,
48
+ # ?writer: Attribute::Signature::visibility_symbol,
49
+ # } & Hash[Symbol, untyped]
50
+ #
51
+ # type result = {
52
+ # callbacks: Array[Attribute::Callback::handler],
53
+ # coercers: Array[Attribute::Coercer::handler],
54
+ # ?default: untyped,
55
+ # ?description: String,
56
+ # name: Symbol,
57
+ # ?nilable: bool,
58
+ # ?position: Integer?,
59
+ # ?required: bool,
60
+ # ?read: Attribute::Signature::visibility_symbol,
61
+ # type: Attribute::Signature::type_symbol,
62
+ # validators: Array[Attribute::Validator::handler],
63
+ # ?write: Attribute::Signature::visibility_symbol,
64
+ # }
65
+
66
+ # Alternative keys for reader visibility settings
67
+ ACCESSOR_READER_KEYS = %i[read read_access reader].freeze #: Array[Symbol]
68
+ private_constant :ACCESSOR_READER_KEYS
69
+
70
+ # Alternative keys for writer visibility settings
71
+ ACCESSOR_WRITER_KEYS = %i[write write_access writer].freeze #: Array[Symbol]
72
+ private_constant :ACCESSOR_WRITER_KEYS
73
+
74
+ # Alternative keys for change callbacks
75
+ CALLBACK_KEYS = %i[callback on_change callbacks].freeze #: Array[Symbol]
76
+ private_constant :CALLBACK_KEYS
77
+
78
+ # Alternative keys for coercion handlers
79
+ COERCER_KEYS = %i[coerce coercers coerce_with].freeze #: Array[Symbol]
80
+ private_constant :COERCER_KEYS
81
+
82
+ # Alternative keys for default value settings
83
+ DEFAULT_KEYS = %i[default_value default_generator default].freeze #: Array[Symbol]
84
+ private_constant :DEFAULT_KEYS
85
+
86
+ # Alternative keys for description
87
+ DESCRIPTION_KEYS = %i[desc description].freeze #: Array[Symbol]
88
+ private_constant :DESCRIPTION_KEYS
89
+
90
+ # Pattern for matching nilability-related keys
91
+ NILABLE_PATTERN = /\A(?:not_|non_)?(?:nil|null)\z/ #: Regexp
92
+ private_constant :NILABLE_PATTERN
93
+
94
+ # Keys that indicate non-nilable requirement
95
+ NON_NILABLE_KEYS = %i[
96
+ non_nil non_nilable not_nil not_nilable
97
+ non_null non_nullable not_null not_nullable
98
+ ].freeze #: Array[Symbol]
99
+ private_constant :NON_NILABLE_KEYS
100
+
101
+ # Alternative keys for validators
102
+ VALIDATOR_KEYS = %i[validate validate_with validators].freeze #: Array[Symbol]
103
+ private_constant :VALIDATOR_KEYS
104
+
105
+ # @rbs @options: options
106
+ # @rbs @result: result
107
+
108
+ # Parse attribute options into a normalized format.
109
+ #
110
+ # @param attribute_name [String, Symbol] the name of the attribute
111
+ # @param attribute_type [String, Symbol] the type of attribute
112
+ # @param options [Hash] the options to parse
113
+ #
114
+ # @return [Hash] normalized options suitable for attribute creation
115
+ # @rbs (String | Symbol attribute_name, String | Symbol attribute_type, options options) -> void
116
+ def self.parse!(attribute_name, attribute_type, options)
117
+ new(attribute_name, attribute_type, options).parse!
118
+ end
119
+
120
+ # Initialize a new OptionParser.
121
+ #
122
+ # @param attribute_name [String, Symbol] the name of the attribute
123
+ # @param attribute_type [String, Symbol] the type of attribute
124
+ # @param options [Hash] the options to parse
125
+ #
126
+ # @return [void]
127
+ # @rbs (String | Symbol attribute_name, String | Symbol attribute_type, options options) -> void
128
+ def initialize(attribute_name, attribute_type, options)
129
+ @options = options.transform_keys(&:to_sym)
130
+ @result = { callbacks: [], coercers: [], validators: [] }
131
+ @result[:name] = attribute_name.to_sym
132
+ @result[:type] = attribute_type.to_sym
133
+ @result[:position] = @options[:position] if @options.key?(:position)
134
+ end
135
+
136
+ # Parse the options into a normalized format.
137
+ #
138
+ # @return [Hash] normalized options suitable for attribute creation
139
+ # @rbs () -> result
140
+ def parse!
141
+ parse_options!
142
+ @result
143
+ end
144
+
145
+ private
146
+
147
+ # Find the last set value among multiple option keys.
148
+ #
149
+ # @param keys [Array<Symbol>] the keys to check
150
+ #
151
+ # @return [Object] the last set value or Undefined
152
+ # @rbs (Array[Symbol]) -> untyped
153
+ def find_last_option(keys)
154
+ keys.reverse_each do |key|
155
+ value = @options[key]
156
+ return value if value
157
+ end
158
+ Undefined
159
+ end
160
+
161
+ # Parse accessor (reader/writer) visibility options.
162
+ #
163
+ # @return [void]
164
+ # @rbs () -> void
165
+ def parse_accessor_options!
166
+ @result[:read] = find_last_option(ACCESSOR_READER_KEYS)
167
+ @result[:write] = find_last_option(ACCESSOR_WRITER_KEYS)
168
+ end
169
+
170
+ # Parse callback handler options.
171
+ #
172
+ # @return [void]
173
+ # @rbs () -> void
174
+ def parse_callbacks_options!
175
+ CALLBACK_KEYS.each do |key|
176
+ @result[:callbacks].concat(Array(@options[key])) if @options[key]
177
+ end
178
+ end
179
+
180
+ # Parse coercion handler options.
181
+ #
182
+ # @return [void]
183
+ # @rbs () -> void
184
+ def parse_coercers_options!
185
+ COERCER_KEYS.each do |key|
186
+ @result[:coercers].concat(Array(@options[key])) if @options[key]
187
+ end
188
+ end
189
+
190
+ # Parse default value options.
191
+ #
192
+ # @return [void]
193
+ # @rbs () -> void
194
+ def parse_default_options!
195
+ @result[:default] = find_last_option(DEFAULT_KEYS)
196
+ end
197
+
198
+ # Parse description options.
199
+ #
200
+ # @return [void]
201
+ # @rbs () -> void
202
+ def parse_description_options!
203
+ @result[:description] = find_last_option(DESCRIPTION_KEYS)
204
+ end
205
+
206
+ # Parse nilability options.
207
+ #
208
+ # @return [void]
209
+ # @rbs () -> void
210
+ def parse_nilable_options!
211
+ return unless @options.keys.any? { |key| key.match?(NILABLE_PATTERN) }
212
+
213
+ @result[:nilable] = !(NON_NILABLE_KEYS.any? { |k| @options[k] == true } || @options[:null] == false)
214
+ end
215
+
216
+ # Parse all option types.
217
+ #
218
+ # @return [void]
219
+ # @rbs () -> void
220
+ def parse_options!
221
+ private_methods.grep(/\Aparse_.*_options!\z/).each { |method| send(method) }
222
+ end
223
+
224
+ # Parse required/optional options.
225
+ #
226
+ # @return [void]
227
+ # @rbs () -> void
228
+ def parse_required_options!
229
+ return unless @options.key?(:optional) || @options.key?(:required)
230
+
231
+ @result[:required] = @options[:optional] == false || @options[:required] == true
232
+ end
233
+
234
+ # Parse validator options.
235
+ #
236
+ # @return [void]
237
+ # @rbs () -> void
238
+ def parse_validator_options!
239
+ VALIDATOR_KEYS.each do |key|
240
+ @result[:validators].concat(Array(@options.fetch(key, [])))
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end