Earlier today, Curtis "Ovid" Poe wrote in his journal about a trick to restrict accesses to a blessed hash to packages in that instance's inheritence tree. He saw this as a clever way to add strict encapsulation to objects.
I got intrigued by this idea not for real code but for detecting naughty code during debugging. It overloads hash accesses to throw exceptions if anyone tries to access object properties directly instead of through accessor methods.
Even more interesting, I figured out how to insert the encapsulation at runtime!
I created a prototype (not headed to CPAN yet) that I'm calling Class::Encapsulate for now. Feedback on that name is welcome. As proof of concept, I applied this prototype to the Perl::Critic test suite. Here's the block of code I inserted into the Perl::Critic test code in t/40_criticize.t:
# Strict object testing -- prevent direct hash key access
eval { require Class::Encapsulate::Runtime; };
if ( !$EVAL_ERROR ) {
for my $pkg ( '', '::Config', '::Policy', '::Violation' ) {
diag 'apply Class::Encapsulate to Perl::Critic'.$pkg;
Class::Encapsulate::Runtime->apply_to('Perl::Critic'.$pkg);
}
}
My pre-0.01 prototype is in the Clotho SVN
Bravo (Score:1)
Nits (Score:1)
ref() lies about object blessings.
Only undef indicates "not-blessed." Some classes are false.
You might want to look at ->DOES( ... ) as well. It's new to 5.10 but it gets at the idea of interfaces that don't use ISA. Allomorphism and all that stuff chromatic is on about.
The return result of _hash_overload should probably be the object itself. You don't need to have a separate hash in %instances, store t
Re: (Score:1)
In fact he needs to duplicate all the things that Class::InsideOut [cpan.org] provides in order to be leak- and thread-safe.
Re: (Score:1)
I've considered calling it Devel::Encapsulate. If you want real encapsulation, you should use a real implementation like inside-out objects.
Re: (Score:1)
No storage is even required to do this.
Re: (Score:1)
The !$pkg part was just to catch a bug that I've since removed, so I think it's obsolete. ref() is good enough for this hack module because if you are using it with something other than blessed hashes, then you get what you deserve.
Re: (Score:1)
Re: (Score:1)
I don't understand. ref() and blessed() only differ in behavior for SCALAR, HASH, ARRAY, CODE, etc. right? Are you worried that someone is going to apply Class::Encapsulate to HASH? Would that even work???
Re: (Score:1)
defined( blessed() is full fidelity because it always returns true for all objects and it always returns false for all non-objects. Plain bool( blessed() ) is *not* sufficient because not all class names evaluate to true.
This is why blessed() returns undef instead of '' for it's "false" value. blessed( bless
Re: (Score:1)
I don't care about non-blessed refs because they can't be affected by "use overload". And "0" and "" are not valid names in "package" statements, so I don't care about those either -- people who bless instances into packages like that get wh
Re: (Score:1)
As for being safe with defined(blessed(...)), I advocate that because there really is seriously weird code out there and pretending otherwise just gets us into accidents.
Also, there's a related issue that no one has come up with a good method overridable method to ask an object what class it wants to pretend to be blessed into. Maybe Moose/MOP provides a nice name. The ref() function is absolutely evil to override while at least b
Re: (Score:1)
I should document that... It's so Class::Encapsulate can be a superclass and automatically install the encapsulation. That co
Re: (Score:1)