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