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,256 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/attributer/attribute/callback'
|
4
|
+
require 'domainic/attributer/attribute/coercer'
|
5
|
+
require 'domainic/attributer/attribute/signature'
|
6
|
+
require 'domainic/attributer/attribute/validator'
|
7
|
+
require 'domainic/attributer/undefined'
|
8
|
+
|
9
|
+
module Domainic
|
10
|
+
module Attributer
|
11
|
+
# A class representing a managed attribute in the Domainic::Attributer system.
|
12
|
+
#
|
13
|
+
# This class serves as the core component of the attribute management system.
|
14
|
+
# It coordinates type information, visibility settings, value coercion,
|
15
|
+
# validation, and change notifications for an attribute. Each instance
|
16
|
+
# represents a single attribute definition within a class.
|
17
|
+
#
|
18
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
19
|
+
# @since 0.1.0
|
20
|
+
class Attribute
|
21
|
+
# @rbs!
|
22
|
+
# type initialize_options = {
|
23
|
+
# ?callbacks: Array[Callback::handler] | Callback::handler,
|
24
|
+
# ?coercers: Array[Coercer::handler] | Coercer::handler,
|
25
|
+
# ?default: untyped,
|
26
|
+
# ?description: String?,
|
27
|
+
# name: String | Symbol,
|
28
|
+
# ?nilable: bool,
|
29
|
+
# ?position: Integer?,
|
30
|
+
# ?read: Signature::visibility_symbol,
|
31
|
+
# ?required: bool,
|
32
|
+
# type: Signature::type_symbol,
|
33
|
+
# ?validators: Array[Validator::handler] | Validator::handler,
|
34
|
+
# ?write: Signature::visibility_symbol
|
35
|
+
# }
|
36
|
+
|
37
|
+
# @rbs @base: __todo__
|
38
|
+
# @rbs @callback: Callback
|
39
|
+
# @rbs @coercer: Coercer
|
40
|
+
# @rbs @default: untyped
|
41
|
+
# @rbs @description: String?
|
42
|
+
# @rbs @name: Symbol
|
43
|
+
# @rbs @signature: Signature
|
44
|
+
# @rbs @validator: Validator
|
45
|
+
|
46
|
+
# @return [Class, Module] the class or module this attribute belongs to
|
47
|
+
attr_reader :base #: __todo__
|
48
|
+
|
49
|
+
# @return [String, nil] the description of the attribute
|
50
|
+
attr_reader :description #: String?
|
51
|
+
|
52
|
+
# @return [Symbol] the name of the attribute
|
53
|
+
attr_reader :name #: Symbol
|
54
|
+
|
55
|
+
# @return [Signature] the signature configuration for this attribute
|
56
|
+
attr_reader :signature #: Signature
|
57
|
+
|
58
|
+
# Initialize a new Attribute instance.
|
59
|
+
#
|
60
|
+
# @param base [Class, Module] the class or module this attribute belongs to
|
61
|
+
# @param options [Hash] the options to create the attribute with
|
62
|
+
# @option options [Array<Proc>, Proc] :callbacks callbacks to trigger on value changes
|
63
|
+
# @option options [Array<Proc, Symbol>, Proc, Symbol] :coercers handlers for value coercion
|
64
|
+
# @option options [Object] :default the default value or generator
|
65
|
+
# @option options [String] :description a description of the attribute
|
66
|
+
# @option options [String, Symbol] :name the name of the attribute
|
67
|
+
# @option options [Boolean] :nilable (true) whether the attribute can be nil
|
68
|
+
# @option options [Integer] :position the position for ordered attributes
|
69
|
+
# @option options [Symbol] :read the read visibility
|
70
|
+
# @option options [Boolean] :required (false) whether the attribute is required
|
71
|
+
# @option options [Symbol] :type the type of attribute
|
72
|
+
# @option options [Array<Proc, Object>, Proc, Object] :validators handlers for value validation
|
73
|
+
# @option options [Symbol] :write the write visibility
|
74
|
+
#
|
75
|
+
# @raise [ArgumentError] if the configuration is invalid
|
76
|
+
# @return [void]
|
77
|
+
#
|
78
|
+
# @rbs (
|
79
|
+
# __todo__ base,
|
80
|
+
# ?callbacks: Array[Callback::handler] | Callback::handler,
|
81
|
+
# ?coercers: Array[Coercer::handler] | Coercer::handler,
|
82
|
+
# ?default: untyped,
|
83
|
+
# ?description: String?,
|
84
|
+
# name: String | Symbol,
|
85
|
+
# ?nilable: bool,
|
86
|
+
# ?position: Integer?,
|
87
|
+
# ?read: Signature::visibility_symbol,
|
88
|
+
# ?required: bool,
|
89
|
+
# type: Signature::type_symbol,
|
90
|
+
# ?validators: Array[Validator::handler] | Validator::handler,
|
91
|
+
# ?write: Signature::visibility_symbol
|
92
|
+
# ) -> void
|
93
|
+
def initialize(base, **options)
|
94
|
+
options = options.transform_keys(&:to_sym)
|
95
|
+
# @type var options: initialize_options
|
96
|
+
validate_and_apply_initialize_options!(base, options)
|
97
|
+
rescue StandardError => e
|
98
|
+
raise ArgumentError, e.message
|
99
|
+
end
|
100
|
+
|
101
|
+
# Apply a value to the attribute on an instance.
|
102
|
+
#
|
103
|
+
# This method applies all attribute constraints (coercion, validation) to a value
|
104
|
+
# and sets it on the given instance. It manages the complete lifecycle of setting
|
105
|
+
# an attribute value including:
|
106
|
+
# 1. Handling default values
|
107
|
+
# 2. Coercing the value
|
108
|
+
# 3. Validating the result
|
109
|
+
# 4. Setting the value
|
110
|
+
# 5. Triggering callbacks
|
111
|
+
#
|
112
|
+
# @param instance [Object] the instance to set the value on
|
113
|
+
# @param value [Object] the value to set
|
114
|
+
#
|
115
|
+
# @raise [ArgumentError] if the value is invalid
|
116
|
+
# @return [void]
|
117
|
+
# @rbs (untyped instance, untyped value) -> void
|
118
|
+
def apply!(instance, value = Undefined)
|
119
|
+
old_value = instance.instance_variable_get(:"@#{name}")
|
120
|
+
|
121
|
+
coerced_value = value == Undefined ? generate_default(instance) : value
|
122
|
+
coerced_value = @coercer.call(instance, coerced_value) unless coerced_value == Undefined
|
123
|
+
|
124
|
+
@validator.call(instance, coerced_value)
|
125
|
+
|
126
|
+
instance.instance_variable_set(:"@#{name}", coerced_value == Undefined ? nil : coerced_value)
|
127
|
+
|
128
|
+
@callback.call(instance, old_value, coerced_value)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Check if this attribute has a default value.
|
132
|
+
#
|
133
|
+
# @return [Boolean] true if a default value is set
|
134
|
+
# @rbs () -> bool
|
135
|
+
def default?
|
136
|
+
@default != Undefined
|
137
|
+
end
|
138
|
+
|
139
|
+
# Create a duplicate instance for a new base class.
|
140
|
+
#
|
141
|
+
# @param new_base [Class, Module] the new base class
|
142
|
+
#
|
143
|
+
# @return [Attribute] the duplicated instance
|
144
|
+
# @rbs (__todo__ new_base) -> Attribute
|
145
|
+
def dup_with_base(new_base)
|
146
|
+
raise ArgumentError, "invalid base: #{new_base}" unless new_base.is_a?(Class) || new_base.is_a?(Module)
|
147
|
+
|
148
|
+
dup.tap { |duped| duped.instance_variable_set(:@base, new_base) }
|
149
|
+
end
|
150
|
+
|
151
|
+
# Generate the default value for this attribute.
|
152
|
+
#
|
153
|
+
# @param instance [Object] the instance to generate the default for
|
154
|
+
#
|
155
|
+
# @return [Object] the generated default value
|
156
|
+
# @rbs (untyped instance) -> untyped
|
157
|
+
def generate_default(instance)
|
158
|
+
@default.is_a?(Proc) ? instance.instance_exec(&@default) : @default
|
159
|
+
end
|
160
|
+
|
161
|
+
# Merge this attribute's configuration with another.
|
162
|
+
#
|
163
|
+
# @param other [Attribute] the attribute to merge with
|
164
|
+
#
|
165
|
+
# @raise [ArgumentError] if other is not an Attribute
|
166
|
+
# @return [Attribute] a new attribute with merged configuration
|
167
|
+
# @rbs (Attribute other) -> Attribute
|
168
|
+
def merge(other)
|
169
|
+
raise ArgumentError, 'other must be an instance of Attribute' unless other.is_a?(self.class)
|
170
|
+
|
171
|
+
self.class.new(other.base, **to_options, **other.send(:to_options)) # steep:ignore InsufficientKeywordArguments
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
# Apply initialization options to create attribute components.
|
177
|
+
#
|
178
|
+
# @param base [Class, Module] the base class
|
179
|
+
# @param options [Hash] the initialization options
|
180
|
+
#
|
181
|
+
# @return [void]
|
182
|
+
# @rbs (__todo__ base, initialize_options options) -> void
|
183
|
+
def apply_initialize_options!(base, options)
|
184
|
+
@base = base
|
185
|
+
@callback = Callback.new(self, options.fetch(:callbacks, []))
|
186
|
+
@coercer = Coercer.new(self, options.fetch(:coercers, []))
|
187
|
+
@default = options.fetch(:default, Undefined)
|
188
|
+
@description = options.fetch(:description, nil)
|
189
|
+
@name = options.fetch(:name).to_sym
|
190
|
+
@signature = Signature.new(
|
191
|
+
self, type: options.fetch(:type), **options.slice(:nilable, :position, :read, :required, :write)
|
192
|
+
)
|
193
|
+
@validator = Validator.new(self, options.fetch(:validators, []))
|
194
|
+
end
|
195
|
+
|
196
|
+
# Initialize a copy of this attribute.
|
197
|
+
#
|
198
|
+
# @param source [Attribute] the source attribute
|
199
|
+
#
|
200
|
+
# @return [Attribute] the initialized copy
|
201
|
+
# @rbs override
|
202
|
+
def initialize_copy(source)
|
203
|
+
@base = source.base
|
204
|
+
@callback = source.instance_variable_get(:@callback).dup_with_attribute(self)
|
205
|
+
@coercer = source.instance_variable_get(:@coercer).dup_with_attribute(self)
|
206
|
+
@default = source.instance_variable_get(:@default)
|
207
|
+
@description = source.description
|
208
|
+
@name = source.name
|
209
|
+
@signature = source.signature.dup_with_attribute(self)
|
210
|
+
@validator = source.instance_variable_get(:@validator).dup_with_attribute(self)
|
211
|
+
super
|
212
|
+
end
|
213
|
+
|
214
|
+
# Get this attribute's configuration as options.
|
215
|
+
#
|
216
|
+
# @return [Hash] the configuration options
|
217
|
+
# @rbs () -> initialize_options
|
218
|
+
def to_options
|
219
|
+
{
|
220
|
+
callbacks: @callback.instance_variable_get(:@handlers),
|
221
|
+
coercers: @coercer.instance_variable_get(:@handlers),
|
222
|
+
default: @default,
|
223
|
+
description: @description,
|
224
|
+
name: @name,
|
225
|
+
validators: @validator.instance_variable_get(:@handlers)
|
226
|
+
}.merge(signature.send(:to_options)) #: initialize_options
|
227
|
+
end
|
228
|
+
|
229
|
+
# Validate and apply initialization options.
|
230
|
+
#
|
231
|
+
# @param base [Class, Module] the base class
|
232
|
+
# @param options [Hash] the initialization options
|
233
|
+
#
|
234
|
+
# @return [void]
|
235
|
+
# @rbs (__todo__ base, initialize_options options) -> void
|
236
|
+
def validate_and_apply_initialize_options!(base, options)
|
237
|
+
validate_initialize_options!(base, options)
|
238
|
+
apply_initialize_options!(base, options)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Validate initialization options.
|
242
|
+
#
|
243
|
+
# @param base [Class, Module] the base class
|
244
|
+
# @param options [Hash] the initialization options
|
245
|
+
#
|
246
|
+
# @raise [ArgumentError] if any options are invalid
|
247
|
+
# @return [void]
|
248
|
+
# @rbs (__todo__ base, initialize_options options) -> void
|
249
|
+
def validate_initialize_options!(base, options)
|
250
|
+
raise ArgumentError, "invalid base: #{base}" unless base.is_a?(Class) || base.is_a?(Module)
|
251
|
+
raise ArgumentError, 'missing keyword :name' unless options.key?(:name)
|
252
|
+
raise ArgumentError, 'missing keyword :type' unless options.key?(:type)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'domainic/attributer/attribute'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
module Domainic
|
7
|
+
module Attributer
|
8
|
+
# A class representing an ordered collection of attributes.
|
9
|
+
#
|
10
|
+
# This class manages a set of attributes for a given class or module. It maintains
|
11
|
+
# attributes in a specific order determined by their type (argument vs option),
|
12
|
+
# default values, and position. The collection supports standard operations like
|
13
|
+
# adding, selecting, and merging attributes while maintaining proper ownership
|
14
|
+
# relationships with their base class.
|
15
|
+
#
|
16
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
17
|
+
# @since 0.1.0
|
18
|
+
class AttributeSet
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
# @rbs @base: __todo__
|
22
|
+
# @rbs @lookup: Hash[Symbol, Attribute]
|
23
|
+
|
24
|
+
# Initialize a new AttributeSet.
|
25
|
+
#
|
26
|
+
# @param base [Class, Module] the class or module this set belongs to
|
27
|
+
# @param attributes [Array<Attribute>] initial attributes to add
|
28
|
+
#
|
29
|
+
# @return [void]
|
30
|
+
# @rbs (__todo__ base, ?Array[Attribute] attributes) -> void
|
31
|
+
def initialize(base, attributes = [])
|
32
|
+
@base = base
|
33
|
+
@lookup = {}
|
34
|
+
attributes.each { |attribute| add(attribute) }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get an attribute by name.
|
38
|
+
#
|
39
|
+
# @param attribute_name [String, Symbol] the name of the attribute
|
40
|
+
#
|
41
|
+
# @return [Attribute, nil] the attribute if found
|
42
|
+
# @rbs (String | Symbol attribute_name) -> Attribute?
|
43
|
+
def [](attribute_name)
|
44
|
+
@lookup[attribute_name.to_sym]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add an attribute to the set.
|
48
|
+
#
|
49
|
+
# If an attribute with the same name exists, the attributes are merged.
|
50
|
+
# If the attribute belongs to a different base class, it is duplicated
|
51
|
+
# with the correct base. After adding, attributes are sorted by type
|
52
|
+
# and position.
|
53
|
+
#
|
54
|
+
# @param attribute [Attribute] the attribute to add
|
55
|
+
#
|
56
|
+
# @raise [ArgumentError] if attribute is invalid
|
57
|
+
# @return [void]
|
58
|
+
# @rbs (Attribute attribute) -> void
|
59
|
+
def add(attribute)
|
60
|
+
raise ArgumentError, "Invalid attribute: #{attribute.inspect}" unless attribute.is_a?(Attribute)
|
61
|
+
|
62
|
+
@lookup[attribute.name] = if @lookup.key?(attribute.name)
|
63
|
+
@lookup[attribute.name].merge(attribute).dup_with_base(@base)
|
64
|
+
elsif attribute.base != @base
|
65
|
+
attribute.dup_with_base(@base)
|
66
|
+
else
|
67
|
+
attribute
|
68
|
+
end
|
69
|
+
|
70
|
+
sort_lookup
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check if an attribute exists in the set.
|
75
|
+
#
|
76
|
+
# @param attribute_name [String, Symbol] the name to check
|
77
|
+
#
|
78
|
+
# @return [Boolean] true if the attribute exists
|
79
|
+
def attribute?(attribute_name)
|
80
|
+
@lookup.key?(attribute_name.to_sym)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get all attribute names.
|
84
|
+
#
|
85
|
+
# @return [Array<Symbol>] the attribute names
|
86
|
+
# @rbs () -> Array[Symbol]
|
87
|
+
def attribute_names
|
88
|
+
@lookup.keys
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get all attributes.
|
92
|
+
#
|
93
|
+
# @return [Array<Attribute>] the attributes
|
94
|
+
# @rbs () -> Array[Attribute]
|
95
|
+
def attributes
|
96
|
+
@lookup.values
|
97
|
+
end
|
98
|
+
|
99
|
+
# @rbs! def count: () ?{ (Symbol, Attribute) -> boolish } -> Integer
|
100
|
+
def_delegators :@lookup, :count
|
101
|
+
|
102
|
+
# Create a duplicate set for a new base class.
|
103
|
+
#
|
104
|
+
# @param new_base [Class, Module] the new base class
|
105
|
+
#
|
106
|
+
# @return [AttributeSet] the duplicated set
|
107
|
+
# @rbs (__todo__ base) -> AttributeSet
|
108
|
+
def dup_with_base(new_base)
|
109
|
+
dup.tap do |duped|
|
110
|
+
duped.instance_variable_set(:@base, new_base)
|
111
|
+
duped.instance_variable_set(
|
112
|
+
:@lookup,
|
113
|
+
@lookup.transform_values { |attribute| attribute.dup_with_base(new_base) }
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Iterate over attribute name/value pairs.
|
119
|
+
#
|
120
|
+
# @yield [name, attribute] each name/attribute pair
|
121
|
+
# @yieldparam name [Symbol] the attribute name
|
122
|
+
# @yieldparam attribute [Attribute] the attribute
|
123
|
+
#
|
124
|
+
# @return [self]
|
125
|
+
# @rbs () { ([Symbol, Attribute]) -> untyped } -> self
|
126
|
+
def each(...)
|
127
|
+
@lookup.each(...)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
alias each_pair each
|
131
|
+
|
132
|
+
# @rbs! def empty?: () -> bool
|
133
|
+
def_delegators :@lookup, :empty?
|
134
|
+
|
135
|
+
# Create a new set excluding specified attributes.
|
136
|
+
#
|
137
|
+
# @param attribute_names [Array<String, Symbol>] names to exclude
|
138
|
+
#
|
139
|
+
# @return [AttributeSet] new set without specified attributes
|
140
|
+
# @rbs (*String | Symbol attribute_names) -> AttributeSet
|
141
|
+
def except(*attribute_names)
|
142
|
+
self.class.new(@base, @lookup.except(*attribute_names.map(&:to_sym)).values)
|
143
|
+
end
|
144
|
+
|
145
|
+
# @rbs! def length: () -> Integer
|
146
|
+
def_delegators :@lookup, :length
|
147
|
+
|
148
|
+
# Merge another set into this one.
|
149
|
+
#
|
150
|
+
# @param other [AttributeSet] the set to merge
|
151
|
+
#
|
152
|
+
# @return [AttributeSet] new set with merged attributes
|
153
|
+
# @rbs (AttributeSet other) -> AttributeSet
|
154
|
+
def merge(other)
|
155
|
+
self.class.new(other.instance_variable_get(:@base), attributes + other.attributes)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Create a new set with rejected attributes.
|
159
|
+
#
|
160
|
+
# @yield [name, attribute] each name/attribute pair
|
161
|
+
# @yieldparam name [Symbol] the attribute name
|
162
|
+
# @yieldparam attribute [Attribute] the attribute
|
163
|
+
#
|
164
|
+
# @return [AttributeSet] new set without rejected attributes
|
165
|
+
# @rbs () { (Symbol, Attribute) -> boolish } -> AttributeSet
|
166
|
+
def reject(...)
|
167
|
+
self.class.new(@base, @lookup.reject(...).values)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Create a new set with selected attributes.
|
171
|
+
#
|
172
|
+
# @yield [name, attribute] each name/attribute pair
|
173
|
+
# @yieldparam name [Symbol] the attribute name
|
174
|
+
# @yieldparam attribute [Attribute] the attribute
|
175
|
+
#
|
176
|
+
# @return [AttributeSet] new set with selected attributes
|
177
|
+
# @rbs () { (Symbol, Attribute) -> boolish } -> AttributeSet
|
178
|
+
def select(...)
|
179
|
+
self.class.new(@base, @lookup.select(...).values)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @rbs! def size: () -> Integer
|
183
|
+
def_delegators :@lookup, :size
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
# Sort attributes by type and position.
|
188
|
+
#
|
189
|
+
# Attributes are sorted first by type (required arguments, defaulted arguments,
|
190
|
+
# then options), and then by their position within those groups.
|
191
|
+
#
|
192
|
+
# @return [void]
|
193
|
+
# @rbs () -> void
|
194
|
+
def sort_lookup
|
195
|
+
@lookup = @lookup.sort_by do |_, attribute|
|
196
|
+
[
|
197
|
+
if attribute.signature.option?
|
198
|
+
2
|
199
|
+
else
|
200
|
+
(attribute.default? ? 1 : 0)
|
201
|
+
end,
|
202
|
+
attribute.signature.position
|
203
|
+
]
|
204
|
+
end.to_h
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|