Skip to content

Commit

Permalink
Add series code
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcsmith committed Mar 12, 2020
1 parent 67e22a2 commit 3a6e184
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 6 deletions.
146 changes: 141 additions & 5 deletions spec/paginator_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,6 @@ describe Lucky::Paginator do
end
end

describe "#series" do
it "allows customizing the series" do
end
end

describe "#path_to_page" do
it "adds a query param to the path" do
path = build_pages(full_path: "/comments").path_to_page(1)
Expand Down Expand Up @@ -144,4 +139,145 @@ describe Lucky::Paginator do
path.should be_nil
end
end

describe "#series" do
it "allows customizing the series" do
pages = build_pages(page: 20, per_page: 1, item_count: 40)

series = pages.series(begin: 2, left_of_current: 2, right_of_current: 2, end: 2)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 1),
Lucky::Paginator::Page.new(pages, page: 2),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 18),
Lucky::Paginator::Page.new(pages, page: 19),
Lucky::Paginator::CurrentPage.new(pages, page: 20),
Lucky::Paginator::Page.new(pages, page: 21),
Lucky::Paginator::Page.new(pages, page: 22),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 39),
Lucky::Paginator::Page.new(pages, page: 40),
])
end

it "doesn't add gaps when begining/ending are next to current pages" do
pages = build_pages(page: 4, per_page: 1, item_count: 7)

series = pages.series(begin: 1, left_of_current: 2, right_of_current: 2, end: 1)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 1),
Lucky::Paginator::Page.new(pages, page: 2),
Lucky::Paginator::Page.new(pages, page: 3),
Lucky::Paginator::CurrentPage.new(pages, page: 4),
Lucky::Paginator::Page.new(pages, page: 5),
Lucky::Paginator::Page.new(pages, page: 6),
Lucky::Paginator::Page.new(pages, page: 7),
])
end

it "adds gaps when there is just 1 page in between middle and edges" do
pages = build_pages(page: 4, per_page: 1, item_count: 7)

series = pages.series(begin: 1, left_of_current: 1, right_of_current: 1, end: 1)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 1),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 3),
Lucky::Paginator::CurrentPage.new(pages, page: 4),
Lucky::Paginator::Page.new(pages, page: 5),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 7),
])
end

it "when 'begin' or 'end' is 0 it leaves of the pages and gap" do
pages = build_pages(page: 20, per_page: 1, item_count: 40)

series = pages.series(begin: 0, left_of_current: 1, right_of_current: 1, end: 0)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 19),
Lucky::Paginator::CurrentPage.new(pages, page: 20),
Lucky::Paginator::Page.new(pages, page: 21),
])
end

it "when 'left/right_of_current' are 0 it keeps the gap between begin/end" do
pages = build_pages(page: 20, per_page: 1, item_count: 40)

series = pages.series(begin: 1, left_of_current: 0, right_of_current: 0, end: 1)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 1),
Lucky::Paginator::Gap.new,
Lucky::Paginator::CurrentPage.new(pages, page: 20),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 40),
])
end

it "when all options are 0 it generates just a current page" do
pages = build_pages(page: 20, per_page: 1, item_count: 40)

series = pages.series(begin: 0, left_of_current: 0, right_of_current: 0, end: 0)

series.should eq([
Lucky::Paginator::CurrentPage.new(pages, page: 20),
])
end

it "returns the correct series when at the last page" do
pages = build_pages(page: 40, per_page: 1, item_count: 40)

series = pages.series(begin: 0, left_of_current: 1, right_of_current: 1, end: 1)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 39),
Lucky::Paginator::CurrentPage.new(pages, page: 40),
])
end

it "returns the correct series when near the last page" do
pages = build_pages(page: 39, per_page: 1, item_count: 40)

series = pages.series(begin: 0, left_of_current: 1, right_of_current: 2, end: 1)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 38),
Lucky::Paginator::CurrentPage.new(pages, page: 39),
Lucky::Paginator::CurrentPage.new(pages, page: 40),
])
end

it "returns the correct series when at the first page" do
pages = build_pages(page: 1, per_page: 1, item_count: 40)

series = pages.series(begin: 1, left_of_current: 0, right_of_current: 3, end: 1)

series.should eq([
Lucky::Paginator::CurrentPage.new(pages, page: 1),
Lucky::Paginator::Page.new(pages, page: 2),
Lucky::Paginator::Page.new(pages, page: 3),
Lucky::Paginator::Page.new(pages, page: 4),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 40),
])
end

it "returns the correct series when near the first page" do
pages = build_pages(page: 2, per_page: 1, item_count: 40)

series = pages.series(begin: 1, left_of_current: 2, right_of_current: 0, end: 1)

series.should eq([
Lucky::Paginator::Page.new(pages, page: 1),
Lucky::Paginator::CurrentPage.new(pages, page: 2),
Lucky::Paginator::Gap.new,
Lucky::Paginator::Page.new(pages, page: 40),
])
end
end
end
95 changes: 94 additions & 1 deletion src/lucky/paginator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class Lucky::Paginator
getter item_count : Int32 | Int64
getter full_path : String

alias SeriesItem = Gap | Page | CurrentPage

def initialize(@page, @per_page, @item_count, @full_path)
end

Expand Down Expand Up @@ -105,6 +107,97 @@ class Lucky::Paginator
left_of_current : Int32,
right_of_current : Int32,
end ending : Int32
)
) : Array(SeriesItem)
middle_pages = build_middle_of_series(left_of_current, right_of_current)
beginning_and_middle_pages = add_beginning_pages(middle_pages, beginning)
add_ending_pages(beginning_and_middle_pages, ending)
end

private def build_middle_of_series(
left_of_current : Int32,
right_of_current : Int32
) : Array(SeriesItem)
arr = [] of SeriesItem

# If given `left_of_current: 2` this would yield `2` then `1`
# If 0 it will not yield anything
left_of_current.downto(1) do |i|
# And page number would be `page - 2` then `page - 1`
# So if you are on page `10` you'd have `8`, then `9`
page_number = page - i
# Don't add a 0 or - page.
if page_number >= 1
arr << Page.new(self, page: page_number)
end
end

# Always add the current page
arr << CurrentPage.new(self, page: page)

# If given `right_of_current: 2` it would yield `1` then `2`
# If 0 it will not yield anything
1.upto(right_of_current) do |i|
# This would be `page + 1` then `page + 2`, etc.
# So if you are on page `10` and there are `15` pages you'd have `11` then `12`
page_number = page + i # + 1
# Don't add the page if it is greater than the total pages
# So if you're on page `10` with a total of `10` pages it will not add pages
# to the rigth
if page_number <= total
arr << Page.new(self, page: page_number)
end
end

arr
end

private def add_beginning_pages(arr : Array(SeriesItem), beginning : Int32) : Array(SeriesItem)
first_page_in_middle_section = (arr.first.as(Page).page)

# If beginning is set to 2 and the first page in the middle section is 4 add Gap: 2, .., 4
# If beginning is set to 2 and the first page in the middle section is 3, don't: 2, 3
if beginning > 0 && (beginning + 1) < first_page_in_middle_section
arr.unshift Gap.new
end

# If beginning is 2 it'll count from 2 down to 1. So it'll yield 2, then 1
beginning.downto(1) do |i|
page_number = i
if page_number < first_page_in_middle_section
# Unshift prepends the item to the beginning of the series
# Since we are counting down this would look something like:
#
# [3, 4, 5].unshift(2) # => [2, 3, 4, 5]
# [2, 3, 4, 5].unshift(1) # => [1, 2, 3, 4, 5]
arr.unshift Page.new(self, page: page_number)
end
end

arr
end

private def add_ending_pages(arr : Array(SeriesItem), ending : Int32) : Array(SeriesItem)
last_page_in_middle_section = (arr.last.as(Page).page)

# First determine the smallest ending page
# So if total pages is 20 and ending is 2 you'd get 18
smallest_ending_page = total - ending
# If smallest ending page is 18 and last page in middle is 20 then do add a gap: 18, .., 20
# If smallest ending page is 18 and last page in middle is 19 don't add a gap: 19, 20
if ending > 0 && smallest_ending_page > last_page_in_middle_section
arr << Gap.new
end

# If ending is 2 it'll count from 2 down to 1. So it'll yield 2, then 1
ending.downto(1) do |i|
# So if there are 10 total pages this would be `10 + 1 - 2` then `10 + 1 -1`
# Giving you `9` then `10`
page_number = total + 1 - i
if page_number > last_page_in_middle_section
arr << Page.new(self, page: page_number)
end
end

arr
end
end
4 changes: 4 additions & 0 deletions src/lucky/paginator/current_page.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require "./page"

class Lucky::Paginator::CurrentPage < Lucky::Paginator::Page
end
8 changes: 8 additions & 0 deletions src/lucky/paginator/gap.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# :nodoc:
#
# Used to represent a gap in a pagination series
class Lucky::Paginator::Gap
def ==(other : self)
true # All gaps are equal to each other
end
end
11 changes: 11 additions & 0 deletions src/lucky/paginator/page.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Lucky::Paginator::Page
def_equals page
getter page

def initialize(@pages : Lucky::Paginator, @page : Int32 | Int64)
end

def inspect(io)
io << page
end
end

0 comments on commit 3a6e184

Please sign in to comment.