diff --git a/.rubocop.yml b/.rubocop.yml
index 24ee097..5ec821f 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -23,3 +23,4 @@ Metrics/BlockLength:
Metrics/LineLength:
IgnoredPatterns:
- '\w*it.+do'
+ - '\w*context.+do'
diff --git a/app/views/shared/_blocks.html.erb b/app/views/shared/_blocks.html.erb
new file mode 100644
index 0000000..530ac74
--- /dev/null
+++ b/app/views/shared/_blocks.html.erb
@@ -0,0 +1,5 @@
+<%=
+ content_tag :li, class: page_item_css_class do
+ link_to page, bets_path(page: page), class: 'page-link'
+ end
+%>
diff --git a/app/views/shared/_paginator.html.erb b/app/views/shared/_paginator.html.erb
new file mode 100644
index 0000000..842f295
--- /dev/null
+++ b/app/views/shared/_paginator.html.erb
@@ -0,0 +1,38 @@
+
+
+
diff --git a/config/application.rb b/config/application.rb
index e15d607..58b21c8 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -11,5 +11,6 @@ class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
+ config.autoload_paths << "#{Rails.root}/lib"
end
end
diff --git a/lib/utilities/paginator.rb b/lib/utilities/paginator.rb
new file mode 100644
index 0000000..0cd3ab1
--- /dev/null
+++ b/lib/utilities/paginator.rb
@@ -0,0 +1,111 @@
+module Utilities
+ module Paginator
+ ELLIPSES = '...'.freeze
+ OFFSET = 1
+ FIRST_PAGE = 1
+ DIVIDER = 2
+
+ def items_to_display(model:, items_per_page: 5, current_page: 1)
+ @items_per_page = items_per_page
+ @current_page = current_page
+ @number_of_items = model.count
+ @last_page = (@number_of_items.to_f / @items_per_page).ceil
+ model.offset(items_per_page * (@current_page - OFFSET))
+ .limit(items_per_page)
+ end
+
+ def paginator(max_page_blocks: 7)
+ render partial: 'shared/paginator',
+ locals: { blocks: pages_to_display(@current_page,
+ max_page_blocks,
+ @last_page),
+ current_page: @current_page,
+ previous_link_css_class: previous_link_css_class,
+ next_link_css_class: next_link_css_class,
+ last_page: @last_page }
+ end
+
+ def page_item_css_class(page, current_page)
+ if page == current_page
+ 'page-item active'
+ elsif page.to_s == ELLIPSES
+ 'page-item disabled'
+ end
+ end
+
+ private
+
+ def previous_link_css_class
+ @current_page == FIRST_PAGE ? 'page-item disabled' : 'page-item'
+ end
+
+ def next_link_css_class
+ @current_page == @last_page ? 'page-item disabled' : 'page-item'
+ end
+
+ def pages_to_display(current_page, max_page_blocks, number_of_pages)
+ if all_pages_fit?(number_of_pages, max_page_blocks)
+ complete_pagination(number_of_pages)
+ elsif current_page_at_the_beginning?(current_page, max_page_blocks)
+ beginning_pagination(max_page_blocks)
+ elsif current_page_at_the_end?(current_page, number_of_pages,
+ max_page_blocks)
+ end_pagination(max_page_blocks, number_of_pages)
+ else
+ general_pagination(current_page, max_page_blocks)
+ end
+ end
+
+ def all_pages_fit?(number_of_pages, max_page_blocks)
+ number_of_pages < max_page_blocks
+ end
+
+ def current_page_at_the_beginning?(current_page, max_page_blocks)
+ current_page <= (max_page_blocks / DIVIDER)
+ end
+
+ def current_page_at_the_end?(current_page, number_of_pages, max_page_blocks)
+ current_page > (number_of_pages - (max_page_blocks / DIVIDER))
+ end
+
+ def general_pagination(current_page, max_page_blocks)
+ if max_page_blocks == 1
+ [current_page]
+ elsif max_page_blocks == 2
+ [current_page, ELLIPSES]
+ elsif max_page_blocks.odd?
+ odd_pages(current_page, max_page_blocks)
+ else
+ even_pages(current_page, max_page_blocks)
+ end
+ end
+
+ def beginning_pagination(max_page_blocks)
+ start = FIRST_PAGE
+ finish = max_page_blocks - OFFSET
+ [*(start..finish), ELLIPSES]
+ end
+
+ def end_pagination(max_page_blocks, number_of_pages)
+ start = number_of_pages - max_page_blocks + OFFSET * 2
+ finish = number_of_pages
+ [ELLIPSES, *(start..finish)]
+ end
+
+ def complete_pagination(last_page)
+ [*FIRST_PAGE..last_page]
+ end
+
+ def odd_pages(current_page, max_page_blocks)
+ start = (current_page - (max_page_blocks - OFFSET) / DIVIDER) + OFFSET
+ finish = (current_page + (max_page_blocks - OFFSET) / DIVIDER) - OFFSET
+ [ELLIPSES, *(start..finish), ELLIPSES]
+ end
+
+ def even_pages(current_page, max_page_blocks)
+ start = (current_page - (max_page_blocks - OFFSET) / DIVIDER) + OFFSET
+ finish = (current_page + (max_page_blocks / DIVIDER)) - OFFSET
+ [ELLIPSES, *(start..finish), ELLIPSES]
+ end
+ end
+end
diff --git a/spec/modules/paginator_spec.rb b/spec/modules/paginator_spec.rb
new file mode 100644
index 0000000..4d0cc34
--- /dev/null
+++ b/spec/modules/paginator_spec.rb
@@ -0,0 +1,93 @@
+require 'rails_helper'
+
+RSpec.describe Utilities::Paginator, type: :module do
+ let!(:paginator) { Class.new { extend Utilities::Paginator } }
+
+ describe('#pages_to_display') do
+ subject(:pages_to_display) do
+ paginator.send(:pages_to_display,
+ current_page,
+ maximum_page_blocks,
+ number_of_pages)
+ end
+
+ context 'All pages fit within the pagination list' do
+ let(:maximum_page_blocks) { 7 }
+ let(:current_page) { 1 }
+ let(:number_of_pages) { 5 }
+ let(:expected_range_of_pages) { [1, 2, 3, 4, 5] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+
+ context 'Current page is in the beginning and there are more pages towards the end of the pagination list' do
+ let(:maximum_page_blocks) { 7 }
+ let(:current_page) { 3 }
+ let(:number_of_pages) { 11 }
+ let(:expected_range_of_pages) { [1, 2, 3, 4, 5, 6, '...'] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+
+ context 'Current page is at the end and there are more pages towards the beginning of the pagination list' do
+ let(:maximum_page_blocks) { 7 }
+ let(:current_page) { 9 }
+ let(:number_of_pages) { 11 }
+ let(:expected_range_of_pages) { ['...', 6, 7, 8, 9, 10, 11] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+
+ context 'Current page is in the middle and there are more pages towards the beginning and the end of the pagination list' do
+ context 'Maximum number of page blocks is 1' do
+ let(:maximum_page_blocks) { 1 }
+ let(:current_page) { 5 }
+ let(:number_of_pages) { 11 }
+ let(:expected_range_of_pages) { [5] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+
+ context 'Maximum number of page blocks is 2' do
+ let(:maximum_page_blocks) { 2 }
+ let(:current_page) { 6 }
+ let(:number_of_pages) { 11 }
+ let(:expected_range_of_pages) { [6, '...'] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+
+ context 'Maximum number of page blocks is odd' do
+ let(:maximum_page_blocks) { 7 }
+ let(:current_page) { 5 }
+ let(:number_of_pages) { 11 }
+ let(:expected_range_of_pages) { ['...', 3, 4, 5, 6, 7, '...'] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+
+ context 'Maximum number of page blocks is even' do
+ let(:maximum_page_blocks) { 6 }
+ let(:current_page) { 5 }
+ let(:number_of_pages) { 11 }
+ let(:expected_range_of_pages) { ['...', 4, 5, 6, 7, '...'] }
+
+ it 'displays correct range of pages' do
+ expect(pages_to_display).to eq expected_range_of_pages
+ end
+ end
+ end
+ end
+end