lib/datashift/method_mapper.rb in datashift-0.10.1 vs lib/datashift/method_mapper.rb in datashift-0.10.2

- old
+ new

@@ -22,11 +22,10 @@ class MethodMapper include DataShift::Logging - attr_accessor :header_row, :headers attr_accessor :method_details, :missing_methods # As well as just the column name, support embedding find operators for that column # in the heading .. i.e Column header => 'BlogPosts:user_id' @@ -43,71 +42,112 @@ def self.set_column_delim(x) @column_delim = x; end def initialize @method_details = [] - @headers = [] end # Build complete picture of the methods whose names listed in columns # Handles method names as defined by a user, from spreadsheets or file headers where the names # specified may not be exactly as required e.g handles capitalisation, white space, _ etc - # Returns: Array of matching method_details + # + # The header can also contain the fields to use in lookups, separated with MethodMapper::column_delim + # + # product:name or project:title or user:email + # + # Returns: Array of matching method_details, including nils for non matched items + # + # N.B Columns that could not be mapped are left in the array as NIL + # + # This is to support clients that need to map via the index on @method_details + # + # Other callers can simply call compact on the results if the index not important. + # + # The Methoddetails instance will contain a pointer to the column index from which it was mapped. + # + # + # Options: + # + # [:force_inclusion] : List of columns that do not map to any operator but should be includeed in processing. + # + # This provides the opportunity for loaders to provide specific methods to handle these fields + # when no direct operator is available on the modle or it's associations + # + # [:include_all] : Include all headers in processing - takes precedence of :force_inclusion # - def map_inbound_to_methods( klass, columns, options = {} ) + def map_inbound_headers_to_methods( klass, columns, options = {} ) + # If klass not in MethodDictionary yet, add to dictionary all possible operators on klass + # which can be used to map headers and populate an object of type klass + unless(MethodDictionary::for?(klass)) + DataShift::MethodDictionary.find_operators(klass) + + DataShift::MethodDictionary.build_method_details(klass) + end + forced = [*options[:force_inclusion]].compact forced.collect! { |f| f.downcase } @method_details, @missing_methods = [], [] - columns.each do |name| - if(name.nil? or name.empty?) + columns.each_with_index do |col_data, col_index| + + raw_col_data = col_data.to_s + + if(raw_col_data.nil? or raw_col_data.empty?) logger.warn("Column list contains empty or null columns") @method_details << nil next end - operator, lookup = name.split(MethodMapper::column_delim) + raw_col_name, lookup = raw_col_data.split(MethodMapper::column_delim) - md = MethodDictionary::find_method_detail( klass, operator ) + md = MethodDictionary::find_method_detail( klass, raw_col_name ) # TODO be nice if we could cheeck that the assoc on klass responds to the specified # lookup key now (nice n early) # active_record_helper = "find_by_#{lookup}" - if(md.nil? && forced.include?(operator.downcase)) - md = MethodDictionary::add(klass, operator) + if(md.nil? && options[:include_all] || forced.include?(raw_col_name.downcase)) + md = MethodDictionary::add(klass, raw_col_name) end if(md) + md.name = raw_col_name + md.column_index = col_index + if(lookup) find_by, find_value = lookup.split(MethodMapper::column_delim) md.find_by_value = find_value md.find_by_operator = find_by # TODO and klass.x.respond_to?(active_record_helper)) #puts "DEBUG: Method Detail #{md.name};#{md.operator} : find_by_operator #{md.find_by_operator}" end - - @method_details << md else - @missing_methods << operator + # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil + @missing_methods << raw_col_name end + @method_details << md + end - #@method_details.compact! .. currently we may need to map via the index on @method_details so don't remove nils for now + @method_details end + + # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil + # # The raw client supplied names def method_names() - @method_details.collect( &:name ) + @method_details.compact.collect( &:name ) end # The true operator names discovered from model def operator_names() - @method_details.collect( &:operator ) + @method_details.compact.collect( &:operator ) end + # Returns true if discovered methods contain every operator in mandatory_list def contains_mandatory?( mandatory_list ) [ [*mandatory_list] - operator_names].flatten.empty? end \ No newline at end of file