Class: DataShift::MethodMapper

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/datashift/method_mapper.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#logdir, #logger

Constructor Details

#initializeMethodMapper

Returns a new instance of MethodMapper.



26
27
28
# File 'lib/datashift/method_mapper.rb', line 26

def initialize
  @method_details = []
end

Instance Attribute Details

#method_detailsObject

Returns the value of attribute method_details.



24
25
26
# File 'lib/datashift/method_mapper.rb', line 24

def method_details
  @method_details
end

#missing_methodsObject

Returns the value of attribute missing_methods.



24
25
26
# File 'lib/datashift/method_mapper.rb', line 24

def missing_methods
  @missing_methods
end

Instance Method Details

#contains_mandatory?(mandatory_list) ⇒ Boolean

Returns true if discovered methods contain every operator in mandatory_list

Returns:

  • (Boolean)


160
161
162
163
164
165
166
# File 'lib/datashift/method_mapper.rb', line 160

def contains_mandatory?( mandatory_list )
  a = [*mandatory_list].collect { |f| f.downcase }
  puts a.inspect
  b = operator_names.collect { |f| f.downcase }
  puts b.inspect
  (a - b).empty?
end

#map_inbound_headers_to_methods(klass, columns, options = {}) ⇒ Object

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

The header can also contain the fields to use in lookups, separated with Delimiters ::column_delim For example specify that lookups on has_one association called ‘product’, be performed using name’

product:name

The header can also contain a default value for the lookup field, again separated with Delimiters ::column_delim

For example specify lookups on assoc called ‘user’, be performed using ‘email’ == ‘[email protected]

user:email:[email protected]

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 included in processing.

    This provides the opportunity for loaders to provide specific methods to handle these fields
    when no direct operator is available on the model or it's associations

[:include_all]      : Include all headers in processing - takes precedence of :force_inclusion


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/datashift/method_mapper.rb', line 63

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 
  
  mgr = DataShift::MethodDictionary.method_details_mgrs[klass]
   
  forced = [*options[:force_inclusion]].compact.collect { |f| f.to_s.downcase }
  
  @method_details, @missing_methods = [], []

  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 column at index #{col_index}") 
      @method_details << nil
      next
    end
    
    raw_col_name, where_field, where_value, *data = raw_col_data.split(Delimiters::column_delim)
     
    md = MethodDictionary::find_method_detail(klass, raw_col_name)

    if(md.nil?)
      if(options[:include_all] || forced.include?(raw_col_name.downcase))
        logger.debug("Operator #{raw_col_name} not found but forced inclusion operative")
        md = MethodDictionary::add(klass, raw_col_name)
      end
    end
    
    if(md)

      md.name = raw_col_name
      md.column_index = col_index

      # put data back as string for now - leave it to clients to decide what to do with it later
      Populator::set_header_default_data(md.operator, data.join(Delimiters::column_delim))

      if(where_field)
        logger.info("Lookup query field [#{where_field}] - specified for association #{md.operator}")

        md.find_by_value = where_value

        # Example :
        # Project:name:My Best Project
        #   User (klass) has_one project (operator) lookup by name  (find_by_operator) == 'My Best Project' (find_by_value)
        #   User.project.where( :name => 'My Best Project')

        # check the finder method name is a valid field on the actual association class

        if(klass.reflect_on_association(md.operator) &&
           klass.reflect_on_association(md.operator).klass.new.respond_to?(where_field))
          md.find_by_operator = where_field
          logger.info("Complex Lookup specified for [#{md.operator}] : on field [#{md.find_by_operator}] (optional value [#{md.find_by_value}])")
        else
          logger.warn("Find by operator [#{where_field}] Not Found on association [#{md.operator}] on Class #{klass.name} (#{md.inspect})")
          logger.warn("Check column (#{md.column_index}) heading - e.g association field names are case sensitive")
          # TODO - maybe derived loaders etc want this data for another purpose - should we stash elsewhere ?
        end
      end
    else
      # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil
      logger.warn("No operator or association found for Header #{raw_col_name}")
      @missing_methods << raw_col_name
    end

    logger.debug("Column [#{col_data}] (#{col_index}) - mapped to :\n#{md.inspect}")

    @method_details << md
    
  end
 
  @method_details
end

#method_namesObject

TODO populate unmapped with a real MethodDetail that is ‘null’ and create is_nil

The raw client supplied names



149
150
151
# File 'lib/datashift/method_mapper.rb', line 149

def method_names()
  @method_details.compact.collect( &:name )
end

#missing_mandatory(mandatory_list) ⇒ Object



168
169
170
# File 'lib/datashift/method_mapper.rb', line 168

def missing_mandatory( mandatory_list )
  [ [*mandatory_list] - operator_names].flatten
end

#operator_namesObject

The true operator names discovered from model



154
155
156
# File 'lib/datashift/method_mapper.rb', line 154

def operator_names()
  @method_details.compact.collect( &:operator )
end