#!/usr/bin/perl -- use strict; use warnings; use Data::Dump qw/ dd /; Main( @ARGV ); exit( 0 ); sub Main { eval { my $vmio = Steeve->new( 'ro', 'sham', 'bo', 5 ); dd( { -ro => $vmio->ro, -bo => $vmio->bo } ); dd( $vmio->dump ); $vmio->{ohno} = 4; 1; } or warn $@; eval { my $vmio = Steeve->new( ); dd( $vmio->dump ); $vmio->ro('sham'); $vmio->bo(5); dd( $vmio->dump ); print $vmio->{bo}; 1; } or warn $@; } BEGIN { package VMIOH; $INC{'VMIOH.pm'} = __FILE__; use Variable::Magic qw< wizard cast getdata >; use Carp qw/ croak /; use strict; use warnings; require Exporter; our @ISA = qw/ Exporter /; our @EXPORT = qw/ has dump new /; # exported by default sub has { my $class = caller; for my $attr( @_ ){ croak qq{illegal "$attr"} if not $attr =~ m/^[a-zA-Z][a-zA-Z0-9]+$/; no strict 'refs'; *{ $class .'::'. $attr } = sub { $#_ ? $_[0]{$attr} = $_[1] : $_[0]{$attr} }; } return; } my $protected = sub { croak qq{Attempt to access protected data "$_[2]"} unless caller()->isa( __PACKAGE__ ); }; my $wiz = wizard( store => $protected, fetch => $protected, exists => $protected, delete => $protected, ); sub new { my $class = shift; my %params = @_; my %self; cast %self, $wiz; my $self = \%self; bless $self, $class; $self->$_($params{$_}) foreach keys %params; return $self; } sub dump { return scalar { %{ shift() } }; } 1; } BEGIN { package Steeve; $INC{'Steeve.pm'} = __FILE__; use VMIOH qw/ has new dump /; has 'ro'; has 'bo'; no VMIOH qw/ has /; 1; } __END__ { -bo => 5, -ro => "sham" } { bo => 5, ro => "sham" } Attempt to access protected data "ohno" at - line 12. {} { bo => 5, ro => "sham" } Attempt to access protected data "bo" at - line 21.