File Coverage

blib/lib/Module/CoreList/More.pm
Criterion Covered Total %
statement 23 24 95.8
branch 2 2 100.0
condition n/a
subroutine 13 13 100.0
pod 8 8 100.0
total 46 47 97.8


line stmt bran cond sub pod time code
1             package Module::CoreList::More;
2              
3             our $DATE = '2015-05-07'; # DATE
4             our $VERSION = '0.07'; # VERSION
5              
6 2     2   89823 use 5.010001;
  2         12  
  2         87  
7 2     2   10 use strict;
  2         3  
  2         68  
8 2     2   8 use warnings;
  2         2  
  2         60  
9              
10 2     2   7 use Module::CoreList;
  2         2  
  2         9  
11              
12             sub _firstidx {
13 22     22   45 my ($item, $ary) = @_;
14 22         59 for (0..@$ary-1) {
15 968 100       1363 return $_ if $ary->[$_] eq $item;
16             }
17 0         0 -1;
18             }
19              
20             # construct our own %delta from Module::CoreList's %delta. our version is a
21             # linear "linked list" (e.g. %delta{5.017} is a delta against %delta{5.016003}
22             # instead of %delta{5.016}. also, version numbers are cleaned (some versions in
23             # Module::CoreList has trailing whitespaces or alphas)
24              
25             # the same for our own %released (version numbers in keys are canonicalized)
26              
27             our @releases; # list of perl release versions, sorted by version
28             our @releases_by_date; # list of perl release versions, sorted by release date
29             our %delta;
30             our %released;
31             my %rel_orig_formats;
32             {
33             # first let's only stored the canonical format of release versions
34             # (Module::Core stores "5.01" as well as "5.010000"), for less headache
35             # let's just store "5.010000"
36             my %releases;
37             for (sort keys %Module::CoreList::delta) {
38             my $canonical = sprintf "%.6f", $_;
39             next if $releases{$canonical};
40             $releases{$canonical} = $Module::CoreList::delta{$_};
41             $released{$canonical} = $Module::CoreList::released{$_};
42             $rel_orig_formats{$canonical} = $_;
43             }
44             @releases = sort keys %releases;
45             @releases_by_date = sort {$released{$a} cmp $released{$b}} keys %releases;
46              
47             for my $i (0..@releases-1) {
48             my $reldelta = $releases{$releases[$i]};
49             my $delta_from = $reldelta->{delta_from};
50             my $changed = {};
51             my $removed = {};
52             # make sure that %delta will be linear "linked list" by release versions
53             if ($delta_from && $delta_from != $releases[$i-1]) {
54             $delta_from = sprintf "%.6f", $delta_from;
55             my $i0 = _firstidx($delta_from, \@releases);
56             #say "D: delta_from jumps from $delta_from (#$i0) -> $releases[$i] (#$i)";
57             # accumulate changes between delta at releases #($i0+1) and #($i-1),
58             # subtract them from delta at #($i)
59             my $changed_between = {};
60             my $removed_between = {};
61             for my $j ($i0+1 .. $i-1) {
62             my $reldelta_between = $releases{$releases[$j]};
63             for (keys %{$reldelta_between->{changed}}) {
64             $changed_between->{$_} = $reldelta_between->{changed}{$_};
65             delete $removed_between->{$_};
66             }
67             for (keys %{$reldelta_between->{removed}}) {
68             $removed_between->{$_} = $reldelta_between->{removed}{$_};
69             }
70             }
71             for (keys %{$reldelta->{changed}}) {
72             next if exists($changed_between->{$_}) &&
73             !defined($changed_between->{$_}) && !defined($reldelta->{changed}{$_}) || # both undef
74             defined ($changed_between->{$_}) && defined ($reldelta->{changed}{$_}) && $changed_between->{$_} eq $reldelta->{changed}{$_}; # both defined & equal
75             $changed->{$_} = $reldelta->{changed}{$_};
76             }
77             for (keys %{$reldelta->{removed}}) {
78             next if $removed_between->{$_};
79             $removed->{$_} = $reldelta->{removed}{$_};
80             }
81             } else {
82             $changed = { %{$reldelta->{changed}} };
83             $removed = { %{$reldelta->{removed} // {}} };
84             }
85              
86             # clean version numbers
87             for my $k (keys %$changed) {
88             for ($changed->{$k}) {
89             next unless defined;
90             s/\s+$//; # eliminate trailing space
91             # for "alpha" version, turn trailing junk such as letters to _
92             # plus a number based on the first junk char
93             s/([^.0-9_])[^.0-9_]*$/'_'.sprintf('%03d',ord $1)/e;
94             }
95             }
96             $delta{$releases[$i]} = {
97             changed => $changed,
98             removed => $removed,
99             };
100             }
101             }
102              
103             my $removed_from = sub {
104             my ($order, $module) = splice @_,0,2;
105             $module = shift if eval { $module->isa(__PACKAGE__) } && @_ > 0 && defined($_[0]) && $_[0] =~ /^\w/;
106              
107             my $ans;
108             for my $rel ($order eq 'date' ? @releases_by_date : @releases) {
109             my $delta = $delta{$rel};
110             if ($delta->{removed}{$module}) {
111             $ans = $rel_orig_formats{$rel};
112             last;
113             }
114             }
115              
116             return wantarray ? ($ans ? ($ans) : ()) : $ans;
117             };
118              
119             sub removed_from {
120 3     3 1 1395 $removed_from->('', @_);
121             }
122              
123             sub removed_from_by_date {
124 3     3 1 2216 $removed_from->('date', @_);
125             }
126              
127             my $first_release = sub {
128             my ($order, $module) = splice @_,0,2;
129             $module = shift if eval { $module->isa(__PACKAGE__) } && @_ > 0 && defined($_[0]) && $_[0] =~ /^\w/;
130              
131             my $ans;
132             for my $rel ($order eq 'date' ? @releases_by_date : @releases) {
133             my $delta = $delta{$rel};
134             if (exists $delta->{changed}{$module}) {
135             $ans = $rel_orig_formats{$rel};
136             last;
137             }
138             }
139              
140             return wantarray ? ($ans ? ($ans) : ()) : $ans;
141             };
142              
143             sub first_release {
144 5     5 1 4156 $first_release->('', @_);
145             }
146              
147             sub first_release_by_date {
148 5     5 1 2765 $first_release->('date', @_);
149             }
150              
151             my $is_core = sub {
152             my $all = pop;
153             my $module = shift;
154             $module = shift if eval { $module->isa(__PACKAGE__) } && @_ > 0 && defined($_[0]) && $_[0] =~ /^\w/;
155             my ($module_version, $perl_version);
156              
157             $module_version = shift if @_ > 0;
158             $perl_version = @_ > 0 ? shift : $];
159              
160             my $mod_exists = 0;
161             my $mod_ver; # module version at each perl release, -1 means doesn't exist
162              
163             RELEASE:
164             for my $rel (sort keys %delta) {
165             last if $all && $rel > $perl_version; # this is the difference with is_still_core()
166              
167             my $reldelta = $delta{$rel};
168              
169             if ($rel > $perl_version) {
170             if ($reldelta->{removed}{$module}) {
171             $mod_exists = 0;
172             } else {
173             next;
174             }
175             }
176              
177             if (exists $reldelta->{changed}{$module}) {
178             $mod_exists = 1;
179             $mod_ver = $reldelta->{changed}{$module};
180             } elsif ($reldelta->{removed}{$module}) {
181             $mod_exists = 0;
182             }
183             }
184              
185             if ($mod_exists) {
186             if (defined $module_version) {
187             return 0 unless defined $mod_ver;
188             return version->parse($mod_ver) >= version->parse($module_version) ? 1:0;
189             }
190             return 1;
191             }
192             return 0;
193             };
194              
195              
196             my $list_core_modules = sub {
197             my $all = pop;
198             my $class = shift if @_ && eval { $_[0]->isa(__PACKAGE__) };
199             my $perl_version = @_ ? shift : $];
200              
201             my %added;
202             my %removed;
203              
204             RELEASE:
205             for my $rel (sort keys %delta) {
206             last if $all && $rel > $perl_version; # this is the difference with list_still_core_modules()
207              
208             my $delta = $delta{$rel};
209              
210             next unless $delta->{changed};
211             for my $mod (keys %{$delta->{changed}}) {
212             # module has been removed between perl_version..latest, skip
213             next if $removed{$mod};
214              
215             if (exists $added{$mod}) {
216             # module has been added in a previous version, update first
217             # version
218             $added{$mod} = $delta->{changed}{$mod} if $rel <= $perl_version;
219             } else {
220             # module is first added after perl_version, skip
221             next if $rel > $perl_version;
222              
223             $added{$mod} = $delta->{changed}{$mod};
224             }
225             }
226             next unless $delta->{removed};
227             for my $mod (keys %{$delta->{removed}}) {
228             delete $added{$mod};
229             # module has been removed between perl_version..latest, mark it
230             $removed{$mod}++ if $rel >= $perl_version;
231             }
232              
233             }
234             %added;
235             };
236              
237 858     858 1 1585982 sub is_core { $is_core->(@_,1) }
238              
239 858     858 1 200522 sub is_still_core { $is_core->(@_,0) }
240              
241 4     4 1 5845 sub list_core_modules { $list_core_modules->(@_,1) }
242              
243 3     3 1 1830 sub list_still_core_modules { $list_core_modules->(@_,0) }
244              
245             1;
246              
247             # ABSTRACT: More functions for Module::CoreList
248              
249             __END__