Class: ConfigToolkit::BaseConfig
- Inherits:
-
Object
- Object
- ConfigToolkit::BaseConfig
- Defined in:
- lib/configtoolkit/baseconfig.rb
Overview
This class can be subclassed to create a class representing a configuration. It provides configuration specification, loading, and dumping functionality. A BaseConfig configuration can contain “scalar” parameters (anything except an array or nested configuration class), ConstrainedArray parameters (arrays), and other BaseConfig child classes parameters (which can be thought of as hashes). Note that nested ConstrainedArray and BaseConfig config parameters recursively can contain anything the parent BaseConfig can contain, so nesting arrays and configuration classes to any depth is supported.
A BaseConfig child instance has getter, setter, and predicate methods for each parameter. The predicate method for a parameter returns whether or not the parameter has a value and is named param_name?
. An optional parameter also has a method to clear its value from the configuration (after this the predicate method will return false
); the clear method is named clear_param_name
.
BaseConfig neither parses from nor writes to configuration files directly but instead does so through reader (Reader) and writer (Writer) classes specified by the programmer. This allows BaseConfig to support virtually any underlying configuration file format (including YAML, key/value pairs, etc). BaseConfig expects reader classes to contain a read
method that sources configuration information and returns a Hash that maps Symbols or Strings (parameter names) to values, which can themselves be Hashes or Arrays. It expects writer classes to support a write
method that in turn accepts such a Hash and writes it to some underlying stream. The reader and writer implementations used have no effect on BaseConfig’s validation functionality; data from any source (even specified programatically via setters) always is validated fully.
Programmers wishing to create their own configuration classes should extend BaseConfig and, in the body of their class’ definition, call add_required_param and add_optional_param for each parameter they wish to specify. If they wish to enforce a relationship between parameters, they also can override validate_all_values.
Direct Known Subclasses
Defined Under Namespace
Classes: ParamSpec
Constant Summary collapse
- NO_DEFAULT_VALUE =
This constant can be passed to add_optional_param in order to indicate that the parameter has no default value. The actual value of the constant does not matter in the least, so long as it never could be a real default value (a gemsym() function would be handy here to generate a unique symbol).
:baseconfig_no_default_value
Class Attribute Summary collapse
-
.param_spec_list ⇒ Object
readonly
This is an Array (class instance variable) containing the parameter specifications (ParamSpecs) in the order in which they were specified in the BaseConfig child class.
-
.param_spec_lookup_table ⇒ Object
readonly
This is a Hash (class instance variable) mapping paramater names (Symbols) to parameter specifications (ParamSpecs).
Instance Attribute Summary collapse
-
#containing_object_name ⇒ Object
readonly
This is a String containing the name of the object that contains this instance’s configuration.
Class Method Summary collapse
-
.inherited(child_class) ⇒ Object
Description: This is BaseConfig’s implementation of the inherited hook, which is called by the Ruby interpreter whenever BaseConfig is extended.
-
.load(reader, containing_object_name = "", config_toolkit_config = nil) ⇒ Object
Description: This class method creates a new config instance and calls load on it with the specified arguments.
-
.load_group(reader, containing_object_name = "", config_toolkit_config = nil) ⇒ Object
Description: This class method loads arbitrarily many configs from reader.
Instance Method Summary collapse
-
#==(rhs) ⇒ Object
Description: Equality operator for BaseConfig; this method iterates through the configuration’s parameters and, for each parameter, compares the value of
self
andrhs
using the value’s equality operator. -
#dump(writer) ⇒ Object
Description: This method writes
self
to a configuration file. -
#enforce_specs ⇒ Object
Description: This method enforces the BaseConfig’s specifications.
-
#initialize(config_toolkit_config = nil, &initialization_block) ⇒ BaseConfig
constructor
Description: This constructs a BaseConfig with no parameters set (each parameter initially has a
nil
value). -
#load(reader, containing_object_name = "") ⇒ Object
Description: This method loads the contents of a configuration file into
self
. -
#raise_error(reason) ⇒ Object
Description: This method raises an Error with message
reason
. -
#to_s ⇒ Object
Returns: String representation of
self
. -
#validate_all_values ⇒ Object
Description: This method enforces constraints between parameters; unless overriden, it is a no-op.
Constructor Details
#initialize(config_toolkit_config = nil, &initialization_block) ⇒ BaseConfig
Description:
This constructs a BaseConfig with no parameters set (each parameter initially has a nil
value).
If a block is specified, then it will be passed the new instance and should set the BaseConfig’s parameters fully (as if load had been called); the BaseConfig’s specifications will be verified by this method after it executes the block (with a call to enforce_specs).
Example:
class Config < ConfigToolkit::BaseConfig
add_required_param(:age, Fixnum)
end
empty_config = Config.new()
initialized_config = Config.new() do |config|
config.age = 5
end
# This will raise an exception, because the age parameter is
# required but is not being set by the block passed to new.
initalized_config = Config.new() do |config|
end
Parameters:
- config_toolkit_config
-
An optional ConfigToolkitConfig instance to configure the new configuration instance (this allows the loading/dumping process to be customized). If this parameter is not specified by the caller, then the value of BaseConfig.default_config_toolkit_config will be used.
- &initialization_block
-
A block that fully initializes the parameters of the new instance
520 521 522 523 524 525 526 527 528 529 530 531 |
# File 'lib/configtoolkit/baseconfig.rb', line 520 def initialize(config_toolkit_config = nil, &initialization_block) @containing_object_name = "" @config_toolkit_config = config_toolkit_config clear_all_values() if(block_given?()) yield self enforce_specs() end end |
Class Attribute Details
.param_spec_list ⇒ Object (readonly)
This is an Array (class instance variable) containing the parameter specifications (ParamSpecs) in the order in which they were specified in the BaseConfig child class.
139 140 141 |
# File 'lib/configtoolkit/baseconfig.rb', line 139 def param_spec_list @param_spec_list end |
.param_spec_lookup_table ⇒ Object (readonly)
This is a Hash (class instance variable) mapping paramater names (Symbols) to parameter specifications (ParamSpecs)
145 146 147 |
# File 'lib/configtoolkit/baseconfig.rb', line 145 def param_spec_lookup_table @param_spec_lookup_table end |
Instance Attribute Details
#containing_object_name ⇒ Object (readonly)
This is a String containing the name of the object that contains this instance’s configuration. For example, if all of this instance’s configuration is under production
and webserver
in the configuration file, then the containing_object_name
would be production.webserver
.
481 482 483 |
# File 'lib/configtoolkit/baseconfig.rb', line 481 def containing_object_name @containing_object_name end |
Class Method Details
.inherited(child_class) ⇒ Object
Description:
This is BaseConfig’s implementation of the inherited hook, which is called by the Ruby interpreter whenever BaseConfig is extended. This function initializes the class instance variables of the new child class.
Parameters:
- child_class
-
The new child class
159 160 161 162 163 164 165 166 167 168 |
# File 'lib/configtoolkit/baseconfig.rb', line 159 def self.inherited(child_class) # :nodoc: # # Initialize the class instance variables. # child_class.class_eval do @param_spec_list = [] @param_spec_lookup_table = {} @default_config_toolkit_config = nil end end |
.load(reader, containing_object_name = "", config_toolkit_config = nil) ⇒ Object
Description:
This class method creates a new config instance and calls load on it with the specified arguments. This is a second “constructor” for the class.
Parameters:
See the parameter list for load.
- config_toolkit_config
-
An optional ConfigToolkitConfig instance to configure the new configuration instance (this allows the loading/dumping process to be customized). If this parameter is not specified by the caller, then the value of BaseConfig.default_config_toolkit_config will be used.
Returns:
The new instance, preloaded!
1132 1133 1134 1135 1136 |
# File 'lib/configtoolkit/baseconfig.rb', line 1132 def self.load(reader, containing_object_name = "", config_toolkit_config = nil) instance = new(config_toolkit_config) instance.load(reader, containing_object_name) return instance end |
.load_group(reader, containing_object_name = "", config_toolkit_config = nil) ⇒ Object
Description:
This class method loads arbitrarily many configs from reader. This should be used when each of containing_object_name
‘s elements is a different instance of this class’ configuration. This call returns a Hash containing elements mapping configuration names (Symbols) to configuration instance.
Parameters:
See the parameter list for load.
- config_toolkit_config
-
An optional ConfigToolkitConfig instance to configure the new configuration instance (this allows the loading/dumping process to be customized). If this parameter is not specified by the caller, then the value of BaseConfig.default_config_toolkit_config will be used.
Returns:
A Hash of names (Symbols) mapping to loaded configuration instances
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 |
# File 'lib/configtoolkit/baseconfig.rb', line 1158 def self.load_group(reader, containing_object_name = "", config_toolkit_config = nil) containing_object_hash = load_containing_object_hash(reader, containing_object_name) config_group = {} if(containing_object_hash != nil) containing_object_hash.each do |key, value| value_containing_object_name = "#{containing_object_name}.#{key}" if(!value.is_a?(Hash)) = "error: #{self}.load_group found " << "#{value.class} " << "rather than Hash when reading the " << "#{value_containing_object_name} containing object" raise Error, end instance = new(config_toolkit_config) # # Have to use send in order to call private method. # I think that not being able to call private instance methods # from class methods is a bug. # instance.send(:load_impl, value, value_containing_object_name) config_group[key.to_sym()] = instance end end return config_group end |
Instance Method Details
#==(rhs) ⇒ Object
Description:
Equality operator for BaseConfig; this method iterates through the configuration’s parameters and, for each parameter, compares the value of self
and rhs
using the value’s equality operator.
Parameters:
- rhs
-
The instance to compare with
self
Returns:
True if and only if the parameter values of self and rhs are equal
679 680 681 682 683 684 685 686 687 688 689 690 691 |
# File 'lib/configtoolkit/baseconfig.rb', line 679 def ==(rhs) if(rhs == nil) return false end self.class.param_spec_list.each do |param_spec| if(send(param_spec.name) != rhs.send(param_spec.name)) return false end end return true end |
#dump(writer) ⇒ Object
Description:
This method writes self
to a configuration file. It does this by passing the contents of self
to writer
. That is, it constructs a Hash containing entries for the parameters and passes the Hash to the write
method of writer
, which should write the information to some underlying medium (most likely writing a configuration file). The method checks the configuration against its specifications (via a call to enforce_specs) before dumping anything.
Parameters:
- writer
-
The writer to which to dump (see Writer)
790 791 792 793 794 795 796 797 798 799 800 801 802 |
# File 'lib/configtoolkit/baseconfig.rb', line 790 def dump(writer) # # If the require_symbol_parameter_names? method returns true, # then Symbol parameter names will be passed to the writer; # otherwise, String parameter names will be passed to the writer. # use_symbol_parameter_names = writer.require_symbol_parameter_names?() config_hash = {} dump_impl(config_hash, use_symbol_parameter_names) writer.write(config_hash, @containing_object_name) end |
#enforce_specs ⇒ Object
Description:
This method enforces the BaseConfig’s specifications. In particular, it:
-
Checks that all required parameters have been set. If a required parameter is missing, then this method raises an Error.
-
Sets all optional parameters without values and with default values to their default values.
-
Calls validate_all_values.
This method is called at the end of a load operation (verifying that a valid configuration was loaded from a reader) and before a dump operation (verifying that a valid configuration will be dumped).
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
# File 'lib/configtoolkit/baseconfig.rb', line 596 def enforce_specs # # Iterate through the parameters without values. If any are # required parameters, then raise (after completing the # iteration in order to get the full list of missing # parameters). Otherwise, set all optional, missing # parameters to their default values. # missing_params = [] self.class.param_spec_list.each do |param_spec| if(!has_value(param_spec.name)) if(param_spec.required?) missing_params.push(param_spec.name) elsif(param_spec.default_value?) # # Even the default values are set through the setter. # send("#{param_spec.name}=", param_spec.default_value) end end end if(!missing_params.empty?) if(@containing_object_name.empty?) param_prefix = "" else param_prefix = "#{@containing_object_name}." end missing_params_str = missing_params.map do |param_name| "#{param_prefix}#{param_name}" end.join(", ") raise Error, "missing parameter(s): #{missing_params_str}" end begin validate_all_values() rescue Error => e # # Rescue the error, add some information to the error message, and # throw a repackaged error. # = "#{self.class}#validate_all_values error" if(!@containing_object_name.empty?) << " for #{@containing_object_name}" end << ": #{e.}" raise Error, , e.backtrace() end end |
#load(reader, containing_object_name = "") ⇒ Object
Description:
This method loads the contents of a configuration file into self
. It loads self
from reader
(with containing object containing_object_name
), deleting all prior parameter values.
Parameters:
- reader
-
The reader (see Reader) from which to load the parameter values. This method will call the
read
method ofreader
, which will return a Hash containing, in the most nested containing object entry, the configuration parameter values forself
. - containing_object_name
-
The containing object name
1104 1105 1106 1107 1108 1109 1110 1111 1112 |
# File 'lib/configtoolkit/baseconfig.rb', line 1104 def load(reader, containing_object_name = "") # # Not being able to call private class methods from one of the # class' instance methods is a language bug. # containing_object_hash = self.class.send(:load_containing_object_hash, reader, containing_object_name) load_impl(containing_object_hash, containing_object_name) end |
#raise_error(reason) ⇒ Object
Description:
This method raises an Error with message reason
. It is meant to be called from user-defined parameter validation blocks (see the arguments to add_param). This is a Hotel California type of call; it does not return.
Parameters:
- reason
-
The reason for the error
662 663 664 |
# File 'lib/configtoolkit/baseconfig.rb', line 662 def raise_error(reason) raise Error, reason, caller() end |
#to_s ⇒ Object
Returns:
String representation of self
1193 1194 1195 1196 1197 1198 1199 1200 |
# File 'lib/configtoolkit/baseconfig.rb', line 1193 def to_s string_stream = StringIO.new() writer = PrettyPrintWriter.new(string_stream) dump(writer) return string_stream.string() end |
#validate_all_values ⇒ Object
Description:
This method enforces constraints between parameters; unless overriden, it is a no-op. It is called after all values have been loaded during a load operation or at the start of a dump operation. If a child class wishes to enforce a particular constraint, it should re-implement this method; if a constraint is violated, the method should call raise_error in order to raise an Error.
1211 1212 1213 |
# File 'lib/configtoolkit/baseconfig.rb', line 1211 def validate_all_values # This must be re-implemented in child classes in order to do anything end |