dragonchild has asked for the wisdom of the Perl Monks concerning the following question:

I have an OO representation I like to use because I wrote it and it's very light. One of the design decisions was to have every class using this representation to "register" its attributes. But, I didn't want to do this using import. (One reason is that I wanted a class to be able to add attributes at run-time.)

So, I created a function called define_attributes(). At first, I had:

__PACKAGE__->define_attributes(qw/ foo bar /);
Ugly. Reading PM led me to the following import function:
sub import { my $caller = (caller)[0]; no strict 'refs'; *{"${caller}::define_attributes"} = \&define_attributes; *{"${caller}::import"} = \&import; }
Better. This works with inheritance and looks clean. However, it clobbers import(). (My contract with anyone who uses me is that I will be clobbering define_attributes(), and that's ok.) Also, a co-worker of mine said that it also will not work with use base (which does not call import() on its own). So, he came up with the following:
{ no strict 'refs'; my $i = 0; while(my $caller = (caller($i))[0]) { unless ($caller eq 'main' or $caller eq 'base') { *{"${caller}::define_attributes"} = \&define_attributes; } $i++; } }
However, this doesn't work with two branches. (Foo and Bar both inherit from Base. Foo gets public(), but Bar doesn't.)

Anyone have any suggestions? I'd really like to submit this module to CPAN so I can use it in other modules I'm working on, but this problem is causing serious issues for me.

------
We are the carpenters and bricklayers of the Information Age.

Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Replies are listed 'Best First'.
Re: importing a function
by adrianh (Chancellor) on Feb 12, 2003 at 16:44 UTC

    Personally I don't find:

    __PACKAGE__->define_attributes(qw/foo bar/);

    ugly - and that's what I would go for since it makes sense and is a familiar construct. Personally I find mixing functional and OO APIs in the same module confusing ;-)

    However, if you really don't like it one other solution would be to reverse the process and have each class declare a method that returns the attributes, e.g:

    sub define_attributes { qw(foo bar) };

    You could then have the super-class scan for its sub-classes via an INIT block, after the sub-classes should have been compiled (obviously won't work if you require classes at run time tho'). You can then find their attributes by calling the appropriate define_attributes method.

    Another method would be to use something like Attribute::Handlers so you could do:

    sub attrs : Attributes { qw(foo bar) };
Re: importing a function
by broquaint (Abbot) on Feb 12, 2003 at 17:25 UTC
    If I understand your question correctly then you probably want something like this
    BEGIN { *old_import = \&import if defined &import; } sub import { _init(@ISA); goto &old_import if defined &old_import; } sub _init { foreach(@_) { _init(@{$_."::ISA"}) if exists ${$_.'::'}{ISA}; *{$_."::define_attributes"} = \&define_attributes unless defined &{$_.'::define_attributes'}; } }
    So that'll add define_attributes() to any parent classes that don't already have that defined. I haven't tested it so I can vouch for it's safety ;)
    HTH

    _________
    broquaint

      That does solve the problem of clobbering import(). However, how would one go about solving this problem for classes that have use base?

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        However, how would one go about solving this problem for classes that have use base?
        Since base updates the @ISA of each package that shouldn't be a problem.
        HTH

        _________
        broquaint