Whelk
view release on metacpan or search on metacpan
lib/Whelk/StrictBase.pm view on Meta::CPAN
package Whelk::StrictBase;
$Whelk::StrictBase::VERSION = '1.04';
use strict;
use warnings;
use parent 'Kelp::Base';
use Kelp::Util;
use Carp;
use List::Util ();
use Text::Levenshtein ();
my %class_attributes;
sub attr
{
my ($class, $name, $default) = @_;
# names starting with a question mark will be used to suggest proper key to
# the user
my $for_user = $name =~ s/^\?//;
my $ret = Kelp::Base::attr($class, $name, $default);
$name =~ s/^-//;
$class_attributes{$class}{$name} = $for_user;
return $ret;
}
sub import
{
my $class = shift;
my $caller = caller;
# Do not import into inherited classes
return if $class ne __PACKAGE__;
my $base = shift || $class;
{
no strict 'refs';
no warnings 'redefine';
Kelp::Util::load_package($base);
push @{"${caller}::ISA"}, $base;
%{$class_attributes{$caller}} = %{$class_attributes{$base} // {}};
*{"${caller}::attr"} = sub { attr($caller, @_) };
namespace::autoclean->import(
-cleanee => $caller
);
}
strict->import;
warnings->import;
feature->import(':5.10');
}
my $find_closest = sub {
my ($class, $key) = @_;
my @options = grep { $class_attributes{$class}{$_} } keys %{$class_attributes{$class}};
my @distances = Text::Levenshtein::distance($key, @options);
my $min = List::Util::min(@distances);
return () unless defined $min && $min < 4;
return map { $options[$_] } grep { $distances[$_] == $min } keys @options;
};
sub new
{
my ($class, %params) = @_;
foreach my $key (keys %params) {
if (!defined $class_attributes{$class}{$key}) {
my @closest = $find_closest->($class, $key);
my $hint = join ' or ', map { "'$_'" } @closest;
croak "attribute '$key' is not valid for class $class" . ($hint ? ". Did you mean $hint?" : '');
}
}
return $class->SUPER::new(%params);
}
1;
( run in 2.111 seconds using v1.01-cache-2.11-cpan-5a3173703d6 )