domainic-attributer 0.1.0
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 +7 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE +21 -0
- data/README.md +396 -0
- data/lib/domainic/attributer/attribute/callback.rb +68 -0
- data/lib/domainic/attributer/attribute/coercer.rb +93 -0
- data/lib/domainic/attributer/attribute/mixin/belongs_to_attribute.rb +68 -0
- data/lib/domainic/attributer/attribute/signature.rb +338 -0
- data/lib/domainic/attributer/attribute/validator.rb +128 -0
- data/lib/domainic/attributer/attribute.rb +256 -0
- data/lib/domainic/attributer/attribute_set.rb +208 -0
- data/lib/domainic/attributer/class_methods.rb +247 -0
- data/lib/domainic/attributer/dsl/attribute_builder/option_parser.rb +247 -0
- data/lib/domainic/attributer/dsl/attribute_builder.rb +233 -0
- data/lib/domainic/attributer/dsl/initializer.rb +130 -0
- data/lib/domainic/attributer/dsl/method_injector.rb +97 -0
- data/lib/domainic/attributer/dsl.rb +5 -0
- data/lib/domainic/attributer/instance_methods.rb +65 -0
- data/lib/domainic/attributer/undefined.rb +44 -0
- data/lib/domainic/attributer.rb +114 -0
- data/lib/domainic-attributer.rb +3 -0
- data/sig/domainic/attributer/attribute/callback.rbs +48 -0
- data/sig/domainic/attributer/attribute/coercer.rbs +59 -0
- data/sig/domainic/attributer/attribute/mixin/belongs_to_attribute.rbs +46 -0
- data/sig/domainic/attributer/attribute/signature.rbs +223 -0
- data/sig/domainic/attributer/attribute/validator.rbs +83 -0
- data/sig/domainic/attributer/attribute.rbs +150 -0
- data/sig/domainic/attributer/attribute_set.rbs +134 -0
- data/sig/domainic/attributer/class_methods.rbs +151 -0
- data/sig/domainic/attributer/dsl/attribute_builder/option_parser.rbs +130 -0
- data/sig/domainic/attributer/dsl/attribute_builder.rbs +156 -0
- data/sig/domainic/attributer/dsl/initializer.rbs +91 -0
- data/sig/domainic/attributer/dsl/method_injector.rbs +66 -0
- data/sig/domainic/attributer/dsl.rbs +1 -0
- data/sig/domainic/attributer/instance_methods.rbs +53 -0
- data/sig/domainic/attributer/undefined.rbs +14 -0
- data/sig/domainic/attributer.rbs +69 -0
- data/sig/domainic-attributer.rbs +1 -0
- data/sig/manifest.yaml +2 -0
- 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
|