File Coverage

blib/lib/JSON/Feed.pm
Criterion Covered Total %
statement 43 48 89.5
branch 7 12 58.3
condition n/a
subroutine 11 12 91.6
pod 5 5 100.0
total 66 77 85.7


line stmt bran cond sub pod time code
1             package JSON::Feed;
2             our $VERSION = 0.001;
3 2     2   432561 use Moo;
  2         21291  
  2         10  
4 2     2   3639 use namespace::clean;
  2         21759  
  2         14  
5              
6 2     2   1464 use Ref::Util qw< is_globref is_ioref is_scalarref >;
  2         3093  
  2         155  
7 2     2   775 use Path::Tiny qw< path >;
  2         10738  
  2         119  
8 2     2   15 use Try::Tiny;
  2         4  
  2         98  
9 2     2   603 use JSON;
  2         9619  
  2         15  
10              
11 2     2   1144 use JSON::Feed::Types qw;
  2         10  
  2         21  
12              
13             has feed => (
14             is => 'ro',
15             default => sub {
16             return +{
17             version => "https://jsonfeed.org/version/1",
18             title => 'Untitle',
19             items => [],
20             };
21             },
22             isa => JSONFeed,
23             );
24              
25             around BUILDARGS => sub {
26             my ( $orig, $class, %args ) = @_;
27              
28             if ( exists $args{feed} ) {
29             return $class->$orig( feed => \%args );
30             }
31              
32             return +{
33             feed => +{
34             version => "https://jsonfeed.org/version/1",
35             title => 'Unknown',
36             items => [],
37              
38             %args
39             }
40             };
41             };
42              
43             sub parse {
44 33     33 1 50582 my ( $class, $o ) = @_;
45              
46 33         50 my $data;
47 33 100       461 if ( is_globref($o) ) {
    100          
    50          
48 11         65 local $/;
49 11         1887 my $content = <$o>;
50 11 50       80 if ( utf8::is_utf8($content) ) {
51 11         59 $data = from_json($content);
52             }
53             else {
54 0         0 $data = decode_json($content);
55             }
56              
57             }
58             elsif ( is_scalarref($o) ) {
59 11         2872 $data = decode_json($$o);
60             }
61             elsif ( -f $o ) {
62 11         73 $data = decode_json( path($o)->slurp );
63             }
64             else {
65 0         0 die "Unable to tell the type of argument";
66             }
67              
68 33         10320 JSONFeed->assert_valid($data);
69              
70 33         5244 return $class->new(%$data);
71             }
72              
73             sub get {
74 33     33 1 5291 my ( $self, $attr_name ) = @_;
75 33         202 return $self->feed->{$attr_name};
76             }
77              
78             sub set {
79 0     0 1 0 my ( $self, $attr_name, $v ) = @_;
80 0         0 return $self->feed->{$attr_name} = $v;
81             }
82              
83             sub add_item {
84 2     2 1 174 my ( $self, %item ) = @_;
85 2         4 my $item = \%item;
86 2         8 JSONFeedItem->assert_valid($item);
87 2         159 push @{ $self->feed->{items} }, $item;
  2         13  
88             }
89              
90             sub to_string {
91 1     1 1 6 my ($self) = @_;
92 1         4 my $feed = $self->feed;
93 1 50       5 if (exists $feed->{expired}) {
94 0 0       0 $feed->{expired} = $feed->{expired} ? JSON::true : JSON::false;
95             }
96 1         6 return to_json( $feed );
97             }
98              
99             1;
100              
101             =head1 NAME
102              
103             JSON::Feed - Syndication with JSON.
104              
105             =head1 SYNOPSIS
106              
107             Parsing:
108              
109             JSON::Feed->parse( '/path/feed.json' );
110             JSON::Feed->parse( $file_handle );
111             JSON::Feed->parse( \$src );
112              
113             Generating:
114              
115             # Initialize, with some content.
116             my $feed = JSON::Feed->new(
117             title => "An example of JSON feed",
118             feed_url => "https://example.com/feed.json",
119             items => [
120             +{
121             id => 42,
122             url => 'https://example.com/item/42',
123             summary => 'An item with some summary',
124             date_published: "2019-03-06T09:24:03+09:00"
125             },
126             +{
127             id => 623,
128             url => 'https://example.com/item/623',
129             summary => 'An item with some summary',
130             date_published: "2019-03-07T06:22:51+09:00"
131             },
132             ]
133             );
134              
135             # Mutate
136             $feed->set( description => 'Some description here.' );
137             $feed->add_item(
138             id => 789,
139             title => "Another URL-less item here",
140             summary => "Another item here. Lorem ipsum yatta yatta yatta.",
141             );
142              
143             # Output
144             print $fh $feed->to_string;
145              
146             =head1 DESCRIPTION
147              
148             L is a simple format for website syndication
149             with JSON, instead of XML.
150              
151             This module implements minimal amout of API for parsing and/or generating such
152             feeds. The users of this module should glance over the jsonfeed spec in order
153             to correctly generate a JSON::Feed.
154              
155             Here's a short but yet comprehensive Type mapping between jsonfeed spec and
156             perl.
157              
158             | jsonfeed | perl |
159             |----------+----------------------------|
160             | object | HashRef |
161             | boolean | JSON::true, or JSON::false |
162             | string | Str |
163             | array | ArrayRef |
164              
165             =head1 METHODS
166              
167             =over 4
168              
169             =item set( $attr, $value )
170              
171             The C<$attr> here must be a name from one top-level attribute
172             from L.
173              
174             The passed C<$value> thus must be the corresponding value.
175              
176             Most of the values from spec are strings and that maps to a perl scalar veraible.
177             The term `object` in the spec is mapped to a perl HashRef.
178              
179             Be aware that the spec allows feed extensions by prefixng attributes with
180             underscore character. Thus, all strings begin with C<'_'> are valid. Whatever
181             those extented attributes mapped to are left as-is.
182              
183             =item get( $attr )
184              
185             Retrieve the value of the given top-level varible.
186              
187             =item add_item( JSONFeedItem $item )
188              
189             Apend the given C<$item> to the C attribute. The type of input C<$item>
190             is described in the "Items" section of L.
191              
192             =item to_string()
193              
194             Stringify this JSON Feed. At this moment, the feed structure is checked and if
195             it is invalid, an exception is thrown.
196              
197             =item parse( $file_name )
198              
199             Take C<$file_name> that should be a valid JSON feed, and build an object from
200             it. Exception will be thrown if the input is not a valid JSON feed.
201              
202             =item parse( $file_handle )
203              
204             Take a pre-opened C<$file_handle> that should be a valid JSON feed and produce
205             an object from it. This slurps the rest of $file_handle but leave it open.
206             Exception will be thrown if the input is not a valid JSON feed.
207              
208             =item parse( \$json_feed_text )
209              
210             Take a reference to a string that is assumed to be a valid json feed and
211             produce an object from it. Exception will be thrown if the input is not a
212             valid JSON feed.
213              
214             =back
215              
216             =head1 References
217              
218             JSON Feed spec v1 L
219              
220             =head1 AUTHOR
221              
222             Kang-min Liu
223              
224             =head1 LICENSE
225              
226             CC0