package My::UNIVERSAL; use strict; use warnings; use Scalar::Util 'reftype'; use Carp 'croak'; use base qw(Class::Data::Inheritable); __PACKAGE__->mk_classdata('__data_key'); sub mk_accessors { my ( $class, @accessors ) = @_; foreach my $accessor (@accessors) { no strict 'refs'; my $key = "$class\::$accessor"; *$key = sub { my $self = shift; return $self->{$key} unless @_; $self->{$key} = shift; return $self; }; } } my $check_keys = sub { my $self = shift; my $data = delete $self->{ $self->__data_key }; if ( my @keys = keys %$data ) { local $" = ', '; croak("Unknown keys to constructor: (@keys)"); } }; sub new { my ( $class, $args ) = @_; my $key = " $class data "; $class->__data_key($key); $args ||= {}; unless ( 'HASH' eq reftype $args ) { croak('Argument to new() must be a hashref'); } my $self = bless { $key => $args } => $class; $self->_initialize; $self->$check_keys; return $self; } sub _initialize { my $class = ref($_[0]); croak "_initialize() must be overridden in a ($class)"; } my $check_attribute = sub { my ( $self, $attribute ) = @_; my $value = delete $self->{ $self->__data_key }->{$attribute}; if ( !defined wantarray ) { $self->$attribute($value); } else { return $value; } }; sub _must_have { my ( $self, $attribute ) = @_; croak("Mandatory attribute ($attribute) not found") unless exists $self->{ $self->__data_key }->{$attribute}; goto $check_attribute; } sub _may_have { my ( $self, $attribute ) = @_; return unless exists $self->{ $self->__data_key }->{$attribute}; goto $check_attribute; } 1;