File Coverage

blib/lib/Articulate/Sortation/Slug.pm
Criterion Covered Total %
statement 33 37 89.1
branch 16 22 72.7
condition 2 2 100.0
subroutine 6 6 100.0
pod 1 1 100.0
total 58 68 85.2


line stmt bran cond sub pod time code
1             package Articulate::Sortation::Slug;
2 2     2   1263 use strict;
  2         2  
  2         66  
3 2     2   8 use warnings;
  2         4  
  2         38  
4              
5 2     2   8 use Moo;
  2         5  
  2         7  
6             with 'Articulate::Role::Sortation::AllYouNeedIsCmp';
7              
8             =head1 NAME
9              
10             Articulate::Sortation::Slug - natural slug sorting
11              
12             =head1 DESCRIPTION
13              
14             This implements the L
15             role to provide a sorter object which will break up
16              
17             =head1 METHODS
18              
19             One method provided here, the rest are as in
20             L.
21              
22             =head3 cmp
23              
24             $self->cmp('foo-1', 'bar'); # bar comes before foo
25             $self->cmp('foo-2', 'foo-11'); # 2 comes before 11
26             $self->cmp('foo-2', 'food'); # foo comes before food
27              
28             Splits into numerical and alphabetical. Sorts the former numerically
29             and the latter alphabetically. All characters other than a-z and 0-9
30             are treated as 'word breaks' and divide up components but are otherwise
31             ignored.
32              
33             =cut
34              
35 1     1   5 sub _left { -1 }
36              
37 4     4   20 sub _right { 1 }
38              
39             sub cmp {
40 19     19 1 25 my $self = shift;
41 19         19 my $left = shift;
42 19         16 my $right = shift;
43 19         59 my $re_break = qr/(?: # Breaks between groups of characters
44             # (?<=[a-z])|(?=[a-z]) # aa
45             (?<=[a-z])(?![a-z]) # a0
46             | (?
47             | (?<=[0-9])(?![0-9]) # 0_
48             | (?
49             )/ix;
50 19         192 my $la = [ grep { $_ ne '' } split( $re_break, $left ) ];
  55         131  
51 19         148 my $ra = [ grep { $_ ne '' } split( $re_break, $right ) ];
  50         95  
52              
53             #warn Dump {left => {$left, $la}, right => {$right,$ra}};
54 19   100     82 while ( scalar @$la && scalar @$ra ) {
55 39         54 my $l = shift @$la;
56 39         57 my $r = shift @$ra;
57 39 100       156 if ( $l =~ /^[^a-z0-9]/i ) {
    100          
    100          
58 11 50       51 if ( $r =~ /^[a-z0-9]/i ) {
59              
60             # left is dash and right is not - left wins
61 0         0 return _left;
62             }
63 11         65 next; # otherwise both are dash - continue
64             }
65             elsif ( $r =~ /^[^a-z0-9]/i ) {
66              
67             # right is dash and left is not - right wins
68 1         4 return _right;
69             }
70             elsif ( $l =~ /^[0-9]/ ) {
71 11 50       27 if ( $r =~ /^[0-9]/ ) {
72              
73             # both are numbers
74 11         23 my $res = ( $l <=> $r );
75 11 100       80 return $res if $res;
76             }
77             else {
78             # left is number, right is alpha - left wins
79 0         0 return _left;
80             }
81             }
82             else {
83             # both are alphabetic
84 16         20 my $res = ( $l cmp $r );
85 16 100       75 return $res if $res;
86             }
87             }
88 4 50       14 return @$ra ? _left : 0 if ( !@$la );
    100          
89 3 50       17 return _right if ( !@$ra );
90 0 0         die 'shouldn\'t be here' if $left ne $right;
91 0           return $left cmp $right;
92             }
93              
94             =head1 SEE ALSO
95              
96             =over
97              
98             =item * L
99              
100             =item * L
101              
102             =back
103              
104             =cut
105              
106             1;