module CanvasSync::Concerns module SyncMapping extend ActiveSupport::Concern class_methods do def sync_mapping(key = nil, reset: false, &blk) key ||= Mapping.normalize_model_name(self) key = key.to_s existing_map = get_sync_mapping(key) mapper = Mapping.new(existing_map&.deep_dup || {}.with_indifferent_access) mapper.reset_links if reset mapper.instance_exec(&blk) @sync_mappings[key] = mapper.map_def.freeze end def get_sync_mapping(key = nil) key ||= Mapping.normalize_model_name(self) key = key.to_s @sync_mappings ||= {} @sync_mappings[key] || superclass.try(:get_sync_mapping, key) || Mapping.default_for(key) end end class Mapping attr_reader :map_def def initialize(map_def, model: nil) @model = model @map_def = map_def @map_def[:conflict_target] ||= [] @map_def[:columns] ||= {} end def self.normalize_model_name(model) model = model.name unless model.is_a?(String) model.pluralize.underscore end def self.default_for(key) default_mappings[key] end def self.default_mappings @mappings ||= begin maps = {} default_v1_mappings.each do |mname, legacy| m = maps[mname] = {} m[:conflict_target] = Array(legacy[:conflict_target]).map(&:to_sym).map do |lct| legacy[:report_columns][lct][:database_column_name] end m[:columns] = {} legacy[:report_columns].each do |rcol, opts| m[:columns][opts[:database_column_name]] = opts.except(:database_column_name).merge!( report_column: rcol, ).freeze end end maps.with_indifferent_access.freeze end end def self.default_v1_mappings @legacy_mappings ||= begin mapping = YAML.load_file(File.join(__dir__, '../processors', "model_mappings.yml")).deep_symbolize_keys! override_filepath = Rails.root.join("config/canvas_sync_provisioning_mapping.yml") if File.file?(override_filepath) override = YAML.load_file(override_filepath).deep_symbolize_keys! mapping = mapping.merge(override) end mapping.freeze end end def conflict_target(*columns) if columns.count == 0 @map_def[:conflict_target] else @map_def[:conflict_target] = columns.flatten.compact end end def reset_links @map_def[:columns] = {}.with_indifferent_access end def unlink_column(key) @map_def[:columns].delete(key) end def link_column(m = {}, type: nil, **kwargs, &blk) if m.is_a?(Hash) m = m.merge(kwargs) raise "Hash should have exactly 1 entry" if m && m.count != 1 @map_def[:columns][m.values[0]] = { report_column: m.keys[0], type: type, transform: blk, } elsif m.is_a?(Symbol) raise "Unrecognized keyword arguments" if kwargs.present? @map_def[:columns][m] = { report_column: m, type: type, transform: blk, } else raise "Cannot handle argument of type #{m.class}" end end end end end