in reply to Re: Method of child module called by parent
in thread Method of child module called by parent

I suspect what you’re looking for is a helper class which your parent class accesses via a HASA or USES relationship. But in the absence of concrete details it’s hard to give more definite advice.

Yes, that is essentially what I'm trying to do.

Specifically, I'm working on an API for Samba 4 Active Directory using Net::LDAP. The API is meant to abstract much of the LDAP code needed to query the AD. For example, you could ask the api:

@posix_users = $ad->listObjects('users','posix');

And then the API would spit out the requested data. The problem I was running into is that the over all perl module was getting large in size, so I thought I'd try and spin off some of the methods to sub-classes. Good examples are those methods associated with users would get a module: Users.pm. And then those associated with groups, Groups.pm.

So my module structure looks like this:

esmith::AD (AD.pm)
- esmith::AD::Users (Users.pm)
- esmith::AD::Groups (Groups.pm)

The methods in Users.pm and Groups.pm really do specialize active directory objects generated in the parent module. Following your example, a user ISA object.

So the primary reason for wanting the parent class to see the child class methods, we me being lazy, I think.

Lazy Code (child method called from parent constructor): use esmith::AD; use strict; my $ad=esmith::AD->new(); warn "User exists\n" if ($ad->doesUserExist('greg')); Proper code (child method called from child constructor): use esmith::AD::User; use strict; my $ad=esmith::AD::User->new(); warn "User exists\n" if ($ad->doesUserExist('greg'));

Is this more along the lines that you were trying to explain?

Replies are listed 'Best First'.
Re^3: Method of child module called by parent
by Athanasius (Archbishop) on Dec 26, 2014 at 09:33 UTC

    Hello gzartman,

    Thanks for adding the <code> tags to the OP.

    If you really need do need to call doesUserExist() from the parent class (which I doubt), you’ll have to give the parent class a default version of this method, then override it in the child class(es) as needed. For example (untested):

    package esmith::AD; sub doesUserExist { return 0; # default answer: 'No' } package esmith::AD::User; use List::Util qw( any ); my @user_names; # class variable sub doesUserExist { my ($self) = @_; # assuming that $self is a hash reference return any { $_ eq $self->{Name} } @user_names; }

    But I still don’t see what you gain by this. In your example, the “lazy” code is no shorter than the “proper” code, so why not just use the latter?

    It occurs to me that you might, just possibly, be looking for some form of factory, but again this seems like overkill unless you have a good reason for it?

    Update: The test for whether or not a new user already exists should, of course, be done in the constructor.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Anthanasius,

      Thank you for the reply and good info. This is making quite alot more sense to me now. I've been using perl for years, but more for system calls and what not, like one would use shell.

      I see now where I was looking at the sub-classes backwards. I've got a really good book titled "Advanced Perl Programming" by Sriram Srinivasan. I spent the day reading through this and reflecting on your comments.

      My ultimate goal is this a easy to use Active Directory API that abstracts Net::LDAP, which I think I've done a fairly good job at doing from a OOP standpoint. I was just looking at the sub-classes incorrectly. I'm rethinking my design now so that the sub-class methods properly inherit functionality from the parent class. One of the things I want to be able to do is to have methods in my parent class that do things like listing Active Directory objects, by object type (i.e., DNS, Users, Groups). I then would have methods in my child class that would be called directly to list the type of objects you are after, causing the method in the parent class to morph to list the requested AD objects called by the method in the child class. I think I'm now there.

      Not the OP, but, Why is this backwards? This is actually a design pattern that I use somewhat regularly with Moose. Or the Correlary, Moose makes this design pattern extremely easy to implement with Delegation (and it's more advanced cousin Currying)

      An example is my Build::VM library I'm working on: https://github.com/three18ti/Build-VM/blob/master/lib/Build/VM.pm

      In my main class Build::VM, I have (stripped down):

      package Build::VM; use 5.010; use Moose; use strict; use warnings; use Build::VM::Guest; has guest => ( isa => 'Build::VM::Guest', lazy => 1, default => sub { Build::VM::Guest->new( name => $_[0]->guest_name, memory => $_[0]->to_kib($_[0]->guest_memory), disk_list => $_[0]->disk_list, cdrom_list => $_[0]->cdrom_list || [[]], ); }, handles => { get_memory => 'memory', }, );

      Then in my driver script that calls the module, I do:

      #!/usr/bin/perl use 5.010; use strict; use warnings; use Build::VM; # Get the parameters from the CLI or config file my $bvm = Build::VM->new( name => $name, memory => $memory, disk_list => $disk_list, cdrom_list => $cdrom_list ); say $bvm->get_memory();

      (ok, bad example because cdrom_list is the only one that is using delegation, technically ->memory is a method in the Build::VM::Guest object, it's just not a user defined function so it's a little more ambiguous).

      Unless I'm misunderstanding what OP wants to do, why is this backwards or inside out? And if it's so backwards, why does Moose make it so easy? If you don't mind, what would be the appropriate organization for my various modules?

      Also, when talking about Perl and inheritance, (which is what I think of when I hear "child class") I'm usually pointed to Moose Roles because they make multiple inheritance trivial (because it's not really inheritance, it's more like Ruby's mixins).

      Thanks, I always like posts like this, even though it's not something I thought I was doing wrong, I like to learn when I am :)

      -three18ti

        Hello three18ti, and happy New Year!

        The OO design I described as back-to-front was a parent class calling methods defined in its own child class. By “parent” and “child” I meant two classes related by inheritance: the child class inherits the parent’s methods (and class data), and overrides method implementations only as needed. One of the main advantages of inheritance is that it facilitates code reuse: by creating a new class and having it inherit from an existing class, your new (child) class reuses as much as it needs of the code already written in the existing (parent) class. And there should be no need for the parent to access the specialised code in its child class(es). If the parent does reference its children, a maintenance problem is created: changes to one child class propagate upwards to the parent, and then potentially back downwards to sibling classes.

        I had a look at your Build::VM project, and found this module structure:

        +--Build-VM/ +--lib/ | +--Build/ | | +--VM.pm (Build::VM) | | +--VM/ | | | +--Guest.pm (Build::VM::Guest)

        But this module layout does not constitute inheritance! The module Guest.pm begins:

        package Build::VM::Guest; use Moose; use strict; use warnings; use MooseX::HasDefaults::RO;

        There is no reference to any parent class, no extends statement, so no inheritance (or ISA) relationship is established. The module VM.pm contains:

        use Build::VM::Guest;

        which creates a HASA relationship, which is what I recommended to the OP when I said:

        I suspect what you’re looking for is a helper class which your parent class accesses via a HASA or USES relationship.

        Since your Build::VM and Build::VM::Guest classes are not related by inheritance, my comments about the design being back-to-front do not apply.

        Note also that Moose delegation is not inheritance-related:

        Delegation is a feature that lets you create "proxy" methods that do nothing more than call some other method on an attribute. This lets you simplify a complex set of "has-a" relationships and present a single unified API from one class.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re^3: Method of child module called by parent
by Anonymous Monk on Dec 26, 2014 at 16:43 UTC

    Some food for thought

    my $ad = AD->new( $server, $login, $pass ); if( my $user = $ad->GetUser( 'craig' ) ){ $user->replace( company => 'rompany' ); $user->company( 'rompany' ); } else { $ad->AddUser( company => 'rompany', ); } sub AD::new { ... return $conn } sub AD::GetUser { my( $conn, $user ) = @_; return AD::User->get( conn => $conn, user => $user ); } sub AD::AddUser { my $conn = shift; Add::User->add( conn => $conn, @_ ); } sub AD::GetGroup { my( $conn, $group ) = @_; return AD::Group->get( conn => $conn, user => $group ); } sub AD::User::add { ... croak "name cannot be 42"; }

    More food for thought App::LDAP, Net::LDAP::Class, Catalyst::Model::LDAP