lib/configurable/class_methods.rb in configurable-0.4.0 vs lib/configurable/class_methods.rb in configurable-0.4.1

- old
+ new

@@ -253,33 +253,30 @@ # const_name:: Determines the constant name of the configurable # class within the nesting class. May be nil. # (default: key.to_s.capitalize) # instance_reader:: The method accessing the nested instance. (default: key) # instance_writer:: The method to set the nested instance. (default: "#{key}=") - # instance_initializer:: The method that initializes the instance. - # (default: "initialize_#{key}") - # reader:: The method used to read the instance configuration. + # reader:: The method used to read the instance config. # (default: "#{key}_config_reader") - # writer:: The method used to initialize or reconfigure the - # instance. (default: "#{key}_config_writer") + # writer:: The method used to reconfigure the instance. + # (default: "#{key}_config_writer") # # Except for const_name, these attributes are used to define methods # required for nesting to work properly. None of the method attributes may # be set to nil, but they may be set to non-default values. In that case, # nest will register the method names as provided, but it will not define # the methods themselves. The user must define methods with the following # functionality: # # Attribute:: Function - # instance_reader:: Returns the instance of the configurable class + # instance_reader:: Returns the instance of the configurable class + # (initializing if necessary, by default nest initializes + # using configurable_class.new) # instance_writer:: Inputs and sets the instance of the configurable class - # instance_initializer:: Receives the initial config and return an instance of - # configurable class # reader:: Returns instance.config - # writer:: Reconfigures instance using the input overrides, - # or uses instance_initializer and instance_writer to - # initialize and set the instance. + # writer:: Reconfigures instance using the input overrides, or + # sets instance if provided. # # Methods can be public or otherwise. Specifying true uses and defines the # default methods. Specifying false uses the default method name, but does # not define the method itself. # @@ -287,11 +284,10 @@ def nest(key, configurable_class=nil, attributes={}, &block) attributes = merge_attributes(block, attributes) attributes = { :instance_reader => true, :instance_writer => true, - :initializer => true }.merge(attributes) # define the nested configurable if configurable_class raise "a block is not allowed when a configurable class is specified" if block_given? @@ -308,39 +304,45 @@ end const_set(const_name, configurable_class) if const_name # define instance reader instance_reader = define_attribute_method(:instance_reader, attributes, key) do |attribute| - attr_reader(key) + instance_variable = "@#{key}".to_sym + + # gets or initializes the instance + define_method(attribute) do + if instance_variable_defined?(instance_variable) + instance_variable_get(instance_variable) + else + instance_variable_set(instance_variable, configurable_class.new) + end + end + public(key) end # define instance writer instance_writer = define_attribute_method(:instance_writer, attributes, "#{key}=") do |attribute| attr_writer(key) public(attribute) end - # define initializer - initializer = define_attribute_method(:initializer, attributes, "initialize_#{key}") do |attribute| - define_method(attribute) {|config| configurable_class.new.reconfigure(config) } - private(attribute) - end - # define the reader reader = define_attribute_method(:reader, attributes, "#{key}_config_reader") do |attribute| - define_method(attribute) { send(instance_reader).config } + define_method(attribute) do + send(instance_reader).config + end private(attribute) end # define the writer writer = define_attribute_method(:writer, attributes, "#{key}_config_writer") do |attribute| define_method(attribute) do |value| - if instance = send(instance_reader) - instance.reconfigure(value) + if value.kind_of?(configurable_class) + send(instance_writer, value) else - send(instance_writer, send(initializer, value)) + send(instance_reader).reconfigure(value) end end private(attribute) end @@ -455,9 +457,36 @@ # Ensures the insertion order of duplicates is separate from parents. def initialize_copy(orig) super @insertion_order = orig.instance_variable_get(:@insertion_order).dup + end + + # Overridden to load an array of [key, value] pairs in order (see to_yaml). + # The default behavior for loading from a hash of key-value pairs is + # preserved, but the insertion order will not be preserved. + def yaml_initialize( tag, val ) + @insertion_order ||= [] + + if Array === val + val.each do |k, v| + self[k] = v + end + else + super + end + end + + # Overridden to preserve insertion order by serializing self as an array + # of [key, value] pairs. + def to_yaml( opts = {} ) + YAML::quick_emit( object_id, opts ) do |out| + out.seq( taguri, to_yaml_style ) do |seq| + each_pair do |key, value| + seq.add( [key, value] ) + end + end + end end end module ClassMethods undef_method :initialize_configurations \ No newline at end of file