module Datasource::Adapters::ActiveRecord

Public Instance Methods

association_klass(reflection) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 174
def association_klass(reflection)
  if reflection.macro == :belongs_to && reflection.options[:polymorphic]
    fail Datasource::Error, "polymorphic belongs_to not supported, write custom loader"
  else
    reflection.klass
  end
end
association_loaded?(records, name, assoc_select) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 182
def association_loaded?(records, name, assoc_select)
  if records.first.association(name).loaded?
    all_loaded = records.all? { |record| record.association(name).loaded? }
    if assoc_select == ["*"]
      all_loaded
    elsif all_loaded
      records.all? do |record|
        assoc_sample = Array(record.send(name)).first
        assoc_sample.nil? || assoc_sample._datasource_instance
      end
    else
      false
    end
  else
    false
  end
end
association_reflection(klass, name) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 140
def association_reflection(klass, name)
  if reflection = klass.reflect_on_association(name)
    {
      klass: reflection.klass,
      macro: reflection.macro,
      foreign_key: reflection.try(:foreign_key)
    }
  end
end
ensure_table_join!(ds, name, att) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 303
def ensure_table_join!(ds, name, att)
  join_value = ds.scope.joins_values.find do |value|
    if value.is_a?(Symbol)
      value.to_s == att[:name]
    elsif value.is_a?(String)
      if value =~ /join (\w+)/i
        $1 == att[:name]
      end
    end
  end
  fail Datasource::Error, "given scope does not join on #{name}, but it is required by #{att[:name]}" unless join_value
end
get_rows(ds) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 280
def get_rows(ds)
  append_select = []
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
    if reflection = association_reflection(ds.class.orm_klass, assoc_name.to_sym)
      Datasource::Base.reflection_select(reflection, append_select, [])
    end
  end
  ds.select(*append_select)

  scope = select_scope(ds)
  if scope.respond_to?(:datasource_set)
    scope = scope.spawn.datasource_set(datasource_class: nil)
  end
  scope.includes_values = []
  scope.to_a.tap do |records|
    load_associations(ds, records)
  end
end
get_table_name(klass) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 150
def get_table_name(klass)
  klass.table_name.to_sym
end
has_attribute?(record, name) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 170
def has_attribute?(record, name)
  record.attributes.key?(name.to_s)
end
is_scope?(obj) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 154
def is_scope?(obj)
  obj.kind_of?(::ActiveRecord::Relation)
end
load_association(records, name, assoc_select, params) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 200
def load_association(records, name, assoc_select, params)
  return if records.empty?
  name = name.to_sym
  klass = records.first.class
  if reflection = klass.reflect_on_association(name)
    assoc_class = association_klass(reflection)
    datasource_class = assoc_class.default_datasource

    scope = assoc_class.all
    datasource = datasource_class.new(scope)
    assoc_select_attributes = assoc_select.reject { |att| att.kind_of?(Hash) }
    assoc_select_associations = assoc_select.inject({}) do |hash, att|
      hash.deep_merge!(att) if att.kind_of?(Hash)
      hash
    end
    Datasource::Base.reflection_select(association_reflection(klass, name), [], assoc_select_attributes)
    datasource.params(params)

    Datasource.logger.debug { "load_association #{records.first.try!(:class)} #{name}: #{assoc_select_attributes.inspect}" }
    datasource.select(*assoc_select_attributes)
    select_values = datasource.get_select_values

    # TODO: manually load associations, and load them all at once for
    # nested associations, eg. in following, load all Users in 1 query:
    # {"user"=>["*"], "players"=>["*"], "picked_players"=>["*",
    # {:position=>["*"]}], "parent_picked_team"=>["*", {:user=>["*"]}]}
    begin
      ::ActiveRecord::Associations::Preloader
        .new.preload(records, name, assoc_class.select(*select_values))
    rescue ArgumentError
      ::ActiveRecord::Associations::Preloader
        .new(records, name, assoc_class.select(*select_values)).run
    end

    assoc_records = records.flat_map { |record| record.send(name) }.compact
    unless assoc_records.empty?
      if Datasource.logger.info? && !assoc_select_associations.empty?
        Datasource.logger.info { "Loading associations " + assoc_select_associations.keys.map(&:to_s).join(", ") + " for #{assoc_records.first.try!(:class)}s" }
      end
      assoc_select_associations.each_pair do |assoc_name, assoc_select|
        Datasource.logger.debug { "load_association nested association #{assoc_name}: #{assoc_select.inspect}" }
        load_association(assoc_records, assoc_name, assoc_select, params)
      end
      datasource.results(assoc_records)
    end
  end
rescue Exception => ex
  if ex.is_a?(SystemStackError) || ex.is_a?(Datasource::RecursionError)
    fail Datasource::RecursionError, "recursive association (involving #{name})"
  else
    raise
  end
end
load_associations(ds, records) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 270
def load_associations(ds, records)
  if Datasource.logger.info? && !ds.expose_associations.empty?
    Datasource.logger.info { "Loading associations " + ds.expose_associations.keys.map(&:to_s).join(", ") + " for #{records.first.try!(:class)}s" }
  end
  Datasource.logger.debug { "load_associations (#{records.size} #{records.first.try!(:class)}): #{ds.expose_associations.inspect}" }
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
    load_association(records, assoc_name, assoc_select, ds.params)
  end
end
primary_scope_table(ds) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 299
def primary_scope_table(ds)
  ds.scope.klass.table_name
end
scope_loaded?(scope) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 162
def scope_loaded?(scope)
  scope.loaded?
end
scope_to_class(scope) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 158
def scope_to_class(scope)
  scope.klass
end
scope_to_records(scope) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 166
def scope_to_records(scope)
  scope.to_a
end
select_scope(ds) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 260
def select_scope(ds)
  ds.scope.select(*ds.get_select_values)
end
to_query(ds) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 254
def to_query(ds)
  ::ActiveRecord::Base.uncached do
    ds.scope.select(*ds.get_select_values).to_sql
  end
end
upgrade_records(ds, records) click to toggle source
# File lib/datasource/adapters/active_record.rb, line 264
def upgrade_records(ds, records)
  Datasource.logger.debug { "Upgrading records #{records.map(&:class).map(&:name).join(', ')}" }
  load_associations(ds, records)
  ds.results(records)
end