lib/pagy/extras/calendar.rb
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/calendar # frozen_string_literal: true require 'pagy/calendar' class Pagy # :nodoc: # Add pagination filtering by calendar unit (:year, :quarter, :month, :week, :day) to the regular pagination module CalendarExtra # Additions for the Backend module module Backend CONF_KEYS = (Calendar::UNITS + %i[pagy active]).freeze private # Take a collection and a conf Hash with keys in CONF_KEYS and return an array with 3 items: [calendar, pagy, results] def pagy_calendar(collection, conf) unless conf.is_a?(Hash) && (conf.keys - CONF_KEYS).empty? && conf.all? { |k, v| v.is_a?(Hash) || k == :active } raise ArgumentError, "keys must be in #{CONF_KEYS.inspect} and object values must be Hashes; got #{conf.inspect}" end conf[:pagy] = {} unless conf[:pagy] # use default Pagy object when omitted calendar, collection = pagy_setup_calendar(collection, conf) unless conf.key?(:active) && !conf[:active] pagy, results = send(conf[:pagy][:backend] || :pagy, collection, conf[:pagy]) # use backend: :pagy when omitted [calendar, pagy, results] end # Setup and return the calendar objects and the filtered collection def pagy_setup_calendar(collection, conf) units = Calendar::UNITS & conf.keys # get the units in time length desc order raise ArgumentError, 'no calendar unit found in pagy_calendar configuration' if units.empty? page_param = conf[:pagy][:page_param] || DEFAULT[:page_param] units.each do |unit| # set all the :page_param vars for later deletion unit_page_param = :"#{unit}_#{page_param}" conf[unit][:page_param] = unit_page_param conf[unit][:page] = params[unit_page_param] end calendar = {} last_obj = nil units.each_with_index do |unit, index| params_to_delete = units[(index + 1), units.size].map { |sub| conf[sub][:page_param] } + [page_param] conf[unit][:params] = lambda do |params| # delete page_param from the sub-units params_to_delete.each { |p| params.delete(p.to_s) } # Hash#except missing from ruby 2.5 baseline params end conf[unit][:period] = last_obj&.send(:active_period) || pagy_calendar_period(collection) calendar[unit] = last_obj = Calendar.send(:create, unit, conf[unit]) end [calendar, pagy_calendar_filter(collection, last_obj.from, last_obj.to)] end # This method must be implemented by the application def pagy_calendar_period(*) raise NoMethodError, 'the pagy_calendar_period method must be implemented by the application ' \ '(see https://ddnexus.github.io/pagy/extras/calendar#pagy_calendar_periodcollection)' end # This method must be implemented by the application def pagy_calendar_filter(*) raise NoMethodError, 'the pagy_calendar_filter method must be implemented by the application ' \ '(see https://ddnexus.github.io/pagy/extras/calendar#pagy_calendar_filtercollection-from-to)' end end end Backend.prepend CalendarExtra::Backend end