# frozen_string_literal: true class ReeDao::LoadAgg include Ree::FnDSL fn :load_agg do link "ree_dao/associations", -> { Associations } link "ree_dao/contract/dao_dataset_contract", -> { DaoDatasetContract } link "ree_dao/contract/entity_contract", -> { EntityContract } end contract( Nilor[DaoDatasetContract], Or[Sequel::Dataset, ArrayOf[Integer], ArrayOf[EntityContract], Integer], Ksplat[ only?: ArrayOf[Symbol], except?: ArrayOf[Symbol], to_dto?: Proc, RestKeys => Any ], Optblock => ArrayOf[Any] ) def call(dao = nil, ids_or_scope, **opts, &block) scope = if ids_or_scope.is_a?(Array) && ids_or_scope.any? { _1.is_a?(Integer) } raise ArgumentError.new("Dao should be provided") if dao.nil? return [] if ids_or_scope.empty? dao.where(id: ids_or_scope) elsif ids_or_scope.is_a?(Integer) raise ArgumentError.new("Dao should be provided") if dao.nil? dao.where(id: ids_or_scope) else ids_or_scope end list = scope.is_a?(Sequel::Dataset) ? scope.all : scope if opts[:to_dto] list = list.map do |item| list = opts[:to_dto].call(item) end end load_associations(list, **opts, &block) if block_given? if ids_or_scope.is_a?(Array) list.sort_by { ids_or_scope.index(_1.id) } else list end end private def load_associations(list, **opts, &block) return if list.empty? local_vars = block.binding.eval(<<-CODE, __FILE__, __LINE__ + 1) vars = self.instance_variables vars.reduce({}) { |hsh, var| hsh[var] = self.instance_variable_get(var); hsh } CODE agg_caller = block.binding.eval("self") associations = Associations.new(agg_caller, list, local_vars, **opts).instance_exec(list, &block) if ReeDao.load_sync_associations_enabled? associations else associations[:association_threads].map do |association, assoc_type, assoc_name, opts, block| Thread.new do association.load(assoc_type, assoc_name, **opts, &block) end end.map(&:join) associations[:field_threads].map do |association, field_proc| Thread.new do association.handle_field(field_proc) end end.map(&:join) end end end