File Coverage

blib/lib/Pinto/Schema/Result/Distribution.pm
Criterion Covered Total %
statement 153 199 76.8
branch 32 58 55.1
condition 16 34 47.0
subroutine 36 49 73.4
pod 0 18 0.0
total 237 358 66.2


line stmt bran cond sub pod time code
1 54     54   56091 use utf8;
  54         130  
  54         468  
2              
3             package Pinto::Schema::Result::Distribution;
4              
5             # Created by DBIx::Class::Schema::Loader
6             # DO NOT MODIFY THE FIRST PART OF THIS FILE
7              
8              
9 54     54   2365 use strict;
  54         125  
  54         1245  
10 54     54   287 use warnings;
  54         1695  
  54         2694  
11              
12 54     54   352 use Moose;
  54         111  
  54         424  
13 54     54   347679 use MooseX::NonMoose;
  54         1918  
  54         496  
14 54     54   369451 use MooseX::MarkAsMethods autoclean => 1;
  54         148  
  54         539  
15             extends 'DBIx::Class::Core';
16              
17              
18             __PACKAGE__->table("distribution");
19              
20              
21             __PACKAGE__->add_columns(
22             "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
23             "author", { data_type => "text", is_nullable => 0 },
24             "archive", { data_type => "text", is_nullable => 0 },
25             "source", { data_type => "text", is_nullable => 0 },
26             "mtime", { data_type => "integer", is_nullable => 0 },
27             "sha256", { data_type => "text", is_nullable => 0 },
28             "md5", { data_type => "text", is_nullable => 0 },
29             "metadata", { data_type => "text", is_nullable => 0 },
30             );
31              
32              
33             __PACKAGE__->set_primary_key("id");
34              
35              
36             __PACKAGE__->add_unique_constraint( "author_archive_unique", [ "author", "archive" ] );
37              
38              
39             __PACKAGE__->has_many(
40             "packages",
41             "Pinto::Schema::Result::Package",
42             { "foreign.distribution" => "self.id" },
43             { cascade_copy => 0, cascade_delete => 0 },
44             );
45              
46              
47             __PACKAGE__->has_many(
48             "prerequisites",
49             "Pinto::Schema::Result::Prerequisite",
50             { "foreign.distribution" => "self.id" },
51             { cascade_copy => 0, cascade_delete => 0 },
52             );
53              
54              
55             __PACKAGE__->has_many(
56             "registrations",
57             "Pinto::Schema::Result::Registration",
58             { "foreign.distribution" => "self.id" },
59             { cascade_copy => 0, cascade_delete => 0 },
60             );
61              
62              
63             with 'Pinto::Role::Schema::Result';
64              
65             # Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-03-26 11:05:47
66             # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vQKIXXk8xddyMmBptwvpUg
67              
68             #-------------------------------------------------------------------------------
69              
70             # ABSTRACT: Represents a distribution archive
71              
72             #-------------------------------------------------------------------------------
73              
74 54     54   294747 use URI;
  54         156  
  54         1254  
75 54     54   25628 use CPAN::Meta;
  54         960252  
  54         2049  
76 54     54   614 use Path::Class;
  54         139  
  54         4349  
77 54     54   19041 use CPAN::DistnameInfo;
  54         39425  
  54         1653  
78 54     54   2702 use String::Format;
  54         5660  
  54         2781  
79              
80 54     54   515 use Pinto::Util qw(itis debug whine throw);
  54         112  
  54         3311  
81 54     54   15778 use Pinto::Target::Distribution;
  54         259  
  54         2744  
82              
83             use overload (
84 54         459 '""' => 'to_string',
85             'cmp' => 'string_compare'
86 54     54   637 );
  54         115  
87              
88             #------------------------------------------------------------------------------
89              
90             our $VERSION = '0.14'; # VERSION
91              
92             #------------------------------------------------------------------------------
93              
94             __PACKAGE__->inflate_column(
95             'metadata' => {
96             inflate => sub { CPAN::Meta->load_json_string( $_[0] ) },
97             deflate => sub { $_[0]->as_string( { version => "2" } ) }
98             }
99             );
100              
101             #------------------------------------------------------------------------------
102              
103             sub FOREIGNBUILDARGS {
104 185     185 0 691 my ( $class, $args ) = @_;
105              
106 185   50     704 $args ||= {};
107 185   100     1235 $args->{source} ||= 'LOCAL';
108              
109 185         3224 return $args;
110             }
111              
112             #------------------------------------------------------------------------------
113              
114             sub register {
115 185     185 0 1412 my ( $self, %args ) = @_;
116              
117 185         633 my $stack = $args{stack};
118 185   50     1334 my $force = $args{force} || 0;
119 185   100     1129 my $pin = $args{pin} || 0;
120              
121 185         5928 my $can_intermingle = $stack->repo->config->intermingle;
122 185         570 my $did_register = 0;
123              
124 185         1588 $stack->assert_is_open;
125              
126             # TODO: This process makes a of trips to the database. You could
127             # optimize this by fetching all the incumbents at once, checking
128             # for pins, and then bulk-insert the new registrations.
129              
130 185         4498 for my $pkg ($self->packages) {
131              
132 218 50       499046 if (not $pkg->can_index) {
133 0   0     0 my $file = $pkg->file || '';
134 0     0   0 debug( sub {"Package $pkg in file $file is not indexable. Skipping registration"} );
  0         0  
135 0         0 next;
136             }
137              
138 218         4487 my $where = {package_name => $pkg->name};
139 218         7649 my $incumbent = $stack->head->find_related(registrations => $where);
140              
141 218 100       1448625 if (not defined $incumbent) {
    100          
142 179     0   5652 debug( sub {"Registering package $pkg on stack $stack"} );
  0         0  
143 179         2055 $pkg->register(stack => $stack, pin => $pin);
144 179         516 $did_register++;
145 179         792 next;
146             }
147             elsif (not $can_intermingle) {
148             # If the repository prohibits intermingled distributions, we can
149             # assume all the apckages in the incumbent are already registered.
150 38         1983 my $dist = $incumbent->distribution;
151 38 100 100     269843 if ($dist->id == $self->id and $incumbent->is_pinned == $pin) {
152 13     0   1128 debug( sub {"Distribution $dist is already fully registered"} );
  0         0  
153 13         114 last;
154             }
155             }
156              
157              
158 26         1940 my $incumbent_pkg = $incumbent->package;
159              
160 26 100       190899 if ( $incumbent_pkg == $pkg ) {
161 4     0   207 debug( sub {"Package $pkg is already on stack $stack"} );
  0         0  
162 4 50 33     103 $incumbent->pin && $did_register++ if $pin and not $incumbent->is_pinned;
      33        
163 4         183 next;
164             }
165              
166              
167 22 100       639 if ( $incumbent->is_pinned ) {
168 2         92 my $pkg_name = $incumbent_pkg->name;
169 2         80 my $dist = $incumbent->distribution;
170 2 50       86 $force ? whine "Forcibly changing $dist to $self"
171             : throw "Unable to register distribution $self: $pkg_name is pinned to $incumbent_pkg";
172             }
173              
174 20 100       432 whine "Downgrading package $incumbent_pkg to $pkg on stack $stack"
175             if $incumbent_pkg > $pkg;
176              
177 20 100       123 if ( $can_intermingle ) {
178             # If the repository allows intermingled distributions, then
179             # remove only the incumbent package from the index.
180 1         43 $incumbent->delete;
181             }
182             else {
183             # Otherwise, remove all packages in the incumbent
184             # distribution from the index. This is the default.
185 19         492 $incumbent->distribution->unregister(stack => $stack, force => $force);
186             }
187              
188 20         2238 $pkg->register(stack => $stack, pin => $pin);
189 20         107 $did_register++;
190             }
191              
192 183 100       11232 $stack->mark_as_changed if $did_register;
193              
194 183         2763 return $did_register;
195             }
196              
197             #------------------------------------------------------------------------------
198              
199             sub unregister {
200 21     21 0 753 my ( $self, %args ) = @_;
201              
202 21         79 my $stack = $args{stack};
203 21         72 my $force = $args{force};
204 21         68 my $did_unregister = 0;
205 21         65 my $conflicts = 0;
206              
207 21         159 $stack->assert_is_open;
208 21         239 $stack->assert_not_locked;
209              
210 21         496 my $rs = $self->registrations( { revision => $stack->head->id } );
211 21         49194 for my $reg ( $rs->all ) {
212              
213 27 50 33     49194 if ( $reg->is_pinned and not $force ) {
214 0         0 my $pkg = $reg->package;
215 0         0 whine "Cannot unregister package $pkg because it is pinned to stack $stack";
216 0         0 $conflicts++;
217 0         0 next;
218             }
219              
220 27         630 $did_unregister++;
221             }
222              
223 21 50       224 throw "Unable to unregister distribution $self from stack $stack" if $conflicts;
224              
225 21         5079 $rs->delete;
226              
227 21 50       32660 $stack->mark_as_changed if $did_unregister;
228              
229 21         124 return $did_unregister;
230             }
231              
232             #------------------------------------------------------------------------------
233              
234             sub pin {
235 7     7 0 45 my ( $self, %args ) = @_;
236              
237 7         23 my $stack = $args{stack};
238 7         61 $stack->assert_not_locked;
239              
240 7         146 my $rev = $stack->head;
241 7         266 $rev->assert_is_open;
242              
243 7         161 my $where = { revision => $rev->id, is_pinned => 0 };
244 7         207 my $regs = $self->registrations($where);
245              
246 7 50       12656 return 0 if not $regs->count;
247              
248 7         25155 $regs->update( { is_pinned => 1 } );
249 7         8465 $stack->mark_as_changed;
250              
251 7         45 return 1;
252             }
253              
254             #------------------------------------------------------------------------------
255              
256             sub unpin {
257 3     3 0 21 my ( $self, %args ) = @_;
258              
259 3         9 my $stack = $args{stack};
260 3         350 $stack->assert_not_locked;
261              
262 3         57 my $rev = $stack->head;
263 3         101 $rev->assert_is_open;
264              
265 3         70 my $where = { revision => $rev->id, is_pinned => 1 };
266 3         98 my $regs = $self->registrations($where);
267              
268 3 50       4415 return 0 if not $regs->count;
269              
270 3         9962 $regs->update( { is_pinned => 0 } );
271 3         3281 $stack->mark_as_changed;
272              
273 3         15 return 1;
274             }
275              
276             #------------------------------------------------------------------------------
277              
278             has distname_info => (
279             isa => 'CPAN::DistnameInfo',
280             init_arg => undef,
281             handles => {
282             name => 'dist',
283             vname => 'distvname',
284             version => 'version',
285             maturity => 'maturity'
286             },
287             default => sub { CPAN::DistnameInfo->new( $_[0]->path ) },
288             lazy => 1,
289             );
290              
291             #------------------------------------------------------------------------------
292              
293             has is_devel => (
294             is => 'ro',
295             isa => 'Bool',
296             init_arg => undef,
297             default => sub { $_[0]->maturity() eq 'developer' },
298             lazy => 1,
299             );
300              
301             #------------------------------------------------------------------------------
302              
303             sub path {
304 387     387 0 199066 my ($self) = @_;
305              
306 387         8427 return join '/', ( substr( $self->author, 0, 1 ), substr( $self->author, 0, 2 ), $self->author, $self->archive );
307             }
308              
309             #------------------------------------------------------------------------------
310              
311             sub native_path {
312 369     369 0 74060 my ( $self, @base ) = @_;
313              
314 369 100       10082 @base = ( $self->repo->config->authors_id_dir ) if not @base;
315              
316 369         8336 return Path::Class::file(
317             @base,
318             substr( $self->author, 0, 1 ),
319             substr( $self->author, 0, 2 ),
320             $self->author, $self->archive
321             );
322             }
323              
324             #------------------------------------------------------------------------------
325              
326             sub uri {
327 2     2 0 52 my ( $self, $base ) = @_;
328              
329             # TODO: Is there a sensible URI for local dists?
330 2 100       11 return 'UNKNOWN' if $self->is_local;
331              
332 1   33     58 $base ||= $self->source;
333              
334 1         26 return URI->new( "$base/authors/id/" . $self->path )->canonical;
335             }
336              
337             #------------------------------------------------------------------------------
338              
339             sub is_local {
340 41     41 0 6483 my ($self) = @_;
341              
342 41         747 return $self->source eq 'LOCAL';
343             }
344              
345             #------------------------------------------------------------------------------
346              
347             sub package {
348 0     0 0 0 my ( $self, %args ) = @_;
349              
350 0         0 my $pkg_name = $args{name};
351              
352 0         0 my $where = { name => $pkg_name };
353 0         0 my $attrs = { key => 'name_distribution_unique' };
354 0 0       0 my $pkg = $self->find_related( 'packages', $where, $attrs ) or return;
355              
356 0 0       0 if ( my $stk_name = $args{stack} ) {
357 0 0       0 return $pkg->registration( stack => $stk_name ) ? $pkg : ();
358             }
359              
360 0         0 return $pkg;
361             }
362              
363             #------------------------------------------------------------------------------
364              
365             sub registered_stacks {
366 0     0 0 0 my ($self) = @_;
367              
368 0         0 my %stacks;
369              
370 0         0 for my $reg ( $self->registrations ) {
371              
372             # TODO: maybe use 'DISTICT'
373 0         0 $stacks{ $reg->stack } = $reg->stack;
374             }
375              
376 0         0 return values %stacks;
377             }
378              
379             #------------------------------------------------------------------------------
380              
381             sub main_module {
382 0     0 0 0 my ($self) = @_;
383              
384             # We start by sorting packages by the length of their name. Most of
385             # the time, the shorter one is more likely to be the main module name.
386 0         0 my @pkgs = sort { length $a->name <=> length $b->name } $self->packages;
  0         0  
387              
388             # Transform the dist name into a package name
389 0         0 my $dist_name = $self->name;
390 0         0 $dist_name =~ s/-/::/g;
391              
392             # First, look for an indexable package that matches the dist name
393 0         0 for my $pkg (@pkgs) {
394 0 0 0     0 return $pkg if $pkg->can_index && $pkg->name eq $dist_name;
395             }
396              
397             # Then, look for any indexable package
398 0         0 for my $pkg (@pkgs) {
399 0 0       0 return $pkg if $pkg->can_index;
400             }
401              
402             # Then, just use the first package
403 0 0       0 return $pkgs[0] if @pkgs;
404              
405             # There are no packages
406 0         0 return undef;
407             }
408              
409             #------------------------------------------------------------------------------
410              
411             sub package_count {
412 0     0 0 0 my ($self) = @_;
413              
414 0         0 return scalar $self->packages;
415             }
416              
417             #------------------------------------------------------------------------------
418              
419             sub prerequisite_specs {
420 0     0 0 0 my ($self) = @_;
421              
422 0         0 return map { $_->as_target } $self->prerequisites;
  0         0  
423             }
424              
425             #------------------------------------------------------------------------------
426              
427             sub as_target {
428 0     0 0 0 my ($self) = @_;
429              
430 0         0 return Pinto::Target::Distribution->new( path => $self->path );
431             }
432              
433             #------------------------------------------------------------------------------
434              
435             sub string_compare {
436 2     2 0 9 my ( $dist_a, $dist_b ) = @_;
437              
438 2         13 my $pkg = __PACKAGE__;
439 2 50 33     22 throw "Can only compare $pkg objects"
440             if not( itis( $dist_a, $pkg ) && itis( $dist_b, $pkg ) );
441              
442 2 50       46 return 0 if $dist_a->id == $dist_b->id;
443              
444 2         116 my $r = ( $dist_a->archive cmp $dist_b->archive );
445              
446 2         88 return $r;
447             }
448              
449             #------------------------------------------------------------------------------
450              
451             sub to_string {
452 1048     1048 0 102789 my ( $self, $format ) = @_;
453              
454             my %fspec = (
455 1     1   49 'd' => sub { $self->name },
456 3     3   122 'D' => sub { $self->vname },
457 1     1   46 'V' => sub { $self->version },
458 1 50   1   72 'm' => sub { $self->is_devel ? 'd' : 'r' },
459 0 0   0   0 'M' => sub { my $m = $self->main_module; $m ? $m->name : '' },
  0         0  
460 1     1   51 'h' => sub { $self->path },
461 0     0   0 'H' => sub { $self->native_path },
462 1037     1037   62945 'f' => sub { $self->archive },
463 1 50   1   46 's' => sub { $self->is_local ? 'l' : 'f' },
464 1     1   71 'S' => sub { $self->source },
465 1038     1038   69369 'a' => sub { $self->author },
466 1     1   47 'u' => sub { $self->uri },
467 0     0   0 'c' => sub { $self->package_count },
468 1048         25942 );
469              
470 1048   66     6609 $format ||= $self->default_format;
471 1048         6142 return String::Format::stringf( $format, %fspec );
472             }
473              
474             #-------------------------------------------------------------------------------
475              
476             sub default_format {
477 1037     1037 0 2496 my ($self) = @_;
478              
479 1037         4155 return '%a/%f', # AUTHOR/Dist-Name-1.0.tar.gz
480             }
481              
482             #------------------------------------------------------------------------------
483              
484             __PACKAGE__->meta->make_immutable;
485              
486             #------------------------------------------------------------------------------
487             1;
488              
489             __END__
490              
491             =pod
492              
493             =encoding UTF-8
494              
495             =for :stopwords Jeffrey Ryan Thalhammer
496              
497             =head1 NAME
498              
499             Pinto::Schema::Result::Distribution - Represents a distribution archive
500              
501             =head1 VERSION
502              
503             version 0.14
504              
505             =head1 NAME
506              
507             Pinto::Schema::Result::Distribution
508              
509             =head1 TABLE: C<distribution>
510              
511             =head1 ACCESSORS
512              
513             =head2 id
514              
515             data_type: 'integer'
516             is_auto_increment: 1
517             is_nullable: 0
518              
519             =head2 author
520              
521             data_type: 'text'
522             is_nullable: 0
523              
524             =head2 archive
525              
526             data_type: 'text'
527             is_nullable: 0
528              
529             =head2 source
530              
531             data_type: 'text'
532             is_nullable: 0
533              
534             =head2 mtime
535              
536             data_type: 'integer'
537             is_nullable: 0
538              
539             =head2 sha256
540              
541             data_type: 'text'
542             is_nullable: 0
543              
544             =head2 md5
545              
546             data_type: 'text'
547             is_nullable: 0
548              
549             =head2 metadata
550              
551             data_type: 'text'
552             is_nullable: 0
553              
554             =head1 PRIMARY KEY
555              
556             =over 4
557              
558             =item * L</id>
559              
560             =back
561              
562             =head1 UNIQUE CONSTRAINTS
563              
564             =head2 C<author_archive_unique>
565              
566             =over 4
567              
568             =item * L</author>
569              
570             =item * L</archive>
571              
572             =back
573              
574             =head1 RELATIONS
575              
576             =head2 packages
577              
578             Type: has_many
579              
580             Related object: L<Pinto::Schema::Result::Package>
581              
582             =head2 prerequisites
583              
584             Type: has_many
585              
586             Related object: L<Pinto::Schema::Result::Prerequisite>
587              
588             =head2 registrations
589              
590             Type: has_many
591              
592             Related object: L<Pinto::Schema::Result::Registration>
593              
594             =head1 L<Moose> ROLES APPLIED
595              
596             =over 4
597              
598             =item * L<Pinto::Role::Schema::Result>
599              
600             =back
601              
602             =head1 AUTHOR
603              
604             Jeffrey Ryan Thalhammer <jeff@stratopan.com>
605              
606             =head1 COPYRIGHT AND LICENSE
607              
608             This software is copyright (c) 2015 by Jeffrey Ryan Thalhammer.
609              
610             This is free software; you can redistribute it and/or modify it under
611             the same terms as the Perl 5 programming language system itself.
612              
613             =cut