class AbstractImporter::Base

Attributes

collection_importers[R]
collections[R]
generate_id[R]
id_map[R]
import_plan[R]
only[R]
options[R]
parent[R]
reporter[R]
skip[R]
source[R]

Public Class Methods

dependencies() click to toggle source
# File lib/abstract_importer/base.rb, line 24
def dependencies
  read_inheritable_instance_variable(:@dependencies) || []
end
depends_on(*dependencies) click to toggle source
# File lib/abstract_importer/base.rb, line 20
def depends_on(*dependencies)
  @dependencies = dependencies
end
import() { |import_plan = import_plan| ... } click to toggle source
# File lib/abstract_importer/base.rb, line 16
def import
  yield @import_plan = ImportPlan.new
end
import_plan() click to toggle source
# File lib/abstract_importer/base.rb, line 28
def import_plan
  read_inheritable_instance_variable(:@import_plan)
end
new(parent, source, options={}) click to toggle source
# File lib/abstract_importer/base.rb, line 47
def initialize(parent, source, options={})
  @parent       = parent
  @source       = source
  @options      = options

  io            = options.fetch(:io, $stderr)
  @reporter     = default_reporter(options, io)
  @dry_run      = options.fetch(:dry_run, false)

  id_map        = options.fetch(:id_map, true)
  @import_plan  = self.class.import_plan.to_h
  @atomic       = options.fetch(:atomic, false)
  @strategies   = options.fetch(:strategy, {})
  @generate_id  = options[:generate_id]
  @skip         = Array(options[:skip])
  @only         = Array(options[:only]) if options.key?(:only)
  @collections  = []

  @use_id_map, @id_map = id_map.is_a?(IdMap) ? [true, id_map] : [id_map, IdMap.new]

  verify_source!
  verify_parent!
  instantiate_collections!

  @collection_importers = []
  collections.each do |collection|
    next if skip? collection
    @collection_importers.push CollectionImporter.new(self, collection)
  end
end

Private Class Methods

read_inheritable_instance_variable(ivar) click to toggle source
# File lib/abstract_importer/base.rb, line 34
def read_inheritable_instance_variable(ivar)
  klass = self
  until klass.instance_variable_defined?(ivar)
    klass = klass.superclass
    return if klass == AbstractImporter::Base
  end
  klass.instance_variable_get(ivar)
end

Public Instance Methods

atomic?() click to toggle source
# File lib/abstract_importer/base.rb, line 96
def atomic?
  @atomic
end
collection_counts() click to toggle source
# File lib/abstract_importer/base.rb, line 143
def collection_counts
  @collection_counts ||= Hash.new do |counts, collection_name|
    counts[collection_name] = if source.respond_to?(:"#{collection_name}_count")
      source.public_send(:"#{collection_name}_count")
    else
      source.public_send(collection_name).count
    end
  end
end
count_collection(collection) click to toggle source
# File lib/abstract_importer/base.rb, line 138
def count_collection(collection)
  collection_name = collection.respond_to?(:name) ? collection.name : collection
  collection_counts[collection_name]
end
describe_destination() click to toggle source
# File lib/abstract_importer/base.rb, line 183
def describe_destination
  parent.to_s
end
describe_source() click to toggle source
# File lib/abstract_importer/base.rb, line 179
def describe_source
  source.to_s
end
dry_run?() click to toggle source
# File lib/abstract_importer/base.rb, line 100
def dry_run?
  @dry_run
end
map_foreign_key(legacy_id, plural, foreign_key, depends_on) click to toggle source
# File lib/abstract_importer/base.rb, line 195
def map_foreign_key(legacy_id, plural, foreign_key, depends_on)
  return nil if legacy_id.nil?
  return legacy_id unless use_id_map_for?(depends_on)
  id_map.apply!(depends_on, legacy_id)
rescue KeyError
  raise IdNotMappedError, "#{plural}.#{foreign_key} will be nil: a #{depends_on.to_s.singularize} with the legacy id #{legacy_id} was not mapped."
end
perform!() click to toggle source
# File lib/abstract_importer/base.rb, line 108
def perform!
  {}.tap do |results|
    reporter.start_all(self)

    setup_ms = Benchmark.ms do
      setup
    end
    reporter.finish_setup(self, setup_ms)

    ms = Benchmark.ms do
      with_transaction do
        collection_importers.each do |importer|
          results[importer.name] = importer.perform!
        end
      end
    end

    teardown_ms = Benchmark.ms do
      teardown
    end
    reporter.finish_teardown(self, teardown_ms)

    reporter.finish_all(self, setup_ms + ms + teardown_ms)
  end
end
remap_foreign_key?(plural, foreign_key) click to toggle source
# File lib/abstract_importer/base.rb, line 191
def remap_foreign_key?(plural, foreign_key)
  true
end
setup() click to toggle source
# File lib/abstract_importer/base.rb, line 134
def setup
  prepopulate_id_map!
end
skip?(collection) click to toggle source
# File lib/abstract_importer/base.rb, line 156
def skip?(collection)
  collection_name = collection.respond_to?(:name) ? collection.name : collection
  return true if skip.member?(collection_name)
  return true if only && !only.member?(collection_name)
  false
end
strategy_for(collection_importer) click to toggle source
# File lib/abstract_importer/base.rb, line 163
def strategy_for(collection_importer)
  collection = collection_importer.collection
  strategy_name = @strategies.fetch collection.name, :default
  strategy_options = {}
  if strategy_name.is_a?(Hash)
    strategy_options = strategy_name
    strategy_name = strategy_name[:name]
  end
  strategy_klass = AbstractImporter::Strategies.const_get :"#{strategy_name.capitalize}Strategy"
  strategy_klass.new(collection_importer, strategy_options)
end
teardown() click to toggle source
# File lib/abstract_importer/base.rb, line 153
def teardown
end
use_id_map_for?(collection) click to toggle source
# File lib/abstract_importer/base.rb, line 90
def use_id_map_for?(collection)
  collection = find_collection(collection) if collection.is_a?(Symbol)
  return false unless collection
  @use_id_map && collection.has_legacy_id?
end

Protected Instance Methods

scope_for(collection_name) click to toggle source
# File lib/abstract_importer/base.rb, line 208
def scope_for(collection_name)
  parent.public_send(collection_name).unscope(:order)
end

Private Instance Methods

default_reporter(options, io) click to toggle source
# File lib/abstract_importer/base.rb, line 287
def default_reporter(options, io)
  reporter = options.fetch(:reporter, ENV["IMPORT_REPORTER"])
  return reporter if reporter.is_a?(AbstractImporter::Reporters::BaseReporter)

  case reporter.to_s.downcase
  when "none"        then Reporters::NullReporter.new(io)
  when "performance" then Reporters::PerformanceReporter.new(io)
  when "debug"       then Reporters::DebugReporter.new(io)
  when "dot"         then Reporters::DotReporter.new(io)
  else
    if ENV["RAILS_ENV"] == "production"
      Reporters::DebugReporter.new(io)
    else
      Reporters::DotReporter.new(io)
    end
  end
end
dependencies() click to toggle source
# File lib/abstract_importer/base.rb, line 255
def dependencies
  @dependencies ||= self.class.dependencies.map do |name|
    Collection.new(name, scope_for(name))
  end
end
find_collection(name) click to toggle source
# File lib/abstract_importer/base.rb, line 261
def find_collection(name)
  collections.find { |collection| collection.name == name } ||
  dependencies.find { |collection| collection.name == name }
end
has_scope_for?(collection_name) click to toggle source
# File lib/abstract_importer/base.rb, line 214
def has_scope_for?(collection_name)
  scope_for(collection_name)
  true
rescue NoMethodError
  false
end
instantiate_collections!() click to toggle source
# File lib/abstract_importer/base.rb, line 246
def instantiate_collections!
  @collections = import_plan.map do |name, block|
    options = ImportOptions.new
    instance_exec(options, &block) if block

    Collection.new(name, scope_for(name), options)
  end
end
prepopulate_id_map!() click to toggle source
# File lib/abstract_importer/base.rb, line 266
def prepopulate_id_map!
  (collections + dependencies).each do |collection|
    next unless use_id_map_for?(collection)
    prepopulate_id_map_for!(collection)
  end
end
prepopulate_id_map_for!(collection) click to toggle source
# File lib/abstract_importer/base.rb, line 273
def prepopulate_id_map_for!(collection)
  id_map.init collection.table_name, collection.scope
end
verify_parent!() click to toggle source
# File lib/abstract_importer/base.rb, line 230
def verify_parent!
  import_plan.keys.each do |collection|
    next if has_scope_for?(collection)

    raise "#{parent.class} does not have a collection named `#{collection}`; " <<
          "but #{self.class} plans to import records with that name"
  end

  self.class.dependencies.each do |collection|
    next if has_scope_for?(collection)

    raise "#{parent.class} does not have a collection named `#{collection}`; " <<
          "but #{self.class} declares it as a dependency"
  end
end
verify_source!() click to toggle source
# File lib/abstract_importer/base.rb, line 221
def verify_source!
  import_plan.keys.each do |collection|
    next if source.respond_to?(collection)

    raise "#{source.class} does not respond to `#{collection}`; " <<
          "but #{self.class} plans to import records with that name"
  end
end
with_transaction(&block) click to toggle source
# File lib/abstract_importer/base.rb, line 279
def with_transaction(&block)
  if atomic?
    ActiveRecord::Base.transaction(requires_new: true, &block)
  else
    block.call
  end
end