@@ -172,6 +172,72 @@ def configure_hash(config_item_name, with:)
172172 ch . const_set ( config_item_name . ns_camelize , c )
173173 end
174174
175+ #
176+ # Class method for declaring that a class stores a single direct value of a given type.
177+ # This provides a consistent mechanism for defining configuration classes that hold
178+ # one typed value (e.g., a string, array, hash, or if_condition) rather than
179+ # multiple named attributes.
180+ #
181+ # The type metadata allows future validation and coercion. Currently supported types:
182+ # - :string — stores a String value
183+ # - :array — stores an Array value
184+ # - :hash — stores an arbitrary Hash value
185+ # - :if_condition — stores a conditional Hash (for access/validation conditions)
186+ #
187+ # Example:
188+ # class Label < SomeBase
189+ # configure_direct :label, type: :string
190+ # end
191+ #
192+ # @param [Symbol] config_item_name - the name of the configuration item
193+ # @param [Symbol] type - the value type (:string, :array, :hash, :if_condition)
194+ def configure_direct ( config_item_name , type :)
195+ attr_accessor ( config_item_name ) unless method_defined? ( config_item_name )
196+
197+ add_option_type ( :direct , config_item_name )
198+
199+ # Store type metadata for future validation/coercion
200+ @direct_types ||= { }
201+ @direct_types [ config_item_name ] = type
202+ end
203+
204+ #
205+ # Returns the registered direct types for this class.
206+ # @return [Hash{Symbol => Symbol}] mapping of attribute name to type
207+ def direct_types
208+ @direct_types || { }
209+ end
210+
211+ #
212+ # Class method for declaring a typed attribute whose value is an instance of
213+ # a class inheriting from BaseConfiguration.
214+ #
215+ # When the including class is initialized with a hash configuration, the
216+ # attribute value is automatically set by passing the corresponding hash
217+ # entry to the type class constructor.
218+ #
219+ # @param [Symbol] config_item_name - the attribute name
220+ # @param [Class] type - a class inheriting from BaseConfiguration that
221+ # accepts a hash in its constructor
222+ #
223+ # @example
224+ # configure_typed_attribute :creatable_if, type: ExtraOptionConfigs::IfCondition
225+ def configure_typed_attribute ( config_item_name , type :)
226+ attr_accessor ( config_item_name ) unless method_defined? ( config_item_name )
227+
228+ add_option_type ( :typed , config_item_name )
229+
230+ @typed_attribute_types ||= { }
231+ @typed_attribute_types [ config_item_name ] = type
232+ end
233+
234+ #
235+ # Returns the registered typed attribute types for this class.
236+ # @return [Hash{Symbol => Class}] mapping of attribute name to type class
237+ def typed_attribute_types
238+ @typed_attribute_types || { }
239+ end
240+
175241 #
176242 # List of configuration items having child options.
177243 # Each represents the name of an accessor attribute in this model
@@ -180,7 +246,9 @@ def option_types
180246 @option_types ||= {
181247 multi : [ ] ,
182248 simple : [ ] ,
183- hash : [ ]
249+ hash : [ ] ,
250+ direct : [ ] ,
251+ typed : [ ]
184252 }
185253 end
186254
@@ -303,6 +371,7 @@ def setup_from_hash_config
303371 setup_all_options_multi hash_configuration
304372 setup_all_options_simple hash_configuration
305373 setup_all_options_hash hash_configuration
374+ setup_all_options_typed hash_configuration
306375
307376 hash_configuration
308377 end
@@ -322,6 +391,14 @@ def setup_all_options_simple(hash_configuration)
322391 end
323392 end
324393
394+ def setup_all_options_typed ( hash_configuration )
395+ self . class . option_types [ :typed ] &.each do |option_type |
396+ type_class = self . class . typed_attribute_types [ option_type ]
397+ config_val = hash_configuration [ option_type ]
398+ send ( "#{ option_type } =" , type_class . new ( config_val ) )
399+ end
400+ end
401+
325402 def setup_all_options_hash ( hash_configuration )
326403 self . class . option_types [ :hash ] . each do |option_type |
327404 setup_options_hash ( hash_configuration , option_type )
0 commit comments