lib/rflow/configuration.rb in rflow-1.0.0a1 vs lib/rflow/configuration.rb in rflow-1.0.0a2
- old
+ new
@@ -1,9 +1,6 @@
-require 'rflow/util'
-
class RFlow
-
# Contains all the configuration data and methods for RFlow.
# Interacts directly with underlying sqlite database, and keeps a
# registry of available data types, extensions, and components.
# Also includes an external DSL, RubyDSL, that can be used in
# crafting config-like files that load the database.
@@ -11,62 +8,51 @@
# Configuration provides a MVC-like framework for config files,
# where the models are the Setting, Component, Port, and Connection
# subclasses, the controllers are things like RubyDSL, and the views
# are defined relative to the controllers
class Configuration
-
- # An exception class
- class ConfigurationInvalid < StandardError; end
-
-
- # A class to hold DB config and connection information
- class ConfigDB < ActiveRecord::Base
- self.abstract_class = true
- end
-
-
# A collection class for data extensions that supports a naive
# prefix-based 'inheritance' on lookup. When looking up a key
# with [] all existing keys will be examined to determine if the
# existing key is a string prefix of the lookup key. All the
# results are consolidated into a single, flattened array.
class DataExtensionCollection
-
def initialize
# TODO: choose a different data structure ...
- @hash = Hash.new {|hash, key| hash[key] = Array.new}
+ @extensions_for = Hash.new {|hash, key| hash[key] = []}
end
# Return an array of all of the values that have keys that are
# prefixes of the lookup key.
def [](key)
- key_string = key.to_s
- @hash.map do |data_type, extensions|
- key_string.start_with?(data_type) ? extensions : nil
- end.flatten.compact
+ @extensions_for.
+ find_all {|data_type, _| key.to_s.start_with?(data_type) }.
+ flat_map {|_, extensions| extensions }
end
# Adds a data extension for a given data type to the collection
def add(data_type, extension)
- @hash[data_type.to_s] << extension
+ @extensions_for[data_type.to_s] << extension
end
# Remove all elements from the collection. Useful for testing,
# not much else
def clear
- @hash.clear
+ @extensions_for.clear
end
+ end
+ # Base class for persisted RFlow configuration items.
+ class ConfigurationItem < ActiveRecord::Base
+ self.abstract_class = true
end
-
class << self
-
# A collection of data types (schemas) indexed by their name and
# their schema type ('avro').
def available_data_types
- @available_data_types ||= Hash.new {|hash, key| hash[key] = Hash.new}
+ @available_data_types ||= Hash.new {|hash, key| hash[key] = {}}
end
# A DataExtensionCollection to hold available extensions that
# will be applied to the de-serialized data types
def available_data_extensions
@@ -74,176 +60,132 @@
end
# A Hash of defined components, usually automatically populated
# when a component subclasses RFlow::Component
def available_components
- @available_components ||= Hash.new
+ @available_components ||= {}
end
- end
- # TODO: refactor each of these add_available_* into collections to
- # make DRYer. Also figure out what to do with all to to_syms
+ # TODO: refactor each of these add_available_* into collections to
+ # make DRYer. Also figure out what to do with all to to_syms
- # Add a schema to the available_data_types class attribute.
- # Schema is indexed by data_type_name and schema/serialization
- # type. 'avro' is currently the only supported
- # data_serialization_type.
- def self.add_available_data_type(data_type_name, data_serialization_type, data_schema)
- unless data_serialization_type == 'avro'
- error_message = "Data serialization_type must be 'avro' for '#{data_type_name}'"
- RFlow.logger.error error_message
- raise ArgumentError, error_message
- end
+ # Add a schema to the available_data_types class attribute.
+ # Schema is indexed by data_type_name and schema/serialization
+ # type. 'avro' is currently the only supported serialization_type.
+ def add_available_data_type(name, serialization_type, schema)
+ raise ArgumentError, "Data serialization_type must be 'avro' for '#{name}'" unless serialization_type == 'avro'
- if available_data_types[data_type_name.to_s].include? data_serialization_type.to_s
- error_message = "Data type '#{data_type_name}' already defined for serialization_type '#{data_serialization_type}'"
- RFlow.logger.error error_message
- raise ArgumentError, error_message
+ if available_data_types[name.to_s].include? serialization_type.to_s
+ raise ArgumentError, "Data type '#{name}' already defined for serialization_type '#{serialization_type}'"
+ end
+
+ available_data_types[name.to_s][serialization_type.to_s] = schema
end
- available_data_types[data_type_name.to_s][data_serialization_type.to_s] = data_schema
- end
+ # Add a data extension to the available_data_extensions class
+ # attributes. The data_extension parameter should be the name of
+ # a ruby module that will extend RFlow::Message::Data object to
+ # provide additional methods/capability. Naive, prefix-based
+ # inheritance is possible, see available_data_extensions or the
+ # DataExtensionCollection class
+ def add_available_data_extension(data_type_name, extension)
+ unless extension.is_a? Module
+ raise ArgumentError, "Invalid data extension #{extension} for #{data_type_name}. Only Ruby Modules allowed"
+ end
- # Add a data extension to the available_data_extensions class
- # attributes. The data_extension parameter should be the name of
- # a ruby module that will extend RFlow::Message::Data object to
- # provide additional methods/capability. Naive, prefix-based
- # inheritance is possible, see available_data_extensions or the
- # DataExtensionCollection class
- def self.add_available_data_extension(data_type_name, data_extension)
- unless data_extension.is_a? Module
- error_message = "Invalid data extension #{data_extension} for #{data_type_name}. Only Ruby Modules allowed"
- RFlow.logger.error error_message
- raise ArgumentError, error_message
+ available_data_extensions.add data_type_name, extension
end
- available_data_extensions.add data_type_name, data_extension
- end
+ # Used when RFlow::Component is subclassed to add another
+ # available component to the list.
+ def add_available_component(component)
+ if available_components.include?(component.name)
+ raise ArgumentError, "Component already '#{component.name}' already defined"
+ end
+ available_components[component.name] = component
+ end
+ # Connect to the configuration sqlite database, but use the
+ # ConfigurationItem class to protect the connection information from
+ # other ActiveRecord apps (i.e. Rails)
+ def establish_config_database_connection(database_path)
+ RFlow.logger.debug "Establishing connection to config database (#{Dir.getwd}) '#{database_path}'"
+ ActiveRecord::Base.logger = RFlow.logger
+ ConfigurationItem.establish_connection(:adapter => "sqlite3", :database => database_path)
+ end
- # Used when RFlow::Component is subclassed to add another
- # available component to the list.
- def self.add_available_component(component)
- if available_components.include?(component.name)
- error_message = "Component already '#{component.name}' already defined"
- RFlow.logger.error error_message
- raise ArgumentError, error_message
+ # Using default ActiveRecord migrations, attempt to migrate the
+ # database to the latest version.
+ def migrate_database
+ RFlow.logger.debug "Applying default migrations to config database"
+ migrations_path = File.join(File.dirname(__FILE__), 'configuration', 'migrations')
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Migrator.migrate migrations_path
end
- available_components[component.name] = component
- end
+ # Load the config file, which should load/process/store all the
+ # elements. Only run this after the database has been setup
+ def process_config_file(path)
+ RFlow.logger.info "Processing config file (#{Dir.getwd}) '#{path}'"
+ load path
+ end
- # Connect to the configuration sqlite database, but use the
- # ConfigDB subclass to protect the connection information from
- # other ActiveRecord apps (i.e. Rails)
- def self.establish_config_database_connection(config_database_path)
- RFlow.logger.debug "Establishing connection to config database (#{Dir.getwd}) '#{config_database_path}'"
- ActiveRecord::Base.logger = RFlow.logger
- ConfigDB.establish_connection(:adapter => "sqlite3",
- :database => config_database_path)
- end
+ # Connect to the configuration database, migrate it to the latest
+ # version, and process a config file if provided.
+ def initialize_database(database_path, config_file_path = nil)
+ RFlow.logger.debug "Initializing config database (#{Dir.getwd}) '#{database_path}'"
+ # TODO should not need this line
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => database_path)
- # Using default ActiveRecord migrations, attempt to migrate the
- # database to the latest version.
- def self.migrate_database
- RFlow.logger.debug "Applying default migrations to config database"
- migrations_directory_path = File.join(File.dirname(__FILE__), 'configuration', 'migrations')
-# ActiveRecord::Migration.verbose = RFlow.logger
- ActiveRecord::Migrator.migrate migrations_directory_path
- end
+ establish_config_database_connection database_path
+ migrate_database
+ working_dir = Dir.getwd
+ Dir.chdir File.dirname(database_path)
- # Load the config file, which should load/process/store all the
- # elements. Only run this after the database has been setup
- def self.process_config_file(config_file_path)
- RFlow.logger.info "Processing config file (#{Dir.getwd}) '#{config_file_path}'"
- load config_file_path
- end
+ if config_file_path
+ process_config_file File.expand_path(config_file_path)
+ end
+ RFlow.logger.debug "Defaulting non-existing config values"
+ merge_defaults!
- # Connect to the configuration database, migrate it to the latest
- # version, and process a config file if provided.
- def self.initialize_database(config_database_path, config_file_path=nil)
- RFlow.logger.debug "Initializing config database (#{Dir.getwd}) '#{config_database_path}'"
+ Dir.chdir working_dir
- RFlow.logger.debug "Establishing connection to config database (#{Dir.getwd}) '#{config_database_path}'"
- ActiveRecord::Base.logger = RFlow.logger
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
- :database => config_database_path)
-
- migrate_database
-
- expanded_config_file_path = File.expand_path config_file_path if config_file_path
-
- working_dir = Dir.getwd
- Dir.chdir File.dirname(config_database_path)
-
- if config_file_path
- process_config_file(expanded_config_file_path)
+ self.new(database_path)
end
- RFlow.logger.debug "Defaulting non-existing config values"
- merge_defaults!
-
- Dir.chdir working_dir
-
- self.new(config_database_path)
- end
-
-
- # Make sure that the configuration has all the necessary values set.
- def self.merge_defaults!
- Setting::DEFAULTS.each do |name, default_value_or_proc|
- setting = Setting.find_or_create_by_name(:name => name,
- :value => default_value_or_proc.is_a?(Proc) ? default_value_or_proc.call() : default_value_or_proc)
- unless setting.valid?
- error_message = setting.errors.map do |attribute, error_string|
- error_string
- end.join ', '
- raise RuntimeError, error_message
+ # Make sure that the configuration has all the necessary values set.
+ def merge_defaults!
+ Setting::DEFAULTS.each do |name, default_value_or_proc|
+ value = default_value_or_proc.is_a?(Proc) ? default_value_or_proc.call() : default_value_or_proc
+ setting = Setting.find_or_create_by_name(:name => name, :value => value)
+ unless setting.valid?
+ raise RuntimeError, setting.errors.map {|_, msg| msg }.join(', ')
+ end
end
end
end
-
- attr_accessor :config_database_path
- attr_accessor :cached_settings
- attr_accessor :cached_components
- attr_accessor :cached_ports
- attr_accessor :cached_connections
-
-
- def initialize(config_database_path=nil)
- @cached_settings = Hash.new
- @cached_components = Hash.new
- @cached_ports = []
- @cached_connections = []
-
+ def initialize(database_path = nil)
# If there is not a config DB path, assume that an AR
- # conncection has already been established
- if config_database_path
- @config_database_path = config_database_path
- self.class.establish_config_database_connection(config_database_path)
+ # connection has already been established
+ if database_path
+ @database_path = database_path
+ Configuration.establish_config_database_connection(database_path)
end
- # Validate the connected database. TODO: make this more
- # complete, i.e. validate the various columns
+ # Validate the connected database.
+ # TODO: make this more complete, i.e. validate the various columns
begin
- Setting.first
- Shard.first
- Component.first
- Port.first
- Connection.first
+ [Setting, Shard, Component, Port, Connection].each(&:first)
rescue ActiveRecord::StatementInvalid => e
- error_message = "Invalid schema in configuration database: #{e.message}"
- RFlow.logger.error error_message
- raise ArgumentError, error_message
+ raise ArgumentError, "Invalid schema in configuration database: #{e.message}"
end
end
-
def to_s
string = "Configuration:\n"
settings.each do |setting|
string << "Setting: '#{setting.name}' = '#{setting.value}'\n"
@@ -264,37 +206,16 @@
end
end
string
end
- # Helper method to access settings with minimal syntax
- def [](setting_name)
- Setting.find_by_name(setting_name).value rescue nil
- end
-
- def settings
- Setting.all
- end
-
- def shards
- Shard.all
- end
-
- def shard(shard_uuid)
- Shard.find_by_uuid shard_uuid
- end
-
- def components
- Component.all
- end
-
- def component(component_uuid)
- Component.find_by_uuid component_uuid
- end
-
- def available_components
- self.class.available_components
- end
+ def [](name); Setting.find_by_name(name).value rescue nil; end
+ def settings; Setting.all; end
+ def shards; Shard.all; end
+ def shard(uuid); Shard.find_by_uuid uuid; end
+ def components; Component.all; end
+ def component(uuid); Component.find_by_uuid uuid; end
+ def available_components; Configuration.available_components; end
end
end
# Load the models
require 'rflow/configuration/setting'