# frozen_string_literal: true module Kadmin class Finder include Kadmin::Presentable # @return [Kadmin::Pager] the pager to use (if any) attr_reader :pager # @return [Hash<String, Kadmin::Finder::Filter>] array of filters applied to the finder attr_reader :filters # @return [ActiveRecord::Relation] the base relation to find items from attr_reader :scope # Simple filter structure Filter = Struct.new(:column, :value) # @param [ActiveRecord::Relation] scope base relation to page/filter on def initialize(scope) @scope = scope @pager = nil @filters = {} @results = nil @filtering = false end # @param [String] name the filter name (should be unique) # @param [String, Array<String>] column the column(s) name to filter on # @param [String, Array<String>] value the value or values to look for (OR'd) def filter(name:, column:, value:) if column.present? @filters[name] = Kadmin::Finder::Filter.new(column, value) if value.present? search_value = ActiveRecord::Base.connection.quote("%#{value}%".squeeze('%')) filters = Array.wrap(column).map do |column_name| %(`#{@scope.table_name}`.`#{column_name}` LIKE #{search_value}) end @scope = @scope.where(filters.join(' OR ')) @filtering = true end end return self end def filtering? return @filtering end # @param [Integer] offset optional; offset/index for the current page # @param [Integer] size optional; size of a page # @return [Kadmin::Finder] itself def paginate(offset: nil, size: nil) offset = offset.to_i size = size.to_i if size.positive? && offset >= 0 @pager = Kadmin::Pager.new(size: size, offset: offset) end return self end # @return [ActiveRecord::Relation] the filtered (and optionally paginated) results def results return @results ||= begin results = @scope results = @pager.paginate(results) unless @pager.nil? results.load results end end # Forces to refetch/recalculate the find operation results def find! @total_found = 0 @results = nil return results end end end