I've been doing some more work with the Class::Trait traits I've written, for a client in the aviation industry, to enable various hairy searches on cdbi classes, and found that I frequently wanted to add extra temporary fields to the classes that are populated by the searches.
The result is this little hack..
=head2 extra_fields
Returns an array of any additional attributes this trait can populate
__PACKAGE__->columns( TEMP => qw/foo bar/,__PACKAGE__->extra_fields );
=cut
sub extra_fields {
my $self = shift;
my @fields = ();
foreach my $trait ( $self->does ) {
(my $traitname = $trait) =~ s/^(\w+\:\:)+(.*)$/$2/;
my $extra_field_subname = lc("_extra_fields_$traitname");
if ($trait->can($extra_field_subname)) {
push (@fields, $trait->$extra_field_subname);
} else {
warn "trait $trait doesn't have extra fields";
}
}
return @fields;
}
..which goes in a trait used by the individual search traits (it's there to provide various shared functionality), which are in turn used by the CDBI classes.
..then I add the sub below in the MyApp::Traits::Foo trait
sub _extra_fields_foo { return 'distance'; }
Now I can add a column named 'distance' to any query in that trait, and the it'll populate that attribute in all the objects in the result.
This is because Class::DBI's sth_to_objects class method will map all columns in the results of a query to attributes in the object, and using the temporary fields means it won't try to save them if you want to modify an object.
What would be really neat would be if Class::Trait allowed you to set method attributes to make methods private or friends, C++ style - I'll email Ovid at some point about it.
I'm rather interested in that. It's a fascinating approach.
As for private and protected methods, private methods can be supported merely by using sub assigned to a variable:
my $private = sub {
my $self = shift;
# do stuff
return $result;
};
sub foo {
my $self = shift;
my $stuff = $self->$private(@_);
# do more stuff
}
That has the advantage of working right now without requiring any code in Class::Trait to change. As for protected or trusted methods, I'm not sure of the best way to implement that. Perhaps if you have can list some examples of how you'd like to see it work?
Side note: You can post code here in <ecode></ecode> tags.
Re:Neat trick!
TeeJay on 2006-11-22T10:12:20
Thanks:)
I was thinking of something like..
sub _internal_foo : private {.. }
or
sub _internal_shared_foo : protected {.. }
using sub attributes
The subs hack will work within a trait, but the code I posted is in a trait used by other traits, so it needs to access what is in them, at the moment anybody can access them, which doesn't worry me too much, I only want to avoid the namespace pollution that would come if I added a ton of extra methods shared between this group of traits:)
It's fun to mix idioms from perl 5 and 6:)
Re:Neat trick!
Ovid on 2006-11-22T11:30:58
Actually, I've already received a patch for that syntax, but in asking around, I received feedback from folks suggesting that they weren't too keen on using attributes. The only reference I can find to that is here.
But are protected methods that useful here? Protected methods can be inherited, but part of the impetus behind traits is to lesson folks dependence on broken inheritance schemes. Still, I can see how some folks might like them.
As for private methods your trait can provide to classes, I would recommend (for the time being) to stick with the leading underscore convention.
Re:Neat trick!
TeeJay on 2006-11-22T17:00:32
I'd be interested in that patch, maybe in order to put together a subclass that just adds it onto Class::Trait.. although I suspect that would be nontrivial..
Presumably the patch is attached to a wishlist item in rt?
'Fraid I'm still stuck on dial up since moving house and so can't really do much useful apart from throw peanuts from this here gallery until next week.