File Coverage

blib/lib/WWW/Shorten/Yourls.pm
Criterion Covered Total %
statement 31 164 18.9
branch 0 78 0.0
condition 0 49 0.0
subroutine 11 20 55.0
pod n/a
total 42 311 13.5


line stmt bran cond sub pod time code
1             package WWW::Shorten::Yourls;
2              
3 1     1   13725 use strict;
  1         2  
  1         25  
4 1     1   3 use warnings;
  1         1  
  1         20  
5 1     1   3 use Carp ();
  1         1  
  1         9  
6 1     1   502 use File::HomeDir ();
  1         3879  
  1         16  
7 1     1   4 use File::Spec ();
  1         2  
  1         11  
8 1     1   367 use JSON::MaybeXS;
  1         5965  
  1         58  
9 1     1   697 use Path::Tiny qw(path);
  1         9326  
  1         66  
10 1     1   6 use Scalar::Util qw(blessed);
  1         1  
  1         64  
11 1     1   554 use URI ();
  1         4732  
  1         35  
12              
13 1     1   22 use base qw( WWW::Shorten::generic Exporter );
  1         1  
  1         793  
14              
15             our $VERSION = '1.000';
16             $VERSION = eval $VERSION;
17              
18             our @EXPORT = qw(new);
19              
20             # _attr (private)
21             sub _attr {
22 0     0     my $self = shift;
23 0   0       my $attr = lc(_trim(shift) || '');
24             # attribute list is small enough to just grep each time. meh.
25 0 0         Carp::croak("Invalid attribute") unless grep {$attr eq $_} @{_attrs()};
  0            
  0            
26 0 0         return $self->{$attr} unless @_;
27              
28 0           my $val = shift;
29 0 0         unless (defined($val)) {
30 0           $self->{$attr} = undef;
31 0           return $self;
32             }
33 0           $self->{$attr} = $val;
34 0           return $self;
35             }
36              
37             # _attrs (static, private)
38             {
39             my $attrs; # mimic the state keyword
40             sub _attrs {
41 0 0   0     return [@{$attrs}] if $attrs;
  0            
42 0           $attrs = [
43             qw(username password server signature),
44             ];
45 0           return [@{$attrs}];
  0            
46             }
47             }
48              
49             # _json_request (static, private)
50             sub _json_request {
51 0     0     my $url = shift;
52 0 0 0       Carp::croak("Invalid URI object") unless $url && blessed($url) && $url->isa('URI');
      0        
53 0           my $ua = __PACKAGE__->ua();
54 0           my $res = $ua->get($url);
55 0 0         Carp::croak("Invalid response") unless $res;
56 0 0         unless ($res->is_success) {
57 0           Carp::croak($res->status_line);
58             }
59              
60 0           my $content_type = $res->header('Content-Type');
61 0           my $content = $res->decoded_content();
62 0 0 0       unless ($content_type && $content_type =~ m{application/json}) {
63 0           Carp::croak("Unexpected response: $content");
64             }
65 0           my $json = decode_json($content);
66 0 0         Carp::croak("Invalid data returned: $content") unless $json;
67 0           return $json;
68             }
69              
70             # _parse_args (static, private)
71             sub _parse_args {
72 0     0     my $args;
73 0 0 0       if ( @_ == 1 && ref $_[0] ) {
    0 0        
    0          
74 0           my %copy = eval { %{ $_[0] } }; # try shallow copy
  0            
  0            
75 0 0         Carp::croak("Argument to method could not be dereferenced as a hash") if $@;
76 0           $args = \%copy;
77             }
78             elsif (@_==1 && !ref($_[0])) {
79 0           $args = {single_arg => $_[0]};
80             }
81             elsif ( @_ % 2 == 0 ) {
82 0           $args = {@_};
83             }
84             else {
85 0           Carp::croak("Method got an odd number of elements");
86             }
87 0           return $args;
88             }
89              
90             # _parse_config (static, private)
91             {
92             my $config; # mimic the state keyword
93             sub _parse_config {
94             # always give back a shallow copy
95 0 0   0     return {%{$config}} if $config;
  0            
96             # only parse the file once, please.
97 0           $config = {};
98 0 0         my $file = $^O eq 'MSWin32'? '_yourls': '.yourls';
99 0 0         $file .= '_test' if $ENV{YOURLS_TEST_CONFIG};
100 0           my $path = path(File::Spec->catfile(File::HomeDir->my_home(), $file));
101              
102 0 0 0       if ($path && $path->is_file) {
103 0           my @lines = $path->lines_utf8({chomp => 1});
104 0           my $attrs = _attrs();
105              
106 0           for my $line (@lines) {
107 0   0       $line = _trim($line) || '';
108 0 0         next if $line =~ /^\s*[;#]/; # skip comments
109 0           $line =~ s/\s+[;#].*$//gm; # trim off comments
110 0 0 0       next unless $line && $line =~ /=/; # make sure we have a =
111              
112 0           my ($key, $val) = split(/(?
113 0   0       $key = lc(_trim($key) || '');
114 0           $val = _trim($val);
115 0 0 0       next unless $key && $val;
116 0 0         $key = 'username' if $key eq 'user';
117 0 0         $key = 'server' if $key eq 'base';
118 0 0         next unless grep {$key eq $_} @{$attrs};
  0            
  0            
119 0           $config->{$key} = $val;
120             }
121             }
122 0           return {%{$config}};
  0            
123             }
124             }
125              
126             # _trim (static, private)
127             sub _trim {
128 0     0     my $input = shift;
129 0 0 0       return $input unless defined $input && !ref($input) && length($input);
      0        
130 0           $input =~ s/\A\s*//;
131 0           $input =~ s/\s*\z//;
132 0           return $input;
133             }
134              
135             sub new {
136 0     0     my $class = shift;
137 0           my $args;
138 0 0 0       if ( @_ == 1 && ref $_[0] ) {
    0          
139 0           my %copy = eval { %{ $_[0] } }; # try shallow copy
  0            
  0            
140 0 0         Carp::croak("Argument to $class->new() could not be dereferenced as a hash") if $@;
141 0           $args = \%copy;
142             }
143             elsif ( @_ % 2 == 0 ) {
144 0           $args = {@_};
145             }
146             else {
147 0           Carp::croak("$class->new() got an odd number of elements");
148             }
149              
150 0           my $attrs = _attrs();
151             # start with what's in our config file (if anything)
152 0           my $href = _parse_config();
153              
154             # override with anything passed in
155 0           for my $key (keys %{$args}) {
  0            
156 0           my $lc_key = lc($key);
157 0 0         $lc_key = 'username' if $lc_key eq 'user';
158 0 0         $lc_key = 'server' if $lc_key eq 'base';
159 0 0         next unless grep {$lc_key eq $_} @{$attrs};
  0            
  0            
160 0           $href->{$lc_key} = $args->{$key};
161             }
162 0 0         my $server = $href->{server} ? $href->{server} : 'https://yourls.org/yourls-api.php';
163 0           my $self = bless $href, $class;
164 0           return $self->server($server);
165             }
166              
167             sub clicks {
168 0     0     my $self = shift;
169 0 0         Carp::croak("You must tell us which server to use.") unless my $server = $self->server();
170              
171 0           my $args = _parse_args(@_);
172 0   0       my $short_url = $args->{shortUrl} || $args->{single_arg} || $args->{URL} || $args->{url} || '';
173 0 0         Carp::croak("A shortUrl parameter is required.\n") unless $short_url;
174              
175 0           my $url = $server->clone();
176 0           my $params = {
177             shorturl => $short_url,
178             format => 'json',
179             action => 'url-stats',
180             };
181 0 0         if (my $sig = $self->signature()) {
182 0           $params->{signature} = $sig;
183             }
184             else {
185 0           my $user = $self->username();
186 0           my $pass = $self->password();
187 0 0 0       unless ($user && $pass) {
188 0           Carp::croak("Username and password required when not using a signature");
189             }
190 0           $params->{username} = $user;
191 0           $params->{password} = $pass;
192             }
193 0           $url->query_form(%$params);
194 0           return _json_request($url);
195             }
196              
197             sub expand {
198 0     0     my $self = shift;
199 0 0         Carp::croak("You must tell us which server to use.") unless my $server = $self->server();
200              
201 0           my $args = _parse_args(@_);
202 0   0       my $short_url = $args->{shortUrl} || $args->{single_arg} || $args->{URL} || $args->{url} || '';
203 0 0         Carp::croak("A shortUrl parameter is required.\n") unless $short_url;
204              
205 0           my $url = $server->clone();
206 0           my $params = {
207             shorturl => $short_url,
208             format => 'json',
209             action => 'expand',
210             };
211 0 0         if (my $sig = $self->signature()) {
212 0           $params->{signature} = $sig;
213             }
214             else {
215 0           my $user = $self->username();
216 0           my $pass = $self->password();
217 0 0 0       unless ($user && $pass) {
218 0           Carp::croak("Username and password required when not using a signature");
219             }
220 0           $params->{username} = $user;
221 0           $params->{password} = $pass;
222             }
223 0           $url->query_form(%$params);
224 0           return _json_request($url);
225             }
226              
227             sub makealongerlink {
228             my $self;
229             if ($_[0] && blessed($_[0]) && $_[0]->isa('WWW::Shorten::Yourls')) {
230             $self = shift;
231             }
232             my $url = shift or Carp::croak('No URL passed to makealongerlink');
233             $self ||= __PACKAGE__->new(@_);
234             my $res = $self->expand(shortUrl=>$url);
235 1     1   33618 use Data::Dumper::Concise; warn Dumper $res;
  0            
  0            
236             return '' unless ref($res) eq 'HASH' and $res->{longurl};
237             return $res->{longurl};
238             }
239              
240             sub makeashorterlink {
241             my $self;
242             if ($_[0] && blessed($_[0]) && $_[0]->isa('WWW::Shorten::Yourls')) {
243             $self = shift;
244             }
245             my $url = shift or Carp::croak('No URL passed to makeashorterlink');
246             $self ||= __PACKAGE__->new(@_);
247             my $res = $self->shorten(longUrl=>$url, @_);
248             return $res->{shorturl};
249             }
250              
251             sub password { return shift->_attr('password', @_); }
252              
253             sub server {
254             my $self = shift;
255             return $self->{server} unless @_;
256             my $val = shift;
257             if (!defined($val) || $val eq '') {
258             $self->{server} = undef;
259             return $self;
260             }
261             elsif (blessed($val) && $val->isa('URI')) {
262             $self->{server} = $val->clone();
263             return $self;
264             }
265             elsif ($val && !ref($val)) {
266             $self->{server} = URI->new(_trim($val));
267             return $self;
268             }
269              
270             Carp::croak("The server attribute must be set to a URI object");
271             }
272              
273             sub shorten {
274             my $self = shift;
275             Carp::croak("You must tell us which server to use.") unless my $server = $self->server();
276              
277             my $args = _parse_args(@_);
278             my $long_url = $args->{longUrl} || $args->{single_arg} || $args->{URL} || $args->{url} || '';
279             Carp::croak("A longUrl parameter is required.\n") unless $long_url;
280              
281             my $url = $server->clone();
282             my $params = {
283             url => $long_url,
284             format => 'json',
285             action => 'shorturl',
286             };
287             if (my $sig = $self->signature()) {
288             $params->{signature} = $sig;
289             }
290             else {
291             my $user = $self->username();
292             my $pass = $self->password();
293             unless ($user && $pass) {
294             Carp::croak("Username and password required when not using a signature");
295             }
296             $params->{username} = $user;
297             $params->{password} = $pass;
298             }
299             $url->query_form(%$params);
300             return _json_request($url);
301             }
302              
303             sub signature { return shift->_attr('signature', @_); }
304              
305             sub username { return shift->_attr('username', @_); }
306              
307             1; # End of WWW::Shorten::Yourls
308              
309             __END__