File Coverage

blib/lib/Whelk/StrictBase.pm
Criterion Covered Total %
statement 59 59 100.0
branch 6 6 100.0
condition 4 5 80.0
subroutine 13 13 100.0
pod 0 2 0.0
total 82 85 96.4


line stmt bran cond sub pod time code
1             package Whelk::StrictBase;
2             $Whelk::StrictBase::VERSION = '1.04';
3 25     25   180 use strict;
  25         58  
  25         1245  
4 25     25   145 use warnings;
  25         46  
  25         1657  
5              
6 25     25   230 use parent 'Kelp::Base';
  25         61  
  25         266  
7 25     25   2672 use Kelp::Util;
  25         68  
  25         842  
8 25     25   196 use Carp;
  25         56  
  25         2493  
9 25     25   246 use List::Util ();
  25         74  
  25         583  
10 25     25   14761 use Text::Levenshtein ();
  25         29373  
  25         4931  
11              
12             my %class_attributes;
13              
14             sub attr
15             {
16 814     814 0 1778 my ($class, $name, $default) = @_;
17              
18             # names starting with a question mark will be used to suggest proper key to
19             # the user
20 814         2125 my $for_user = $name =~ s/^\?//;
21              
22 814         2004 my $ret = Kelp::Base::attr($class, $name, $default);
23              
24 814         16230 $name =~ s/^-//;
25 814         2105 $class_attributes{$class}{$name} = $for_user;
26              
27 814         1843 return $ret;
28             }
29              
30             sub import
31             {
32 337     337   754 my $class = shift;
33 337         900 my $caller = caller;
34              
35             # Do not import into inherited classes
36 337 100       92317 return if $class ne __PACKAGE__;
37              
38 245   66     927 my $base = shift || $class;
39              
40             {
41 25     25   218 no strict 'refs';
  25         52  
  25         1161  
  245         396  
42 25     25   148 no warnings 'redefine';
  25         42  
  25         15426  
43              
44 245         1156 Kelp::Util::load_package($base);
45 245         4466 push @{"${caller}::ISA"}, $base;
  245         3536  
46 245   100     589 %{$class_attributes{$caller}} = %{$class_attributes{$base} // {}};
  245         1438  
  245         1559  
47              
48 245     814   1462 *{"${caller}::attr"} = sub { attr($caller, @_) };
  245         1605  
  814         1810  
49              
50 245         1779 namespace::autoclean->import(
51             -cleanee => $caller
52             );
53             }
54              
55 245         13469 strict->import;
56 245         6897 warnings->import;
57 245         89163 feature->import(':5.10');
58             }
59              
60             my $find_closest = sub {
61             my ($class, $key) = @_;
62              
63             my @options = grep { $class_attributes{$class}{$_} } keys %{$class_attributes{$class}};
64             my @distances = Text::Levenshtein::distance($key, @options);
65             my $min = List::Util::min(@distances);
66             return () unless defined $min && $min < 4;
67              
68             return map { $options[$_] } grep { $distances[$_] == $min } keys @options;
69             };
70              
71             sub new
72             {
73 763     763 0 3160 my ($class, %params) = @_;
74              
75 763         1973 foreach my $key (keys %params) {
76 1035 100       2646 if (!defined $class_attributes{$class}{$key}) {
77 6         11 my @closest = $find_closest->($class, $key);
78 6         9 my $hint = join ' or ', map { "'$_'" } @closest;
  5         14  
79              
80 6 100       82 croak "attribute '$key' is not valid for class $class" . ($hint ? ". Did you mean $hint?" : '');
81             }
82             }
83              
84 757         2398 return $class->SUPER::new(%params);
85             }
86              
87             1;
88