File Coverage

blib/lib/Sort/Sub.pm
Criterion Covered Total %
statement 49 49 100.0
branch 10 12 83.3
condition 4 4 100.0
subroutine 7 7 100.0
pod 1 1 100.0
total 71 73 97.2


line stmt bran cond sub pod time code
1             package Sort::Sub;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2020-05-25'; # DATE
5             our $DIST = 'Sort-Sub'; # DIST
6             our $VERSION = '0.120'; # VERSION
7              
8 16     16   691 use 5.010001;
  16         49  
9 16     16   81 use strict 'subs', 'vars';
  16         23  
  16         409  
10 16     16   101 use warnings;
  16         37  
  16         426  
11 16     16   25395 use Log::ger;
  16         786  
  16         68  
12              
13             our $re_spec = qr/\A(\$)?(\w+)(?:<(\w*)>)?\z/;
14              
15             our %argsopt_sortsub = (
16             sort_sub => {
17             summary => 'Name of a Sort::Sub::* module (without the prefix)',
18             schema => ['sortsub::spec*'],
19             },
20             sort_args => {
21             'x.name.is_plural' => 1,
22             'x.name.singular' => 'sort_arg',
23             summary => 'Arguments to pass to the Sort::Sub::* routine',
24             schema => ['array*', of=>'str*'],
25             element_completion => sub {
26             my %cargs = @_;
27              
28             # do we have the routine already? if yes, extract the metadata
29             my $rname = $cargs{args}{sort_sub};
30             return [] unless defined $rname;
31              
32             my $mod = "Sort::Sub::$rname";
33             (my $mod_pm = "$mod.pm") =~ s!::!/!g;
34             eval { require $mod_pm };
35             return {message=>"Cannot load $mod: $@"} if $@;
36             my $meta;
37             eval { $meta = $mod->meta };
38             return [] unless $meta;
39              
40             require Complete::Sequence;
41             return Complete::Sequence::complete_sequence(
42             word => $cargs{word},
43             sequence => [
44             sub {
45             [$meta->{args} ? keys(%{ $meta->{args} }) : ()];
46             },
47             '=',
48             sub {
49             my $stash = shift;
50             my $argname = $stash->{completed_item_words}[0];
51             return [] unless defined $argname;
52              
53             my $argspec = $meta->{args}{$argname};
54             return [] unless $argspec->{schema};
55              
56             require Complete::Sah;
57             require Complete::Util;
58             Complete::Util::arrayify_answer(
59             Complete::Sah::complete_from_schema(
60             word => $stash->{cur_word},
61             schema => $argspec->{schema},
62             )
63             );
64              
65             },
66             ],
67             );
68             },
69             },
70             );
71              
72             sub get_sorter {
73 43     43 1 1117 my ($spec, $args, $with_meta) = @_;
74              
75 43 50       437 my ($is_var, $name, $opts) = $spec =~ $re_spec
76             or die "Invalid sorter spec '$spec', please use: ".
77             '[$]NAME [ <OPTS> ]';
78 43         7142 require "Sort/Sub/$name.pm";
79 43   100     183 $opts //= "";
80 43         113 my $is_reverse = $opts =~ /r/;
81 43         89 my $is_ci = $opts =~ /i/;
82 43         55 my $gen_sorter = \&{"Sort::Sub::$name\::gen_sorter"};
  43         156  
83 43   100     169 my $sorter = $gen_sorter->($is_reverse, $is_ci, $args // {});
84 43 100       109 if ($with_meta) {
85 1         1 my $meta = {};
86 1         2 eval { $meta = &{"Sort::Sub::$name\::meta"}() };
  1         1  
  1         3  
87 1 50       2 warn if $@;
88 1         4 return ($sorter, $meta);
89             } else {
90 42         102 return $sorter;
91             }
92             }
93              
94             sub import {
95 41     41   835 my $class = shift;
96 41         76 my $caller = caller;
97              
98 41         65 my $i = -1;
99 41         53 while (1) {
100 82         117 $i++;
101 82 100       245 last if $i >= @_;
102 41         68 my $import = $_[$i];
103 41         63 my $args = {};
104 41 100       121 if (ref $_[$i+1] eq 'HASH') {
105 13         28 $args = $_[$i+1];
106 13         16 $i++;
107             }
108 41         132 my $sorter = get_sorter($import, $args);
109 41         319 my ($is_var, $name) = $import =~ $re_spec; # XXX double matching
110 41 100       102 if ($is_var) {
111 1         1 ${"$caller\::$name"} = \&$sorter;
  1         4  
112             } else {
113 16     16   14852 no warnings 'redefine';
  16         44  
  16         1461  
114 40         69 *{"$caller\::$name"} = \&$sorter;
  40         357  
115             }
116             }
117             }
118              
119             1;
120             # ABSTRACT: Collection of sort subroutines
121              
122             __END__
123              
124             =pod
125              
126             =encoding UTF-8
127              
128             =head1 NAME
129              
130             Sort::Sub - Collection of sort subroutines
131              
132             =head1 VERSION
133              
134             This document describes version 0.120 of Sort::Sub (from Perl distribution Sort-Sub), released on 2020-05-25.
135              
136             =head1 SYNOPSIS
137              
138             use Sort::Sub qw($naturally);
139              
140             my @sorted = sort $naturally ('track1.mp3', 'track10.mp3', 'track2.mp3', 'track1b.mp3', 'track1a.mp3');
141             # => ('track1.mp3', 'track1a.mp3', 'track1b.mp3', 'track2.mp3', 'track10.mp3')
142              
143             Request as subroutine:
144              
145             use Sort::Sub qw(naturally);
146              
147             my @sorted = sort {naturally} (...);
148              
149             Request a reverse sort:
150              
151             use Sort::Sub qw($naturally<r>);
152              
153             my @sorted = sort $naturally (...);
154             # => ('track10.mp3', 'track2.mp3', 'track1b.mp3', 'track1a.mp3', 'track1.mp3')
155              
156             Request a case-insensitive sort:
157              
158             use Sort::Sub qw($naturally<i>);
159              
160             my @sorted = sort $naturally (...);
161              
162             Request a case-insensitive, reverse sort:
163              
164             use Sort::Sub qw($naturally<ir>);
165              
166             my @sorted = sort $naturally ('track2.mp3', 'Track1.mp3', 'Track10.mp3');
167             => ('Track10.mp3', 'track2.mp3', 'Track1.mp3')
168              
169             Pass arguments to sort generator routine:
170              
171             use Sort::Sub '$by_num_of_colons', {pattern=>':'};
172              
173             my @sorted = sort $by_num_of_colons ('a::','b:','c::::','d:::');
174             => ('b:','a::','d:::','c::::')
175              
176             Request a coderef directly, without using the import interface:
177              
178             use Sort::Sub;
179              
180             my $naturally = Sort::Sub::get_sorter('naturally');
181             my $naturally = Sort::Sub::get_sorter('$naturally');
182             my $rev_naturally = Sort::Sub::get_sorter('naturally<r>');
183              
184             =head1 DESCRIPTION
185              
186             L<Sort::Sub> and C<Sort::Sub::*> are a convenient packaging of any kind of
187             subroutine which you can use for C<sort()>.
188              
189             To use Sort::Sub, you import a list of:
190              
191             ["$"]NAME [ "<" [i][r] ">" ]
192              
193             Where NAME is actually searched under C<Sort::Sub::*> namespace. For example:
194              
195             naturally
196              
197             will attempt to load C<Sort::Sub::naturally> module and call its C<gen_sorter>
198             subroutine.
199              
200             You can either request a subroutine name like the above or a variable name (e.g.
201             C<$naturally>).
202              
203             After the name, you can add some options, enclosed with angle brackets C<< <>
204             >>. There are some known options, e.g. C<i> (for case-insensitive sort) or C<r>
205             (for reverse sort). Some examples:
206              
207             naturally<i>
208             naturally<r>
209             naturally<ri>
210              
211             =head1 GUIDELINES FOR WRITING A SORT::SUB::* MODULE
212              
213             The name should be in lowercase. It should be an adverb (e.g. C<naturally>) or a
214             phrase with words separated by underscore (C<_>) and the phrase begins with
215             C<by> (e.g. C<by_num_and_non_num_parts>).
216              
217             The module must contain a C<gen_sorter> subroutine. It will be called with:
218              
219             ($is_reverse, $is_ci, $args)
220              
221             Where C<$is_reserve> will be set to true if user requests a reverse sort,
222             C<$is_ci> will be set to true if user requests a case-insensitive sort. C<$args>
223             is hashref to pass additional arguments to the C<gen_sorter()> routine. The
224             subroutine should return a code reference.
225              
226             The module should also contain a C<meta> subroutine which returns a metadata
227             L<DefHash>. Known properties (keys) include: C<v> (currently at 1), C<summary>,
228             C<compares_record> (bool, if set to true then sorter will be fed records C<<
229             [$data, $order] >> instead of just C<$data>; C<$order> is a number that can be
230             line number of array index; this allows sorter to sort by additional information
231             instead of just the data items). Other metadata properties will be added in the
232             future.
233              
234             =head1 FUNCTIONS
235              
236             =head2 get_sorter
237              
238             Usage:
239              
240             my $coderef = Sort::Sub::get_sorter('SPEC' [ , \%args [ , $with_meta ] ]);
241              
242             Example:
243              
244             my $rev_naturally = Sort::Sub::get_sorter('naturally<r>');
245              
246             This is an alternative to using the import interface. This function is not
247             imported.
248              
249             If C<$with_meta> is set to true, will return this:
250              
251             ($sorter, $meta)
252              
253             instead of just the C<$sorter> subroutine.
254              
255             =head1 HOMEPAGE
256              
257             Please visit the project's homepage at L<https://metacpan.org/release/Sort-Sub>.
258              
259             =head1 SOURCE
260              
261             Source repository is at L<https://github.com/perlancar/perl-Sort-Sub>.
262              
263             =head1 BUGS
264              
265             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Sort-Sub>
266              
267             When submitting a bug or request, please include a test-file or a
268             patch to an existing test-file that illustrates the bug or desired
269             feature.
270              
271             =head1 SEE ALSO
272              
273             Other additional C<Sort::Sub::*> not bundled in this distribution.
274              
275             Supporting CLI's: L<sortsub> (from L<App::sortsub>), L<sorted> (from
276             L<App::sorted>), CLI's from L<App::SortSubUtils>.
277              
278             =head1 AUTHOR
279              
280             perlancar <perlancar@cpan.org>
281              
282             =head1 COPYRIGHT AND LICENSE
283              
284             This software is copyright (c) 2020, 2019, 2018, 2016, 2015 by perlancar@cpan.org.
285              
286             This is free software; you can redistribute it and/or modify it under
287             the same terms as the Perl 5 programming language system itself.
288              
289             =cut