File Coverage

blib/lib/Statocles/AppRole/ExtraFeeds.pm
Criterion Covered Total %
statement 54 55 98.1
branch 8 10 80.0
condition 3 8 37.5
subroutine 11 11 100.0
pod n/a
total 76 84 90.4


line stmt bran cond sub pod time code
1 3     3   3487481 use 5.006; # our
  3         10  
2 3     3   14 use strict;
  3         5  
  3         69  
3 3     3   12 use warnings;
  3         12  
  3         234  
4              
5             package Statocles::AppRole::ExtraFeeds;
6              
7             our $VERSION = '0.001003';
8              
9             # ABSTRACT: Generate additional feed sets for apps
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 3     3   418 use Moo::Role qw( has around );
  3         14036  
  3         23  
14 3     3   875 use Carp qw( croak );
  3         4  
  3         186  
15 3     3   502 use Statocles::App 0.070 (); # 0.70 required for ->template
  3         1123886  
  3         67  
16 3     3   514 use Statocles::Page::List ();
  3         258257  
  3         76  
17 3     3   17 use namespace::autoclean;
  3         5  
  3         31  
18              
19             has 'extra_feeds' => (
20             is => 'ro',
21             lazy => 1,
22             default => sub { {} },
23             );
24              
25              
26              
27              
28              
29 3     3   496 use constant PATH_INDEX_PREFIX => qr{ \A (.*) / index [.](\w+) \z}sx;
  3         5  
  3         433  
30 3     3   35 use constant PATH_GENERIC_PREFIX => qr{ \A (.*) / ([^/.]+) [.](\w+) \z}sx;
  3         5  
  3         1525  
31              
32             # This separation is to help Coverage tools not lie to me that I did a perfect job
33             around pages => \&_around_pages;
34              
35             sub _around_pages {
36 2     2   561492 my ( $orig, $self, @rest ) = @_;
37 2         30 my (@pages) = $self->$orig(@rest);
38              
39 2 50       139020 return @pages unless @pages;
40              
41 2         4 my %feed_cache;
42              
43             my @out_pages;
44              
45 2         10 while (@pages) {
46 18         422 my $page = shift @pages;
47              
48 18         23 push @out_pages, $page;
49              
50 18         238 my (@existing_feeds) = $page->links('feed');
51              
52 18 100       1292 next if not @existing_feeds;
53              
54 6         7 for my $feed_id ( sort keys %{ $self->extra_feeds } ) {
  6         83  
55 6         108 my $feed = $self->extra_feeds->{$feed_id};
56 6         23 my $feed_path;
57              
58             {
59 6         7 my $reference_path = $existing_feeds[0]->href;
  6         69  
60 6   33     40 my $feed_suffix = $feed->{name} || $feed_id;
61              
62 6 100       36 if ( $reference_path =~ PATH_INDEX_PREFIX ) {
    50          
63 3         11 $feed_path = "$1/$feed_suffix";
64             }
65             elsif ( $reference_path =~ PATH_GENERIC_PREFIX ) {
66 3         17 $feed_path = "$1/$2.$feed_suffix";
67             }
68             else {
69 0         0 croak "Don't know how to derive feed path from $reference_path for $feed_suffix";
70             }
71             }
72              
73 6 100       14 if ( not exists $feed_cache{$feed_path} ) {
74             my $feed_page = $feed_cache{$feed_path}{feed_page} = Statocles::Page::List->new(
75             app => $self,
76             pages => $page->pages,
77             path => $feed_path,
78             template => $self->template( $feed->{template} || $feed->{name} || $feed_id ),
79             links => {
80             alternate => [
81             $self->link(
82             href => $page->path,
83 4   33     12 title => ( $feed->{'index_title'} || $page->title || 'Web Index' ),
      50        
84             type => $page->type,
85             ),
86             ],
87             },
88             );
89              
90             $feed_cache{$feed_path}{feed_link} = $self->link(
91             text => $feed->{text},
92 4         4771 href => $feed_page->path->stringify,
93             type => $feed_page->type,
94             );
95             }
96 6         643 $page->links( feed => $feed_cache{$feed_path}{feed_link} );
97             }
98             }
99 2         10 return @out_pages, map { $feed_cache{$_}{feed_page} } sort keys %feed_cache;
  4         71  
100             }
101              
102             1;
103              
104             __END__
105              
106             =pod
107              
108             =encoding UTF-8
109              
110             =head1 NAME
111              
112             Statocles::AppRole::ExtraFeeds - Generate additional feed sets for apps
113              
114             =head1 VERSION
115              
116             version 0.001003
117              
118             =head1 EXPERIMENTAL
119              
120             This module is very new and it comes with the following present caveats:
121              
122             =over 4
123              
124             =item * Application outside C<Statocles::App::Blog> untested.
125              
126             Feedback welcome though, and it might work by magic!
127              
128             =item * Implementation details a bit sketchy
129              
130             The code works. I kinda feel like it shouldn't, and its like I've performed some magic
131             trick and the gods have smiled on me for a moment.
132              
133             =item * You're on your own with templates
134              
135             This at present is a glorified lump of glue on top of existing C<Statocles> behavior.
136              
137             As such, if you want this to work, you'll probably want to copy some templates and modify them.
138              
139             This module does nothing for you in terms of the actual formatting, it just pumps the right
140             glue so that the same code that generates the existing feeds will be invoked a few more times
141             but with the file names and templates you chose ( instead of the ones provided by default by the app )
142              
143             Basically, you're going to want to copy C<blog/index.rss.ep> to C<blog/fulltext.rss.ep> and tweak
144             it a bit, or something.
145              
146             =back
147              
148             =head1 DESCRIPTION
149              
150             This module is a role that can be applied to any C<Statocles::App> in a C<Statocles>'s C<Beam::Wire>
151             configuration.
152              
153             ...
154             blog_app:
155             class: 'Statocles::App::Blog'
156             with: 'Statocles::AppRole::ExtraFeeds'
157             args:
158             url_root: '/blog'
159             # ... more Statocles::App::Blog args
160             extra_feeds:
161             fulltext.rss:
162             text: "RSS FullText"
163              
164             This example creates a feed called C</blog/fulltext.rss> containing the contents of C<theme/blog/fulltext.rss.ep>
165             after template application, and is linked from every C<index> listing.
166              
167             It also creates a feed called C<< /blog/tag/<%= tagname %>.fulltext.rss >> for each tag, provisioned from the same template.
168              
169             =for Pod::Coverage PATH_INDEX_PREFIX PATH_GENERIC_PREFIX
170              
171             =head1 PARAMETERS
172              
173             =head2 C<extra_feeds>
174              
175             This Role provides one tunable parameter on its applied class, C<extra_feeds>, which contains a
176             mapping of
177              
178             id => spec
179              
180             =head3 C<extra_feeds> spec.
181              
182             {
183             text => required
184             name => default( id )
185             template => default( id )
186             }
187              
188             =head4 C<text>
189              
190             This is the name of the feed when shown in links on both C<index> and C<tag index> pages.
191              
192             =head4 C<template>
193              
194             This is the name of the template to render the feeds content into.
195              
196             Defaults to taking the same value as the key in the C<extra_fields> hash.
197              
198             =head4 C<name>
199              
200             This is the name of the file/file suffix that is generated.
201              
202             It defaults to the same value as the key in the C<extra_feeds>
203             hash.
204              
205             So:
206              
207             extra_feeds:
208             fulltext.rss:
209             text: "My FullText RSS"
210              
211             And
212              
213             extra_feeds:
214             genericlabel:
215             text: "My FullText RSS"
216             name: 'fulltext.rss' # output name
217             template: 'fulltext.rss' # source template
218              
219             Should both generate the same result.
220              
221             =head1 AUTHOR
222              
223             Kent Fredric <kentnl@cpan.org>
224              
225             =head1 COPYRIGHT AND LICENSE
226              
227             This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
228              
229             This is free software; you can redistribute it and/or modify it under
230             the same terms as the Perl 5 programming language system itself.
231              
232             =cut