lib/datashift/method_mapper.rb in datashift-0.0.2 vs lib/datashift/method_mapper.rb in datashift-0.1.0
- old
+ new
@@ -14,41 +14,58 @@
# and return details of real has_many association 'product_properties'.
#
# This real association can then be used to send spreadsheet row data to the AR object.
#
require 'method_detail'
+require 'method_dictionary'
module DataShift
class MethodMapper
attr_accessor :header_row, :headers
attr_accessor :method_details, :missing_methods
- @@has_many = Hash.new
- @@belongs_to = Hash.new
- @@assignments = Hash.new
- @@column_types = Hash.new
+
+ # As well as just the column name, support embedding find operators for that column
+ # in the heading .. i.e Column header => 'BlogPosts:user_id'
+ # ... association has many BlogPosts selected via find_by_user_id
+ #
+ def self.column_delim
+ @column_delim ||= ':'
+ @column_delim
+ end
+ 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 method_list
# Handles method names as defined by a user or in file headers where names may
# not be exactly as required e.g handles capitalisation, white space, _ etc
# Returns: Array of matching method_details
#
- def populate_methods( klass, method_list )
+ def map_inbound_to_methods( klass, method_list )
+
@method_details, @missing_methods = [], []
- method_list.each do |x|
- md = MethodMapper::find_method_detail( klass, x )
+ method_list.each do |name|
+ x, lookup = name.split(MethodMapper::column_delim)
+ md = MethodDictionary::find_method_detail( klass, x )
+
+ # 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}"
+
+ md.find_by_operator = lookup if(lookup) # TODO and klass.x.respond_to?(active_record_helper))
md ? @method_details << md : @missing_methods << x
end
- #@method_details.compact! .. currently we may neeed to map via the index on @method_details so don't remove nils for now
+ #@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
# The raw client supplied names
def method_names()
@@ -67,191 +84,8 @@
def missing_mandatory( mandatory_list )
[ [*mandatory_list] - operator_names].flatten
end
- # Create picture of the operators for assignment available on an AR model,
- # including via associations (which provide both << and = )
- # Options:
- # :reload => clear caches and reperform lookup
- # :instance_methods => if true include instance method type assignment operators as well as model's pure columns
- #
- def self.find_operators(klass, options = {} )
-
- # Find the has_many associations which can be populated via <<
- if( options[:reload] || @@has_many[klass].nil? )
- @@has_many[klass] = klass.reflect_on_all_associations(:has_many).map { |i| i.name.to_s }
- klass.reflect_on_all_associations(:has_and_belongs_to_many).inject(@@has_many[klass]) { |x,i| x << i.name.to_s }
- end
- # puts "DEBUG: Has Many Associations:", @@has_many[klass].inspect
-
- # Find the belongs_to associations which can be populated via Model.belongs_to_name = OtherArModelObject
- if( options[:reload] || @@belongs_to[klass].nil? )
- @@belongs_to[klass] = klass.reflect_on_all_associations(:belongs_to).map { |i| i.name.to_s }
- end
-
- #puts "Belongs To Associations:", @@belongs_to[klass].inspect
-
- # Find the has_one associations which can be populated via Model.has_one_name = OtherArModelObject
- if( options[:reload] || self.has_one[klass].nil? )
- self.has_one[klass] = klass.reflect_on_all_associations(:has_one).map { |i| i.name.to_s }
- end
-
- #puts "has_one Associations:", self.has_one[klass].inspect
-
- # Find the model's column associations which can be populated via xxxxxx= value
- # Note, not all reflections return method names in same style so we convert all to
- # the raw form i.e without the '=' for consistency
- if( options[:reload] || @@assignments[klass].nil? )
-
- @@assignments[klass] = klass.column_names
-
- if(options[:instance_methods] == true)
- setters = klass.instance_methods.grep(/\w+=/).collect {|x| x.to_s }
-
- if(klass.respond_to? :defined_activerecord_methods)
- setters = setters - klass.defined_activerecord_methods.to_a
- end
-
- # get into same format as other names
- @@assignments[klass] += setters.map{|i| i.gsub(/=/, '')}
- end
-
- @@assignments[klass] -= @@has_many[klass] if(@@has_many[klass])
- @@assignments[klass] -= @@belongs_to[klass] if(@@belongs_to[klass])
- @@assignments[klass] -= self.has_one[klass] if(self.has_one[klass])
-
- @@assignments[klass].uniq!
-
- @@assignments[klass].each do |assign|
- @@column_types[klass] ||= {}
- column_def = klass.columns.find{ |col| col.name == assign }
- @@column_types[klass].merge!( assign => column_def) if column_def
- end
- end
- end
-
- def self.build_method_details( klass )
- @method_details ||= {}
-
- @method_details[klass] = []
-
- assignments_for(klass).each do |n|
- @method_details[klass] << MethodDetail.new(n, klass, n, :assignment)
- end
-
- has_one_for(klass).each do |n|
- @method_details[klass] << MethodDetail.new(n, klass, n, :has_one)
- end
-
- has_many_for(klass).each do |n|
- @method_details[klass] << MethodDetail.new(n, klass, n, :has_many)
- end
-
- belongs_to_for(klass).each do |n|
- @method_details[klass] << MethodDetail.new(n, klass, n, :belongs_to)
- end
- end
-
- def self.method_details
- @method_details ||= {}
- @method_details
- end
-
- # Find the proper format of name, appropriate call + column type for a given name.
- # e.g Given users entry in spread sheet check for pluralization, missing underscores etc
- #
- # If not nil, returned method can be used directly in for example klass.new.send( call, .... )
- #
- def self.find_method_detail( klass, external_name )
- operator = nil
-
- name = external_name.to_s
-
- # TODO - check out regexp to do this work better plus Inflections ??
- # Want to be able to handle any of ["Count On hand", 'count_on_hand', "Count OnHand", "COUNT ONHand" etc]
- [
- name,
- name.tableize,
- name.gsub(' ', '_'),
- name.gsub(' ', '_').downcase,
- name.gsub(/(\s+)/, '_').downcase,
- name.gsub(' ', ''),
- name.gsub(' ', '').downcase,
- name.gsub(' ', '_').underscore].each do |n|
-
- operator = (assignments_for(klass).include?(n)) ? n : nil
-
- return MethodDetail.new(name, klass, operator, :assignment, @@column_types[klass]) if(operator)
-
- operator = (has_one_for(klass).include?(n)) ? n : nil
-
- return MethodDetail.new(name, klass, operator, :has_one, @@column_types[klass]) if(operator)
-
- operator = (has_many_for(klass).include?(n)) ? n : nil
-
- return MethodDetail.new(name, klass, operator, :has_many, @@column_types[klass]) if(operator)
-
- operator = (belongs_to_for(klass).include?(n)) ? n : nil
-
- return MethodDetail.new(name, klass, operator, :belongs_to, @@column_types[klass]) if(operator)
-
- end
-
- nil
- end
-
- def self.clear
- @@belongs_to.clear
- @@has_many.clear
- @@assignments.clear
- @@column_types.clear
- self.has_one.clear
- end
-
- def self.column_key(klass, column)
- "#{klass.name}:#{column}"
- end
-
- # TODO - remove use of class variables - not good Ruby design
- def self.belongs_to
- @@belongs_to
- end
-
- def self.has_many
- @@has_many
- end
-
- def self.has_one
- @has_one ||= {}
- @has_one
- end
-
- def self.assignments
- @@assignments
- end
- def self.column_types
- @@column_types
- end
-
-
- def self.belongs_to_for(klass)
- @@belongs_to[klass] || []
- end
- def self.has_many_for(klass)
- @@has_many[klass] || []
- end
-
- def self.has_one_for(klass)
- self.has_one[klass] || []
- end
-
- def self.assignments_for(klass)
- @@assignments[klass] || []
- end
- def self.column_type_for(klass, column)
- @@column_types[klass] ? @@column_types[klass][column] : []
- end
-
end
end
\ No newline at end of file