File Coverage

blib/lib/Dist/Zilla/Plugin/Prereqs/DarkPAN.pm
Criterion Covered Total %
statement 56 79 70.8
branch 6 22 27.2
condition n/a
subroutine 9 11 81.8
pod 1 2 50.0
total 72 114 63.1


line stmt bran cond sub pod time code
1 2     2   2019681 use 5.006; # our
  2         6  
2 2     2   7 use strict;
  2         2  
  2         43  
3 2     2   8 use warnings;
  2         2  
  2         140  
4              
5             package Dist::Zilla::Plugin::Prereqs::DarkPAN;
6              
7             our $VERSION = 'v0.3.0';
8              
9             # ABSTRACT: Depend on things from arbitrary places-not-CPAN
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 2     2   416 use Moose qw( with has around );
  2         283124  
  2         15  
14             with 'Dist::Zilla::Role::PrereqSource::External';
15              
16 2     2   8272 use namespace::autoclean;
  2         5758  
  2         10  
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31             has prereq_phase => (
32             is => 'ro',
33             isa => 'Str',
34             lazy => 1,
35             init_arg => undef,
36             default => 'runtime',
37             );
38              
39             has prereq_type => (
40             is => 'ro',
41             isa => 'Str',
42             lazy => 1,
43             init_arg => undef,
44             default => 'requires',
45             );
46              
47             # For full phase control, use above commented code.
48              
49             has _deps => ( is => 'ro', isa => 'HashRef', default => sub { {} }, );
50             has _raw_deps => ( is => 'ro', isa => 'HashRef', default => sub { {} }, );
51              
52             around 'dump_config' => sub {
53             my ( $orig, $self, @args ) = @_;
54             my $config = $self->$orig(@args);
55             my $localconf = $config->{ +__PACKAGE__ } = {};
56              
57             $localconf->{prereq_phase} = $self->prereq_phase;
58             $localconf->{prereq_type} = $self->prereq_type;
59             $localconf->{_raw_deps} = $self->_raw_deps;
60              
61             $localconf->{ q[$] . __PACKAGE__ . q[::VERSION] } = $VERSION
62             unless __PACKAGE__ eq ref $self;
63             return $config;
64             };
65              
66             __PACKAGE__->meta->make_immutable;
67 2     2   425 no Moose;
  2         2  
  2         8  
68              
69             sub _add_dep {
70 1     1   4 my ( undef, $stash, $args ) = @_;
71 1 50       3 $stash->{deps} = {} unless exists $stash->{deps};
72 1         2 my $ds = $stash->{deps};
73 1         1 my $logger = $stash->{logger};
74              
75 1         1 my $key = $args->{key};
76 1         2 my $value = $args->{value};
77              
78             # TODO perhaps have support for multiple URLs with either some
79             # fallback strategy or round-robbin or random-source support.
80             # Not a priority atm.
81             return $logger->log_fatal( [ 'tried to define base uri for \'%s\' more than once.', $key ] )
82 1 50       3 if exists $ds->{$key};
83              
84 1         4 return ( $ds->{$key} = $value );
85              
86             }
87              
88             sub _add_attribute {
89 0     0   0 my ( undef, $stash, $args ) = @_;
90              
91 0 0       0 $stash->{attributes} = {} unless exists $stash->{attributes};
92              
93 0         0 my $attributes = $stash->{attributes};
94 0         0 my $logger = $stash->{logger};
95              
96 0         0 my $key = $args->{key};
97 0         0 my $attribute = $args->{attribute};
98 0         0 my $value = $args->{value};
99              
100 0         0 my $supported_attrs = { map { $_ => 1 } qw( minversion uri ) };
  0         0  
101              
102             return $logger->log_fatal( [ 'Attribute \'%s\' for key \'%s\' not supported.', $attribute, $key, ], )
103 0 0       0 if not exists $supported_attrs->{$attribute};
104              
105 0 0       0 $attributes->{$key} = {} unless exists $attributes->{$key};
106              
107             return $logger->log_fatal( [ 'tried to set attribute \'%s\' for %s more than once.', $attribute, $key, ] )
108 0 0       0 if exists $attributes->{$key}->{$attribute};
109              
110 0         0 return ( $attributes->{$key}->{$attribute} = $value );
111              
112             }
113              
114             sub _collect_data {
115 1     1   3 my ( $class, $stash, $key, $value ) = @_;
116              
117 1         3 my $logger = $stash->{logger};
118              
119             # Parameters
120             # -phase
121             # -type
122             # as supported by Prereqs are not supported here ( at least, not yet )
123 1 50       11 return $logger->log_fatal('dash ( - ) prefixed parameters are presently not supported.')
124             if $key =~ /\A-/msx;
125              
126 1 50       5 if ( $key =~ /\A([^.]+)[.](.*\z)/msx ) {
127              
128             # Foo::Bar.minversion
129 0         0 my $key_name = "$1";
130 0         0 my $key_attribute = "$2";
131 0         0 return $class->_add_attribute(
132             $stash,
133             {
134             key => $key_name,
135             attribute => $key_attribute,
136             value => $value,
137             },
138             );
139             }
140              
141 1         6 return $class->_add_dep( $stash, { key => $key, value => $value } );
142             }
143              
144             sub BUILDARGS {
145 1     1 1 3 my ( $class, @args ) = @_;
146 1         2 my %config;
147 1 50       4 if ( ref $args[0] ) {
148 1         2 %config = %{ $args[0] };
  1         4  
149 1         2 shift @args;
150             }
151             else {
152 0         0 %config = @args;
153             }
154              
155 1         2 my $zilla = delete $config{zilla};
156 1         2 my $name = delete $config{plugin_name};
157 1         2 my $_deps = {};
158              
159 1         22 my $zilla_logger = $zilla->chrome->logger;
160 1         35 my $logger = $zilla_logger->proxy( { proxy_prefix => '[' . $name . ']', } );
161              
162 1         28 my $deps = {};
163 1         2 my $attributes = {};
164              
165 1         5 for my $key ( keys %config ) {
166 1         8 $class->_collect_data( { logger => $logger, deps => $deps, attributes => $attributes, }, $key, $config{$key} );
167             }
168 1         1 for my $dep ( keys %{$attributes} ) {
  1         3  
169             $logger->log_fatal( [ '[%s] Attributes specified for dependency \'%s\', which is not defined', $name, $dep ] )
170 0 0       0 unless exists $deps->{$dep};
171             }
172 1         2 for my $dep ( keys %{$deps} ) {
  1         2  
173 1         402 require Dist::Zilla::ExternalPrereq;
174 1         7 my $edep = $attributes->{$dep};
175 1 50       7 $edep = {} unless defined $edep;
176              
177             my $instance = Dist::Zilla::ExternalPrereq->new(
178             name => $dep,
179             plugin_name => $name . '{ExternalPrereq: dep on=\'' . $dep . '\'}',
180             zilla => $zilla,
181             baseurl => $deps->{$dep},
182              
183 1         8 %{$edep},
  1         43  
184             );
185 1         4 $_deps->{$dep} = $instance;
186             }
187             return {
188 1         41 zilla => $zilla,
189             plugin_name => $name,
190             _deps => $_deps,
191             _raw_deps => $deps,
192             logger => $logger,
193             };
194              
195             }
196              
197              
198              
199              
200              
201             sub register_external_prereqs {
202 0     0 0   my ( $self, $registersub ) = @_;
203              
204 0           for my $dep ( keys %{ $self->_deps } ) {
  0            
205             $registersub->(
206             {
207             type => $self->prereq_type,
208             phase => $self->prereq_phase,
209             },
210 0           $self->_deps->{$dep},
211             );
212             }
213 0           return;
214             }
215              
216             1;
217              
218             __END__
219              
220             =pod
221              
222             =encoding UTF-8
223              
224             =head1 NAME
225              
226             Dist::Zilla::Plugin::Prereqs::DarkPAN - Depend on things from arbitrary places-not-CPAN
227              
228             =head1 VERSION
229              
230             version v0.3.0
231              
232             =head1 SYNOPSIS
233              
234             From time to time, people find themselves in want to depending on something that
235             isn't from CPAN, but their team/in-house crew want a painless way to depend on
236             it anyway.
237              
238             [Prereqs::DarkPAN]
239             DDG = http://adarkpan.example.org/ ; DarkPAN Base URI
240             ; optional
241             DDG.minversion = 0.4.0
242             ; optional
243             ; But likely to be substantially faster.
244             DDG.uri = /path/to/foo/bar.tar.gz
245              
246             This would provide to various user commands the knowledge that C<DDG.tar.gz> was
247             wanted to provide the package C<DDG>.
248              
249             Our hope is one day you can just do
250              
251             # Doesn't work yet :(
252             $ cpanm $( dzil listdeps )
253              
254             or
255             # Doesn't work yet :(
256             $ cpanm $( dzil listdeps --missing )
257              
258             and have it do the right things.
259              
260             In the interim, you can do
261              
262             $ cpanm $( dzil listdeps ) \
263             && cpanm $( dzil listdeps_darkpan )
264              
265             or
266              
267             $ cpanm $( dzil listdeps --missing ) \
268             && cpanm $( dzil listdeps_darkpan --missing )
269              
270             and have it work.
271              
272             =begin MetaPOD::JSON v1.1.0
273              
274             {
275             "namespace":"Dist::Zilla::Plugin::Prereqs::DarkPAN",
276             "interface":"class",
277             "inherits":"Moose::Object",
278             "does":"Dist::Zilla::Role::PrereqSource::External"
279             }
280              
281              
282             =end MetaPOD::JSON
283              
284             =for Pod::Coverage register_external_prereqs
285              
286             =head1 DarkPAN Configurations.
287              
288             =head2 A Simple HTTP Server
289              
290             The easiest DarkPAN-ish thing that this module supports is naïve HTTP Servers,
291             by simply setting the server and path to the resource.
292              
293             [Prereqs::DarkPAN]
294             Foo = http://my.server/
295             Foo.uri = files/foo.tar.gz
296              
297             You can specify an optional minimum version parameter C<minversion> as a client-side check to
298             make sure they haven't installed an older version of Foo.
299              
300             This C<uri> will be reported to listdeps_darkpan with minimal modification, only
301             expanding relative paths to absolute ones so tools like C<cpanm> can use them.
302              
303             =head2 A C<MicroCPAN> Configuration
304              
305             There is a newly formed system for creating "proper" CPANs which only contain a
306             handful of modules. For these services you can simply do
307              
308             [Prereqs::DarkPAN]
309             Foo = http://my.server/
310              
311             And we'll fire up all sorts of magic to get the C<02packages.details.tar.gz>
312             file, shred it, and try installing 'Foo' from there.
313              
314             =head2 Heavier CPAN configurations
315              
316             The 3rd use case is when you have somewhat heavy-weight private CPANs where you
317             don't want to be encumbered by the weight of downloading and parsing
318             C<02packages.details.tar.gz>. If you have a full CPAN clone with a few modules
319             stuffed into it, and you only want those stuffed modules while using normal CPAN
320             ( because the cloned versions from CPAN are now old ), its possibly better to
321             use the original notation
322              
323             [Prereqs::DarkPAN]
324             Foo = http://my.server/
325             Foo.uri = path/too/foo.tar.gz
326              
327             As it will only fetch the file specified instead of relying on
328             C<02packages.details.tar.gz>
329              
330             Granted, this latter approach will bind again to downloading a specific version
331             of the prerequisite, but this is still here for you if you need it.
332              
333             =head1 AUTHOR
334              
335             Kent Fredric <kentnl@cpan.org>
336              
337             =head1 COPYRIGHT AND LICENSE
338              
339             This software is copyright (c) 2017 by Kent Fredric <kentnl@cpan.org>.
340              
341             This is free software; you can redistribute it and/or modify it under
342             the same terms as the Perl 5 programming language system itself.
343              
344             =cut