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

default_file_handler[W]

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

data[RW]

Public Class Methods

attributes(attrs) click to toggle source

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
default_file_handler() click to toggle source

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
new(parser, file_path, file_handler: nil) click to toggle source

@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

generic_get(key, tree = data) click to toggle source

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
generic_set(key, value, tree = data) click to toggle source

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
load() click to toggle source

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
loaded?() click to toggle source

Returns if configuration was already loaded

# File lib/cfa/base_model.rb, line 77
def loaded?
  @loaded
end
save() click to toggle source

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

add_new(key, value, tree) click to toggle source
# File lib/cfa/base_model.rb, line 164
def add_new(key, value, tree)
  tree.add(key, value)
end
modify(key, value, tree) click to toggle source

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
tree_value_change(key, value) click to toggle source
# 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
tree_value_plain(value) click to toggle source
# File lib/cfa/base_model.rb, line 120
def tree_value_plain(value)
  value.is_a?(AugeasTreeValue) ? value.value : value
end
uncomment(key, value, tree) click to toggle source

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