kaspernj/baza_models

View on GitHub
lib/baza_models/autoloader.rb

Summary

Maintainability
A
0 mins
Test Coverage
class BazaModels::Autoloader
  def initialize(args)
    @models = args.fetch(:models)
    @model_class = @models.first.class
    @autoloads = args.fetch(:autoloads)
    @db = args.fetch(:db)
    @debug = args[:debug]

    debug "Autoloading #{@autoloads} on #{@model_class.name} with ID's: #{@models.map(&:id)}" if @debug
  end

  def autoload
    autoload_argument(@autoloads)
  end

private

  def debug(msg)
    print "#{msg}\n" if @debug
  end

  def model_ids
    @model_ids ||= @models.map(&:id)
  end

  def autoload_argument(autoload)
    if autoload.is_a?(Hash)
      autoload_hash(autoload)
    elsif autoload.is_a?(Array)
      autoload_array(autoload)
    elsif autoload.is_a?(Symbol)
      autoload_symbol(autoload)
    else
      raise "Didn't know what to do with autoload: #{autoload} (#{autoload.class.name})"
    end
  end

  def autoload_array(autoload)
    autoload.each do |autoload_i|
      autoload_argument(autoload_i)
    end
  end

  def autoload_hash(autoload)
    autoload.each do |autoload_key, autoload_value|
      result = autoload_symbol(autoload_key)
      next if result.fetch(:models).empty?

      autoloader = BazaModels::Autoloader.new(
        models: result.fetch(:models),
        autoloads: autoload_value,
        db: @db,
        debug: @debug
      )
      autoloader.autoload
    end
  end

  def autoload_symbol(autoload)
    relation = @model_class.relationships.fetch(autoload)

    if relation.fetch(:type) == :has_many
      autoload_has_many(autoload, relation)
    elsif relation.fetch(:type) == :belongs_to
      autoload_belongs_to(autoload, relation)
    elsif relation.fetch(:type) == :has_one
      autoload_has_one(autoload, relation)
    else
      raise "Unknown relation type: #{relation.fetch(:type)}"
    end
  end

  def autoload_belongs_to(autoload_name, relation)
    result = {models: []}

    model_id_mappings = {}
    model_ids = []

    @models.each do |model|
      key = model.data.fetch(relation.fetch(:foreign_key))
      next if model_id_mappings.key?(key)

      model_ids << key
      model_id_mappings[key] = model.id
    end

    @db.select(relation.fetch(:table_name), id: model_ids) do |model_data|
      model = StringCases.constantize(relation.fetch(:class_name)).new(model_data, init: true)

      orig_model_id = model_id_mappings.fetch(model_data.fetch(:id))
      orig_model = @models.detect { |array_model| array_model.id == orig_model_id }
      orig_model.autoloads[autoload_name] ||= model

      result.fetch(:models) << model
    end

    result
  end

  def autoload_has_many(autoload_name, relation)
    result = {models: []}

    @db.select(relation.fetch(:table_name), relation.fetch(:foreign_key) => model_ids) do |model_data|
      model = StringCases.constantize(relation.fetch(:class_name)).new(model_data, init: true)

      orig_model_id = model_data.fetch(relation.fetch(:foreign_key).to_sym)
      orig_model = @models.detect { |array_model| array_model.id == orig_model_id }

      orig_model.autoloads[autoload_name] ||= []
      orig_model.autoloads[autoload_name] << model

      result.fetch(:models) << model
    end

    result
  end

  def autoload_has_one(autoload_name, relation)
    result = {models: []}

    @db.select(relation.fetch(:table_name), relation.fetch(:foreign_key) => model_ids) do |model_data|
      model = StringCases.constantize(relation.fetch(:class_name)).new(model_data, init: true)

      orig_model_id = model_data.fetch(relation.fetch(:foreign_key))
      orig_model = @models.detect { |array_model| array_model.id == orig_model_id }

      raise "Already autoloaded?" if orig_model.autoloads.key?(autoload_name)

      orig_model.autoloads[autoload_name] = model

      result.fetch(:models) << model
    end

    result
  end
end