File Coverage

blib/lib/JSAN/Client.pm
Criterion Covered Total %
statement 105 114 92.1
branch 17 30 56.6
condition 3 5 60.0
subroutine 27 27 100.0
pod 6 6 100.0
total 158 182 86.8


line stmt bran cond sub pod time code
1             package JSAN::Client;
2              
3             =pod
4              
5             =head1 NAME
6              
7             JSAN::Client - JSAN Client 2.0 (migrated from Class::DBI to ORLite)
8              
9             =head1 SYNOPSIS
10              
11             # Create the client object
12             my $client = JSAN::Client->new(
13             prefix => '/usr/local/js',
14             verbose => 1,
15             );
16            
17             # Install by library name
18             $client->install_library('Display.Swap');
19            
20             # Install by distribution name
21             $client->install_distribution('DOM.Trigger');
22              
23             =head1 DESCRIPTION
24              
25             The C API intended to provide the highest-possible level
26             abstraction for the process of installing JSAN distributions.
27              
28             However this class is still intended for developers, and
29             won't do things like auto-detect your C and so on.
30              
31             For more DWIM type functionality aimed at users, see the L
32             class, or even more preferably the L installer application.
33              
34             =head1 METHODS
35              
36             =cut
37              
38 3     3   259658 use 5.008005;
  3         13  
  3         119  
39 3     3   15 use strict;
  3         7  
  3         93  
40 3     3   14 use warnings;
  3         4  
  3         109  
41              
42             # Versioned loads of all our main dependencies both for certainty
43             # and to feed the information to Module::Install.
44 3     3   1797 use Algorithm::Dependency 1.106 ();
  3         24261  
  3         90  
45 3     3   7839 use Config::Tiny 2.12 ();
  3         5536  
  3         117  
46 3     3   27 use Digest::MD5 2.33 ();
  3         63  
  3         76  
47 3     3   18 use File::Spec 0.80 ();
  3         74  
  3         66  
48 3     3   21 use File::Path 1.06 ();
  3         65  
  3         63  
49 3     3   3240 use File::HomeDir 0.69 ();
  3         18282  
  3         87  
50 3     3   50 use LWP::Simple 1.41 ();
  3         58  
  3         61  
51 3     3   1110 use LWP::Online 0.03 ();
  3         6555  
  3         71  
52 3     3   21 use Params::Util 1.00 ();
  3         60  
  3         81  
53 3     3   18 use Scalar::Util 1.11 ();
  3         67  
  3         58  
54 3     3   2118 use URI::ToDisk 1.08 ();
  3         21555  
  3         291  
55 3     3   7559 use DBI 1.607 ();
  3         48877  
  3         160  
56 3     3   4629 use DBD::SQLite 1.25 ();
  3         68731  
  3         3649  
57 3     3   1186 use JSAN::Index ();
  3         10  
  3         3219  
58              
59             our $VERSION = '0.29';
60              
61             # Pass through any import params to JSAN::Index
62             # if none params were provided - do nothing
63             sub import {
64 3     3   44 my $class = shift;
65 3   100     26 my $params = Params::Util::_HASH(shift) || {};
66            
67 3 100       18 if (keys(%$params) > 0) {
68             # Prevent double-initialisation
69 2 50       11 Carp::croak("Attempt to re-initialize JSAN::Index via JSAN::Client's import method")
70             if JSAN::Index->self;
71            
72 2         10 JSAN::Index->init( $params );
73             }
74              
75 3         8510 return 1;
76             }
77              
78              
79              
80              
81              
82             #####################################################################
83             # Constructor and Accessors
84              
85             =pod
86              
87             =head2 new param => 'value', ...
88              
89             The C constructor takes a set of key/value params and creates a new
90             JSAN installation client, who's purpose is to install JSAN distributions
91             to a specific location on the local disk.
92              
93             Please note that although you can create multiple C objects
94             for multiple install paths, all clients will share the common L
95             and L layers, which means both that distribution packages
96             will be cached across all the clients, and that they B install from
97             the same remote JSAN repository.
98              
99             The constructor takes the following parameters
100              
101             =over 4
102              
103             =item prefix
104              
105             The C params should be the location on the local disk that the JSAN
106             libraries contained in the distribution should be installed to.
107              
108             =item verbose
109              
110             The C (current not implemented) enables verbose mode for the client.
111              
112             In verbose mode, the client will write a number of procedural and diagnostic
113             messages to C as it processes the installation requests.
114              
115             =back
116              
117             Returns a new C object, or dies on error.
118              
119             =cut
120              
121             sub new {
122 3 50   3 1 3060 my $class = ref $_[0] ? ref shift : shift;
123 3 100       29 if ( scalar(@_) % 2 ) {
124 1         219 Carp::croak("Odd number of params passed to JSAN::Client::new");
125             }
126 2         13 my %params = @_;
127              
128             # Create the basic object
129 2         15 my $self = bless {
130             prefix => $params{prefix},
131             build => !! $params{build},
132             verbose => !! $params{verbose},
133             }, $class;
134              
135             # Check the prefix
136 2 50       10 unless ( $self->prefix ) {
137 0         0 Carp::croak("No prefix provided to JSAN::Client::new");
138             }
139 2 50 33     8 unless ( -d $self->prefix and -w $self->prefix ) {
140 0         0 Carp::croak("Prefix provided to JSAN::Client::new is not a writable directory");
141             }
142            
143 2 50       21 unless (JSAN::Index->self) {
144 0         0 Carp::croak("Cannot instantiate JSAN::Client until JSAN::Index is not initialized");
145             };
146              
147 2         8 $self;
148             }
149              
150             =pod
151              
152             =head2 prefix
153              
154             The C accessor returns the installation prefix path for the client.
155              
156             =cut
157              
158 24     24 1 682 sub prefix { $_[0]->{prefix} }
159              
160             =pod
161              
162             =head2 verbose
163              
164             The C accessor returns the boolean flag indicating whether the
165             client is running in verbose mode. If called with argument - modifies the
166             current 'verbose' value.
167              
168             =cut
169              
170             sub verbose {
171 24     24 1 47 my $self = shift;
172            
173 24 50       202 return $self->{verbose} unless @_;
174            
175 0         0 $self->{verbose} = shift;
176             }
177              
178             =pod
179              
180             =head2 build
181              
182             The C accessor returns the boolean flag indicating whether the
183             client is running in build-time mode.
184              
185             =cut
186              
187 3     3 1 27 sub build { $_[0]->{build} }
188              
189              
190              
191              
192              
193             #####################################################################
194             # JSAN::Client Methods
195              
196             =pod
197              
198             =head2 install_library $name
199              
200             The C method takes the name of a JSAN library and
201             installs the most recent release of the distribution that the library is
202             contained in as indicated in the JSAN index.
203              
204             Any dependencies required for the library will also be installed as needed.
205              
206             Please note the difference between a JSAN "library" and a JSAN
207             "distribution". There are often many libraries contained in a single
208             distribution.
209              
210             Returns true if the library was installed, false if the library is already
211             up to date and did not need to be installed, or dies on error.
212              
213             =cut
214              
215             sub install_library {
216 2     2 1 385545 my $self = shift;
217              
218             # Take the library as an object or a name
219 2         6 my $library = shift;
220 2 50       19 unless ( Params::Util::_INSTANCE($library, 'JSAN::Index::Library') ) {
221 2 50       35 $library = JSAN::Index::Library->retrieve( name => $library )
222             or Carp::croak("The JSAN library '$library' does not exist");
223             }
224              
225 2         15 $self->_install_release( $library->release, $_[0] );
226             }
227              
228             =pod
229              
230             =head2 install_distribution $name
231              
232             The C method takes the name of a JSAN distribution and
233             installs the most recent release of that distribution.
234              
235             Any dependencies required for the distribution will also be installed as
236             needed.
237              
238             Please note the difference between a JSAN "library" and a JSAN
239             "distribution". There are often many libraries contained in a single
240             distribution.
241              
242             Returns true if the distribution was installed, false if the distribution
243             is already up to date and did not need to be installed, or dies on error.
244              
245             =cut
246              
247             sub install_distribution {
248 1     1 1 15231 my $self = shift;
249              
250             # Take the distribution as an object or a name
251 1         20 my $distribution = shift;
252 1 50       40 unless ( Params::Util::_INSTANCE($distribution, 'JSAN::Index::Distribution') ) {
253 1 50       53 $distribution = JSAN::Index::Distribution->retrieve( name => $distribution )
254             or Carp::croak("The JSAN distribution '$distribution' does not exist");
255             }
256              
257 1         25 $self->_install_release( $distribution->latest_release, $_[0] );
258             }
259              
260             # Takes a JSAN::Index::Release object, and installs it
261             sub _install_release {
262 3     3   14 my ($self, $requested, $name ) = @_;
263              
264             # Find the full schedule
265 3         18 $self->_print("Scanning index for dependencies...");
266 3         16 my $dependency = JSAN::Index->dependency( build => $self->build );
267 3 50       19 my $schedule = $dependency->schedule( $requested )
268             or Carp::croak("Error while finding dependencies for '$name'");
269 7 50       44 my @releases = map {
270 3         20502 JSAN::Index::Release->retrieve( source => $_ )
271             or Carp::croak("Failed to get an object for '$_'")
272             } @$schedule;
273              
274             # Following debian's lead, download all the releases first.
275             # That way if there's a download error we won't be left half-installed.
276 3         20 my $total = scalar(@releases);
277 3         10 my $count = 0;
278 3         20 $self->_print("Fetching releases from JSAN...");
279 3         14 foreach my $release ( @releases ) {
280 7         63 $count++;
281 7         50 $self->_print("$count of $total: Mirroring release " . $release->source);
282 7         39 $release->mirror;
283             }
284              
285             # Install each of the releases
286 3         150 $count = 0;
287 3         23 $self->_print("Installing release to '" . $self->prefix . "'");
288 3         74 foreach my $release ( @releases ) {
289 7         19 $count++;
290 7         47 $self->_print("$count of $total: Extracting release " . $release->source);
291 7         25 $self->_extract_release( $release );
292             }
293              
294 2         1431 1;
295             }
296              
297             # Takes a single JSAN::Index::Release object, and extracts its libs to the prefix dir
298             sub _extract_release {
299 7     7   20 my ($self, $release) = @_;
300 7         26 $release->extract_libs( to => $self->prefix );
301 7         47 $release->extract_static_files( to => $self->prefix );
302             }
303              
304              
305              
306              
307              
308             #####################################################################
309             # Support Methods
310              
311             # Print to screen if in verbose mode
312             sub _print {
313 23     23   48 my $self = shift;
314 23 50       92 return 1 unless $self->verbose;
315 0           while ( @_ ) {
316 0           my $line = shift;
317 0           chomp($line);
318 0           print STDOUT "$line\n";
319             }
320 0           1;
321             }
322              
323             1;
324              
325             =pod
326              
327             =head1 TO DO
328              
329             - Add the testing dependency algorithm variant
330              
331             - Add support for JSON META.yml files
332              
333             =head1 SUPPORT
334              
335             Bugs should be reported via the CPAN bug tracker at
336              
337             L
338              
339             For other issues, contact the author.
340              
341             =head1 AUTHOR
342              
343             Adam Kennedy Eadamk@cpan.orgE
344              
345             =head1 COPYRIGHT
346              
347             Copyright 2005 - 2010 Adam Kennedy.
348              
349             This program is free software; you can redistribute
350             it and/or modify it under the same terms as Perl itself.
351              
352             The full text of the license can be found in the
353             LICENSE file included with this module.
354              
355             =cut