class CFA::BaseModel
A base class for models. Represents a configuration file as an object with domain-specific attributes/methods. For persistent storage, use load and save, Non-responsibilities: actual storage and parsing (both delegated). There is no caching involved.
Attributes
Sets default file handler. Useful when needed to change default like if whole program use non standard file reading. @param value for value specification see constructor
Public Class Methods
Generates accessors for trivial key-value attributes @param attrs [Hash{Symbol => String}] mapping of methods to file keys
@example Usage
class FooModel < CFA::BaseModel attributes( server: "server", read_timeout: "ReadTimeout", write_timeout: "WriteTimeout" ) ... end
# File lib/cfa/base_model.rb, line 106 def self.attributes(attrs) attrs.each_pair do |method_name, key| define_method(method_name) do tree_value_plain(generic_get(key)) end define_method(:"#{method_name}=") do |value| tree_value_change(key, value) end end end
Gets default file handler used when nil passed as file_handler in constructor
# File lib/cfa/base_model.rb, line 83 def self.default_file_handler @default_file_handler ||= File end
@param parser [.parse, .serialize, .empty] parser that can convert object
to string and vice versa. It have to provide methods `string #serialize(object)`, `object #parse(string)` and `object #empty` For example see {CFA::AugeasParser}
@param file_path [String] expected path passed to file_handler @param file_handler [.read, .write] an object able to read/write a string.
It has to provide methods `string read(string)` and `write(string, string)`. For an example see {CFA::MemoryFile}. If unspecified or `nil`, {.default_file_handler} is asked.
# File lib/cfa/base_model.rb, line 25 def initialize(parser, file_path, file_handler: nil) @file_handler = file_handler || BaseModel.default_file_handler @parser = parser @file_path = file_path @loaded = false self.data = parser.empty end
Public Instance Methods
powerfull method that gets unformatted any value in config. @note prefer to use specialized methods of children
# File lib/cfa/base_model.rb, line 72 def generic_get(key, tree = data) tree[key] end
powerfull method that sets any value in config. It try to be smart to at first modify existing value, then replace commented out code and if even that doesn't work, then append it at the end @note prefer to use specialized methods of children
# File lib/cfa/base_model.rb, line 65 def generic_set(key, value, tree = data) modify(key, value, tree) || uncomment(key, value, tree) || add_new(key, value, tree) end
Reads a String using file_handler and parses it with parser, storing the result in data. @return [void] @raise a file_handler specific error. If file_path does not exist
or permission is not sufficient it may raise an error depending on the used file handler.
@raise a parser specific error. If the parsed String is malformed, then
depending on the used parser it may raise an error.
# File lib/cfa/base_model.rb, line 55 def load @parser.file_name = @file_path if @parser.respond_to?(:file_name=) self.data = @parser.parse(@file_handler.read(@file_path)) @loaded = true end
Returns if configuration was already loaded
# File lib/cfa/base_model.rb, line 77 def loaded? @loaded end
Serializes data using parser and writes the resulting String using file_handler. @return [void] @raise a file_handler specific error if file_path cannot be written
e.g. due to missing permissions or living on a read only device.
@raise a parser specific error. If data contain invalid values
then *parser* may raise an error. A properly written BaseModel subclass should prevent that by preventing insertion of such values in the first place.
# File lib/cfa/base_model.rb, line 42 def save @parser.file_name = @file_path if @parser.respond_to?(:file_name=) @file_handler.write(@file_path, @parser.serialize(data)) end
Protected Instance Methods
# File lib/cfa/base_model.rb, line 164 def add_new(key, value, tree) tree.add(key, value) end
Modify an existing entry and return `true`, or do nothing and return `false`. @return [Boolean]
# File lib/cfa/base_model.rb, line 138 def modify(key, value, tree) # if already set, just change value return false unless tree[key] tree[key] = value true end
# File lib/cfa/base_model.rb, line 124 def tree_value_change(key, value) old_value = generic_get(key) if old_value.is_a?(AugeasTreeValue) old_value.value = value value = old_value end generic_set(key, value) end
# File lib/cfa/base_model.rb, line 120 def tree_value_plain(value) value.is_a?(AugeasTreeValue) ? value.value : value end
Replace a commented out entry and return `true`, or do nothing and return `false`. @return [Boolean]
# File lib/cfa/base_model.rb, line 149 def uncomment(key, value, tree) # Try to find if it is commented out, so we can replace line matcher = Matcher.new( collection: "#comment", # FIXME: this assumes a specific "=" syntax, bypassing the lens # FIXME: this will match also "# If you set FOO=bar then..." value_matcher: /(\s|^)#{key}\s*=/ ) return false unless tree.data.any?(&matcher) # FIXME: this assumes that *data* is an AugeasTree tree.add(key, value, ReplacePlacer.new(matcher)) true end