require_relative "associative" require "administrate/page/collection" require "administrate/order" module Administrate module Field class HasMany < Associative DEFAULT_LIMIT = 5 def self.permitted_attribute(attr, _options = {}) # This may seem arbitrary, and improvable by using reflection. # Worry not: here we do exactly what Rails does. Regardless of the name # of the foreign key, has_many associations use the suffix `_ids` # for this. # # Eg: if the associated table and primary key are `countries.code`, # you may expect `country_codes` as attribute here, but it will # be `country_ids` instead. # # See https://github.com/rails/rails/blob/b30a23f53b52e59d31358f7b80385ee5c2ba3afe/activerecord/lib/active_record/associations/builder/collection_association.rb#L48 {"#{attr.to_s.singularize}_ids": []} end def associated_collection(order = self.order) Administrate::Page::Collection.new( associated_dashboard, order: order, collection_attributes: options[:collection_attributes] ) end def attribute_key permitted_attribute.keys.first end def associated_resource_options candidate_resources.map do |associated_resource| [ display_candidate_resource(associated_resource), associated_resource.send(association_primary_key) ] end end def selected_options return if data.empty? data.map { |object| object.send(association_primary_key) } end def limit options.fetch(:limit, DEFAULT_LIMIT) end def paginate? limit.respond_to?(:positive?) ? limit.positive? : limit.present? end def permitted_attribute self.class.permitted_attribute( attribute, resource_class: resource.class ) end def resources(page = 1, order = self.order) resources = order.apply(data) if paginate? resources = resources.page(page).per(limit) end includes.any? ? resources.includes(*includes) : resources end def more_than_limit? paginate? && data.count(:all) > limit end def data @data ||= associated_class.none end def order_from_params(params) Administrate::Order.new( params.fetch(:order, sort_by), params.fetch(:direction, direction) ) end def order @order ||= Administrate::Order.new(sort_by, direction) end private def includes associated_dashboard.collection_includes end def candidate_resources if options.key?(:includes) includes = options.fetch(:includes) associated_class.includes(*includes).all else associated_class.all end end def display_candidate_resource(resource) associated_dashboard.display_resource(resource) end def sort_by options[:sort_by] end def direction options[:direction] end end end end