File Coverage

blib/lib/Mojolicious/Plugin/Pager.pm
Criterion Covered Total %
statement 60 60 100.0
branch 37 40 92.5
condition 14 27 51.8
subroutine 8 8 100.0
pod 3 3 100.0
total 122 138 88.4


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Pager;
2 1     1   969 use Mojo::Base 'Mojolicious::Plugin';
  1         6  
  1         10  
3              
4 1     1   215 use POSIX ();
  1         3  
  1         18  
5              
6 1     1   6 use constant PAGE_PARAM => 'page_param_name';
  1         2  
  1         70  
7 1     1   6 use constant WINDOW_SIZE => 'pager.window_size';
  1         2  
  1         614  
8              
9             our $VERSION = '0.03';
10              
11             sub pager_link {
12 145     145 1 412 my ($self, $c, $page, @args) = @_;
13 145         764 my $url = $c->url_with;
14 145 50 33     59021 my @text = (@args and ref $args[-1] eq 'CODE') ? () : ($page->{n});
15 145         293 my (@extra, @classes);
16              
17 145 100       398 push @classes, $self->{classes}{current} if $page->{current};
18 145 100       349 push @classes, $self->{classes}{first} if $page->{first};
19 145 100       322 push @classes, $self->{classes}{last} if $page->{last};
20 145 100       346 push @classes, $self->{classes}{next} if $page->{next};
21 145 100       358 push @classes, $self->{classes}{prev} if $page->{prev};
22 145 100       432 push @classes, $self->{classes}{normal} unless @classes;
23 145 100       363 push @extra, rel => 'next' if $page->{next};
24 145 100       323 push @extra, rel => 'prev' if $page->{prev};
25              
26 145   50     348 $url->query->param($c->stash(PAGE_PARAM) => $page->{n} || 1);
27 145         9171 return $c->link_to(@text, $url, class => join(' ', @classes), @extra, @args);
28             }
29              
30             sub pages_for {
31 16     16 1 318976 my $c = shift;
32 16 50 50     131 my $args = ref $_[0] ? shift : {total_pages => shift || 1};
33 16   100     119 my $current_page = $args->{current} || $c->param($c->stash(PAGE_PARAM)) || 1;
34 16   50     5254 my $pager_size = $args->{size} || 8;
35 16         62 my $window_size = ($pager_size / 2) - 1;
36 16         117 my $total_pages = POSIX::ceil($args->{total_pages});
37 16         42 my ($start_page, @pages);
38              
39 16 100       91 if ($current_page < $window_size) {
    100          
40 3         7 $start_page = 1;
41             }
42             elsif ($current_page + $pager_size - $window_size > $total_pages) {
43 5         15 $start_page = 1 + $total_pages - $pager_size;
44             }
45             else {
46 8         19 $start_page = 1 + $current_page - $window_size;
47             }
48              
49 16         69 for my $n ($start_page .. $total_pages) {
50 130 100       309 last if @pages >= $pager_size;
51 122         331 push @pages, {n => $n};
52 122 100       297 $pages[-1]{first} = 1 if $n == 1;
53 122 100       289 $pages[-1]{last} = 1 if $n == $total_pages;
54 122 100       278 $pages[-1]{current} = 1 if $n == $current_page;
55             }
56              
57 16 50       54 return @pages unless @pages;
58 16 100       70 return @pages unless $total_pages > $pager_size;
59              
60 14 100       82 unshift @pages, {prev => 1, n => $current_page - 1} if $current_page > 1;
61 14 100       80 push @pages, {next => 1, n => $current_page + 1} if $current_page < $total_pages;
62              
63 14         88 return @pages;
64             }
65              
66             sub register {
67 1     1 1 38 my ($self, $app, $config) = @_;
68              
69 1   50     14 $app->defaults(PAGE_PARAM, $config->{param_name} || 'page');
70 1   50     30 $app->defaults(WINDOW_SIZE, $config->{window_size} || 3);
71              
72 1   50     21 $self->{classes}{current} = $config->{classes}{current} || 'active';
73 1   50     6 $self->{classes}{first} = $config->{classes}{first} || 'first';
74 1   50     7 $self->{classes}{last} = $config->{classes}{last} || 'last';
75 1   50     14 $self->{classes}{next} = $config->{classes}{next} || 'next';
76 1   50     6 $self->{classes}{prev} = $config->{classes}{prev} || 'prev';
77 1   50     6 $self->{classes}{normal} = $config->{classes}{normal} || 'page';
78              
79 1     145   9 $app->helper(pager_link => sub { $self->pager_link(@_) });
  145         61720  
80 1         34 $app->helper(pages_for => \&pages_for);
81             }
82              
83             1;
84              
85             =encoding utf8
86              
87             =head1 NAME
88              
89             Mojolicious::Plugin::Pager - Pagination plugin for Mojolicious
90              
91             =head1 SYNOPSIS
92              
93             =head2 Example lite app
94              
95             use Mojolicious::Lite;
96              
97             plugin "pager";
98              
99             get "/" => sub {
100             my $c = shift;
101             $c->stash(total_entries => 1431, entries_per_page => 20);
102             };
103              
104             =head2 Example template
105              
106            
107             % for my $page (pages_for $total_entries / $entries_per_page) {
108            
  • <%= pager_link $page %>
  • 109             % }
    110            
    111              
    112             =head2 Custom template
    113              
    114            
    115             % for my $page (pages_for $total_entries / $entries_per_page) {
    116             % my $url = url_with; $url->query->param(x => $page->{n});
    117            
  • <%= link_to "hey!", $url %>
  • 118             % }
    119            
    120              
    121             =head1 DESCRIPTION
    122              
    123             L is a L plugin for creating paged
    124             navigation, without getting in the way. There are other plugins which ship with
    125             complete markup, but this is often not the markup that I want.
    126              
    127             Note that this plugin is currently EXPERIMENTAL.
    128              
    129             =head1 HELPERS
    130              
    131             =head2 pager_link
    132              
    133             $bytestream = $c->pager_link(\%page, @args);
    134             $bytestream = $c->pager_link(\%page, @args, sub { int(rand 100) });
    135              
    136             Takes a C<%page> hash and creates an anchor using
    137             L. C<@args> is passed on, without
    138             modification, to C. The anchor generated has some classes added.
    139              
    140             See L for detail about C<%page>.
    141              
    142             Examples output:
    143              
    144            
    145             1
    146             2
    147             3
    148             4
    149             5
    150             6
    151            
    152              
    153             =head2 pages_for
    154              
    155             @pages = $self->pages_for($total_pages);
    156              
    157             Returns a list of C<%page> hash-refs, that can be passed on to L.
    158              
    159             Example C<%page>:
    160              
    161             {
    162             n => 2, # page number
    163             current => 1, # if page number matches "page" query parameter
    164             first => 1, # if this is the first page
    165             last => 1, # if this is the last page
    166             next => 1, # if this is last, that brings you to the next page
    167             prev => 1, # if this is first, that brings you to the previous page
    168             }
    169              
    170             =head1 METHODS
    171              
    172             =head2 register
    173              
    174             $app->plugin("pager" => \%config);
    175              
    176             Used to register this plugin and the L above. C<%config> can be:
    177              
    178             =over 4
    179              
    180             =item * classes
    181              
    182             Used to set default class names, used by L.
    183              
    184             Default:
    185              
    186             {
    187             current => "active",
    188             first => "first",
    189             last => "last",
    190             next => "next",
    191             prev => "prev",
    192             normal => "page",
    193             }
    194              
    195             =item * param_name
    196              
    197             The query parameter that will be looked up to figure out which page you are on.
    198             Can also be set in L on each request under the
    199             name "page_param_name".
    200              
    201             Default: "page"
    202              
    203             =item * window_size
    204              
    205             Used to decide how many pages to show after/before the current page.
    206              
    207             Default: 3
    208              
    209             =back
    210              
    211             =head1 AUTHOR
    212              
    213             Jan Henning Thorsen
    214              
    215             =head1 COPYRIGHT AND LICENSE
    216              
    217             Copyright (C) 2017, Jan Henning Thorsen
    218              
    219             This program is free software, you can redistribute it and/or modify it under
    220             the terms of the Artistic License version 2.0.
    221              
    222             =cut