module ETL #:nodoc: module Control #:nodoc: # Object representation of a control file class Control attr_reader :file class << self # Parse a control file and return a Control instance def parse(control_file) control_file = control_file.path if control_file.instance_of?(File) # logger.debug "Parsing control file #{control_file.path}" control = ETL::Control::Control.new(control_file) # TODO: better handling of parser errors. Return the line in the control file where the error occurs. eval(IO.readlines(control_file).join("\n"), control.get_binding) control.validate control end def resolve(control) case control when String ETL::Control::Control.parse(File.new(control)) when File ETL::Control::Control.parse(control) when ETL::Control::Control control else raise ControlError, "Control must be a String, File or Control object" end end end def initialize(file) @file = file end # Define a source def source(name, configuration={}, definition={}) source_types = [:file, :db] source_types.each do |source_type| if configuration[source_type] source_class = ETL::Control::Source.class_for_name(source_type) sources << source_class.new(self, configuration, definition) end end end # Get the defined source def sources @sources ||= [] end # Define a destination def destination(name, configuration={}, mapping={}) destination_types.each do |dest_type| if configuration[dest_type] dest_class = ETL::Control::Destination.class_for_name(dest_type) destinations << dest_class.new(self, configuration, mapping) end end end # Get the defined destinations def destinations @destinations ||= [] end def transform(name, transformer=nil, configuration={}, &block) transforms[name] ||= [] if transformer transform_class = ETL::Transform.const_get("#{transformer.to_s.classify}Transform") transforms[name] << transform_class.new(self, configuration) elsif block_given? transforms[name] << block else raise ControlError, "Either a transformer or a block must be specified" end end def get_transform(name) transforms[name] ||= [] end def pre_process(name, configuration={}) processor_class = ETL::Processor.const_get("#{name.to_s.classify}Processor") pre_processors << processor_class.new(self, configuration) end def pre_processors @pre_processors ||= [] end def post_process(name, configuration={}) processor_class = ETL::Processor.const_get("#{name.to_s.classify}Processor") post_processors << processor_class.new(self, configuration) end def post_processors @post_processors ||= [] end def get_binding binding end # Get a map of all transforms for this control def transforms @transforms ||= {} end # Validate the control file def validate unless sources.length > 0 raise ControlError, "Configuration must include one of the following for the source: #{source_types.join(',')}" end unless destinations.length > 0 raise ControlError, "Configuration must include one of the following for the destination: #{destination_types.join(',')}" end end protected # Get an array of supported source types def source_types [:file, :database] end # Get an array of supported destination types def destination_types [:file, :database] end end end end