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/controllers/bets_controller.rb b/app/controllers/bets_controller.rb index 3e2da6b..13d848a 100644 --- a/app/controllers/bets_controller.rb +++ b/app/controllers/bets_controller.rb @@ -1,6 +1,12 @@ class BetsController < ApplicationController + include Utilities::Paginator + helper Utilities::Paginator + def index - @bets = Bet.includes(:user_bets) + current_page = params[:page].nil? ? 1 : params[:page].to_i + @bets = items_to_display(model: Bet, + items_per_page: 25, + current_page: current_page) end def show diff --git a/app/views/bets/index.html.erb b/app/views/bets/index.html.erb index 67c00f2..9b5f3a8 100644 --- a/app/views/bets/index.html.erb +++ b/app/views/bets/index.html.erb @@ -26,5 +26,12 @@ +
+ +
<% end %> 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 @@ +
+
    + <%= + content_tag :li, class: previous_link_css_class do + link_to bets_path(page: '1'), class: 'page-link' do + content_tag :i, nil, class: 'fa fa-angle-double-left' + end + end + %> + <%= + content_tag :li, class: previous_link_css_class do + link_to bets_path(page: current_page - 1), class: 'page-link' do + content_tag :i, nil, class: 'fa fa-angle-left' + end + end + %> + + <% blocks.each do |page|%> + <%= render 'shared/blocks', page: page, + page_item_css_class: page_item_css_class(page, current_page) %> + <% end %> + + <%= + content_tag :li, class: next_link_css_class do + link_to bets_path(page: current_page + 1), class: 'page-link' do + content_tag :i, nil, class: 'fa fa-angle-right' + end + end + %> + <%= + content_tag :li, class: next_link_css_class do + link_to bets_path(page: last_page), class: 'page-link' do + content_tag :i, nil, class: 'fa fa-angle-double-right' + end + end + %> +
+
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