Skip to content

Commit

Permalink
WIP: Super refactoring 5
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Jan 10, 2025
1 parent ec37b9e commit 68c7643
Show file tree
Hide file tree
Showing 26 changed files with 358 additions and 413 deletions.
21 changes: 0 additions & 21 deletions gem/lib/pagy/loaders/offset.rb

This file was deleted.

35 changes: 16 additions & 19 deletions gem/lib/pagy/mixins/arel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,25 @@
require_relative 'offset' # require the offset mixin

class Pagy
module OffsetArelMixin
module BackendAddOn
private
Backend.class_eval do
private

# Return Pagy object and paginated collection/results
def pagy_arel(collection, **vars)
vars[:count] ||= pagy_arel_count(collection)
pagy_offset(collection, **vars)
end
# Return Pagy object and paginated collection/results
def pagy_arel(collection, **vars)
vars[:count] ||= pagy_arel_count(collection)
pagy_offset(collection, **vars)
end

# Count using Arel when grouping
def pagy_arel_count(collection)
if collection.group_values.empty?
# COUNT(*)
collection.count(:all)
else
# COUNT(*) OVER ()
sql = Arel.star.count.over(Arel::Nodes::Grouping.new([]))
collection.unscope(:order).pick(sql).to_i
end
# Count using Arel when grouping
def pagy_arel_count(collection)
if collection.group_values.empty?
# COUNT(*)
collection.count(:all)
else
# COUNT(*) OVER ()
sql = Arel.star.count.over(Arel::Nodes::Grouping.new([]))
collection.unscope(:order).pick(sql).to_i
end
end
Backend.prepend BackendAddOn
end
end
23 changes: 10 additions & 13 deletions gem/lib/pagy/mixins/array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@
require_relative '../offset'

class Pagy
module OffsetArrayMixin
# Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding
module BackendaAddOn
private
# Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding
Backend.class_eval do
private

# Return Pagy object and paginated items
def pagy_array(array, **vars)
vars[:count] ||= array.size
vars[:limit] ||= pagy_get_limit(vars)
vars[:page] ||= pagy_get_page(vars)
pagy = Offset.new(**vars)
[pagy, array[pagy.offset, pagy.limit]]
end
# Return Pagy object and paginated items
def pagy_array(array, **vars)
vars[:count] ||= array.size
vars[:limit] ||= pagy_get_limit(vars)
vars[:page] ||= pagy_get_page(vars)
pagy = Offset.new(**vars)
[pagy, array[pagy.offset, pagy.limit]]
end
Backend.prepend BackendaAddOn
end
end
3 changes: 1 addition & 2 deletions gem/lib/pagy/mixins/bootstrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class Pagy
# Frontend modules are specially optimized for performance.
# The resulting code may not look very elegant, but produces the best benchmarks
module BootstrapMixin
Frontend.class_eval do
# Pagination for bootstrap: it returns the html with the series of links to the pages
def pagy_bootstrap_nav(pagy, id: nil, classes: 'pagination', aria_label: nil, **vars)
id = %( id="#{id}") if id
Expand Down Expand Up @@ -92,5 +92,4 @@ def bootstrap_next_html(pagy, a)
end
end
end
Frontend.prepend BootstrapMixin
end
3 changes: 1 addition & 2 deletions gem/lib/pagy/mixins/bulma.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class Pagy
# Frontend modules are specially optimized for performance.
# The resulting code may not look very elegant, but produces the best benchmarks
module BulmaMixin
Frontend.class_eval do
# Pagination for bulma: it returns the html with the series of links to the pages
def pagy_bulma_nav(pagy, id: nil, classes: 'pagy-bulma nav pagination is-centered',
aria_label: nil, **vars)
Expand Down Expand Up @@ -88,5 +88,4 @@ def bulma_prev_next_html(pagy, a)
end
end
end
Frontend.prepend BulmaMixin
end
111 changes: 54 additions & 57 deletions gem/lib/pagy/mixins/calendar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,73 @@

class Pagy
# Add pagination filtering by calendar unit (:year, :quarter, :month, :week, :day) to the regular pagination
module CalendarMixin
# Additions for the Backend module
module BackendAddOn
CONF_KEYS = (Offset::Calendar::UNITS + %i[pagy active]).freeze
# Additions for the Backend module
module CalendarBackendAddOn
CALENDAR_CONF_KEYS = (Offset::Calendar::UNITS + %i[pagy active]).freeze

private
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)
raise ArgumentError, "keys must be in #{CONF_KEYS.inspect}" \
unless conf.is_a?(Hash) && (conf.keys - CONF_KEYS).empty?
# 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)
raise ArgumentError, "keys must be in #{CALENDAR_CONF_KEYS.inspect}" \
unless conf.is_a?(Hash) && (conf.keys - CALENDAR_CONF_KEYS).empty?

conf[:pagy] ||= {}
unless conf.key?(:active) && !conf[:active]
calendar, from, to = Offset::Calendar.send(:init, conf, pagy_calendar_period(collection), params) do |unit, period|
pagy_calendar_counts(collection, unit, *period) if respond_to?(:pagy_calendar_counts)
end
collection = pagy_calendar_filter(collection, from, to)
end
pagy, results = send(conf[:pagy][:backend] || :pagy_offset, collection, **conf[:pagy])
[calendar, pagy, results]
conf[:pagy] ||= {}
unless conf.key?(:active) && !conf[:active]
calendar, from, to = Offset::Calendar.send(:init, conf, pagy_calendar_period(collection), params) do |unit, period|
pagy_calendar_counts(collection, unit, *period) if respond_to?(:pagy_calendar_counts)
end
collection = pagy_calendar_filter(collection, from, to)
end
pagy, results = send(conf[:pagy][:backend] || :pagy_offset, collection, **conf[:pagy])
[calendar, pagy, results]
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/docs/extras/calendar/#pagy-calendar-period-collection)'
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/docs/extras/calendar/#pagy-calendar-period-collection)'
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/docs/extras/calendar/#pagy-calendar-filter-collection-from-to)'
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/docs/extras/calendar/#pagy-calendar-filter-collection-from-to)'
end
Backend.prepend BackendAddOn
end
Backend.prepend CalendarBackendAddOn

# Override the pagy_anchor
module FrontendOverride
# Consider the vars[:counts]
def pagy_anchor(pagy, anchor_string: nil)
return super unless (counts = pagy.vars[:counts])
# Override the pagy_anchor
module CalendarFrontendOverride
# Consider the vars[:counts]
def pagy_anchor(pagy, anchor_string: nil)
return super unless (counts = pagy.vars[:counts])

anchor_string &&= %( #{anchor_string})
left, right = %(<a#{anchor_string} href="#{pagy_page_url(pagy, PAGE_TOKEN)}").split(PAGE_TOKEN, 2)
# lambda used by all the helpers
lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
count = counts[page - 1]
if count.zero?
classes = "#{classes && (classes + ' ')}empty-page"
info_key = 'pagy.info.no_items'
else
info_key = 'pagy.info.single_page'
end
title = %( title="#{pagy_t(info_key, item_name: pagy_t('pagy.item_name', count:), count:)}")
classes = %( class="#{classes}") if classes
aria_label = %( aria-label="#{aria_label}") if aria_label
%(#{left}#{page}#{right}#{title}#{classes}#{aria_label}>#{text}</a>)
anchor_string &&= %( #{anchor_string})
left, right = %(<a#{anchor_string} href="#{pagy_page_url(pagy, PAGE_TOKEN)}").split(PAGE_TOKEN, 2)
# lambda used by all the helpers
lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
count = counts[page - 1]
if count.zero?
classes = "#{classes && (classes + ' ')}empty-page"
info_key = 'pagy.info.no_items'
else
info_key = 'pagy.info.single_page'
end
title = %( title="#{pagy_t(info_key, item_name: pagy_t('pagy.item_name', count:), count:)}")
classes = %( class="#{classes}") if classes
aria_label = %( aria-label="#{aria_label}") if aria_label
%(#{left}#{page}#{right}#{title}#{classes}#{aria_label}>#{text}</a>)
end
end
Frontend.prepend FrontendOverride
end
Frontend.prepend CalendarFrontendOverride

# Additions for the Frontend module
module UrlHelpersAddOn
# Return the url for the calendar page at time
def pagy_calendar_url_at(calendar, time, **)
pagy_page_url(calendar.send(:calendar_at, time, **), 1, **)
end
# Additions for the Frontend module
UrlHelpers.class_eval do
# Return the url for the calendar page at time
def pagy_calendar_url_at(calendar, time, **)
pagy_page_url(calendar.send(:calendar_at, time, **), 1, **)
end
UrlHelpers.prepend UrlHelpersAddOn
end
end
37 changes: 17 additions & 20 deletions gem/lib/pagy/mixins/countless.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,26 @@
require_relative '../offset/countless'

class Pagy
module CountlessMixin
# Paginate without the need of any count, saving one query per rendering
module BackendAddOn
private
# Paginate without the need of any count, saving one query per rendering
Backend.class_eval do
private

# Return Pagy object and records
def pagy_countless(collection, **vars)
vars[:limit] ||= pagy_get_limit(vars)
vars[:page] ||= pagy_get_page(vars)
pagy = Offset::Countless.new(**vars)
[pagy, pagy_countless_get_items(collection, pagy)]
end
# Return Pagy object and records
def pagy_countless(collection, **vars)
vars[:limit] ||= pagy_get_limit(vars)
vars[:page] ||= pagy_get_page(vars)
pagy = Offset::Countless.new(**vars)
[pagy, pagy_countless_get_items(collection, pagy)]
end

# Sub-method called only by #pagy_countless: here for easy customization of record-extraction by overriding
# You may need to override this method for collections without offset|limit
def pagy_countless_get_items(collection, pagy)
return collection.offset(pagy.offset).limit(pagy.limit) if pagy.vars[:countless_minimal]
# Sub-method called only by #pagy_countless: here for easy customization of record-extraction by overriding
# You may need to override this method for collections without offset|limit
def pagy_countless_get_items(collection, pagy)
return collection.offset(pagy.offset).limit(pagy.limit) if pagy.vars[:countless_minimal]

fetched = collection.offset(pagy.offset).limit(pagy.limit + 1).to_a # eager load limit + 1
pagy.finalize(fetched.size) # finalize the pagy object
fetched[0, pagy.limit] # ignore eventual extra item
end
fetched = collection.offset(pagy.offset).limit(pagy.limit + 1).to_a # eager load limit + 1
pagy.finalize(fetched.size) # finalize the pagy object
fetched[0, pagy.limit] # ignore eventual extra item
end
Backend.prepend BackendAddOn
end
end
Loading

0 comments on commit 68c7643

Please sign in to comment.