in reply to Two Moose classes consumering the same role and using each other

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.

Replies are listed 'Best First'.
Re^2: Two Moose classes consumering the same role and using each other
by v_melnik (Scribe) on Jun 26, 2014 at 12:42 UTC

    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!

        Thank you!

        I've got rid of warnings by deleting additional slashes (I added them just for "visual rhyme" of code).

        And also warnings disappear when I add "make_immutable" (it's strongly recommended, as I understand) in the end of ClassA and ClassB packages.

        I'll definitely try Try::Tiny instead of eval, thanks a lot.

        V.Melnik