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

Dear colleagues,

I started using Moose about a month ago and I like it.

Now I have a situation when I have some role (let's call it Role) consumed by two classes - ClassA and ClassB. The role has a method called do_something(). This method being called as ClassA->do_something() creates a ClassB-object.

require ClassB.pm; $objB = ClassB->new();

As I see by further tests, it works fine, but I'm getting warnings:

Subroutine foo redefined at /XXX/lib/YYY/ClassB.pm line 17. Subroutine bar redefined at /XXX/lib/YYY/ClassB.pm line 23. Subroutine baz redefined at /XXX/lib/YYY/ClassB.pm line 30.

Actually it says all methods I have in the role are redefined.

Is there a way to create a ClassB object from ClassA if both classes consume the same role?

Lots of thanks for your help!

V.Melnik

Replies are listed 'Best First'.
Re: Two Moose classes consumering the same role and using each other
by jeffa (Bishop) on Jun 26, 2014 at 16:39 UTC
Re: Two Moose classes consumering the same role and using each other
by Anonymous Monk on Jun 26, 2014 at 11:59 UTC

    I think we'll need to see more of the code (a minimized example that reproduces the problem would be the best)... at the moment we don't even know (and it's not clear from the description) which classes and roles define which methods.

    Also: "require ClassB.pm;" - is this an actual piece of code? Don't you use warnings; use strict;? If not, please do! You can just write "use ClassB;", that's the more common way to do it.

      No, problem, I'll add some actual code.

      Here is the role:

      package MonkeyMan::CloudStack::Element; use strict; use warnings; use Moose::Role; use namespace::autoclean; with 'MonkeyMan::ErrorHandling'; # ^^^ by the way, the role consumes one more role, but it doesn't gene +rate any warnings # <...> sub find_related_to_me { # <...> my $quasi_object = eval { require "MonkeyMan//CloudStack//Elements//$module_name.pm"; return("MonkeyMan::CloudStack::Elements::$module_name"->new(m +m => $mm)); }; # <...> } # <...> 1;

      And here goest our "ClassA":

      package MonkeyMan::CloudStack::Elements::Domain; use strict; use warnings; use MonkeyMan::Constants; use Moose; use MooseX::UndefTolerant; use namespace::autoclean; with 'MonkeyMan::CloudStack::Element'; sub element_type { # <...> } sub _load_full_list_command { # <...> } sub _generate_xpath_query { # <...> } 1;

      ClassB looks almost the same way:

      package MonkeyMan::CloudStack::Elements::VirtualMachine; use strict; use warnings; use MonkeyMan::Constants; use Moose; use MooseX::UndefTolerant; use namespace::autoclean; with 'MonkeyMan::CloudStack::Element'; sub element_type { # <...> } sub _load_full_list_command { # <...> } sub _generate_xpath_query { # <...> } 1;

      Yes, ClassA and ClassB are quite short and quite similar, the differences are only in a couple of lines, because all their logic is coded in the Element role. Then my main module calls ClassA->find_related_to_me(...) and it needs to create a ClassB object for doing some tricks. The object is being created, everything works fine, but here are some warnings:

      Subroutine element_type redefined at /opt/monkeyman-0.2.x/bin/../lib/M +onkeyMan//CloudStack//Elements//VirtualMachine.pm line 17. Subroutine _load_full_list_command redefined at /opt/monkeyman-0.2.x/b +in/../lib/MonkeyMan//CloudStack//Elements//VirtualMachine.pm line 23. Subroutine _generate_xpath_query redefined at /opt/monkeyman-0.2.x/bin +/../lib/MonkeyMan//CloudStack//Elements//VirtualMachine.pm line 30.

      So it doesn't like that I defined element_type(), _load_full_list_command() and _generate_xpath_query() in MonkeyMan::CloudStack::Elements::Domain and then I require MonkeyMan::CloudStack::Elements::VirtualMachine where the same methods are defined as well.

      I'm pretty sure that my case isn't too "exotic" and many Perl developers are facing with the necessity of using "sibling" classes consuming the same role.

      Thank you!

      V.Melnik

        I think this is nothing to do with Moose. I think you're loading VirtualMachine.pm twice. Normally require uses %INC to avoid doing that, but the second time you're loading it as ""MonkeyMan//CloudStack//Elements//VirtualMachine.pm" with those strange doubled slashes, so require sees it as a different file name and loads it again.

        You have warnings enabled, so you get a warning about the subs being redefined in the MonkeyMan::CloudStack::Elements::VirtualMachine package. This is because the first time VirtualMachine.pm is loaded, the subs get defined, and the second time it's loaded, they get redefined. (Redefined to the same definition, but redefined all the same!)

        If you want to check to see if I'm right, do this in your role:

        use Data::Dumper (); END { print Data::Dumper::Dumper(\%INC); }

        ... then run your script. When it exits you should get a big dump of all the modules that Perl loaded. VirtualMachine.pm will be in there twice with slightly differing paths.

        I'd combat this problem by getting rid of this:

        my $quasi_object = eval { require "MonkeyMan//CloudStack//Elements//$module_name.pm"; return("MonkeyMan::CloudStack::Elements::$module_name"->new(m +m => $mm)); };

        And replacing it with something cleaner like this:

        use Try::Tiny; use Module::Runtime qw(use_module); my $quasi_object = try { my $class = "MonkeyMan::CloudStack::Elements::$module_name"; use_module($class)->new( mm => $mm ); };

        You won't be adding any extra dependencies because Moose already uses Try::Tiny and Module::Runtime internally!