Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

undef is undef

by jozef (Sexton)
on Aug 29, 2012 at 08:40 UTC ( [id://990406]=perlquestion: print w/replies, xml ) Need Help??

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

Hi, mostly with DBIx::Class I have to often write:

my $foo = $bar->related1->related2->foo;

While any of the related objects may not exist and return undef so it's necessary to test first or do an easy bad solution and use eval:

my $foo = eval{ $bar->related1->related2->foo };

Which also means that when ever there is a typoo (for example misspelled relation name) inside eval {}, it's silently ignored.

And here is my question: Is there a module or an easy way to say Perl that in this given scope when ever method is called on undef return undef instead of an exception? Here is how it would look like:

my $foo = undef_is_undef { undef->bar };

Thanks!
Jozef

Replies are listed 'Best First'.
Re: undef is undef
by Corion (Patriarch) on Aug 29, 2012 at 08:44 UTC

    Maybe instead of tracing the paths in Perl, you want to make the database do the work instead?

    I suggest you either write a view or a query in DBIx::Class that does the JOIN query directly instead of manually fetching the tables from the database.

Re: undef is undef
by moritz (Cardinal) on Aug 29, 2012 at 08:52 UTC

    If you often need to chase multiple levels of tabels, it means you are working on a too low level.

    Add an accessor to the result class that does an in-database join over the levels, which automatically deals with the case of not having results at one level or another.

      Well, even for one level it is necessary to check first. here's real world example:

      my $avatar = $usr->person->avatar;

      usr and person (extended information about user) are two tables. not every user has to have extended information and the code snipped just wants to find out if user has avatar.

      Adding accessor to usr sounds like a wise solution to hide test ifs, but it will mask the fact that avatar is accessor of person.

        Adding accessor to usr sounds like a wise solution to hide test ifs, but it will mask the fact that avatar is accessor of person.

        Hiding unneeded facts is what abstraction is all about. Why should a user of your backend model care about which table the avatar comes from? If the user doesn't have to care, it means you can change that stuff later on the in the database and only adapt your model, not your code that generates the HTML.

Re: undef is undef
by philiprbrenan (Monk) on Aug 29, 2012 at 13:00 UTC

    Perhaps something like:

    use feature ":5.14"; use warnings FATAL => qw(all); use strict; use Data::Dump qw(dump pp); use Test::More qw(no_plan); sub deref(@) {my $h = shift; for(@_) {return undef unless $h and ref($h) eq __PACKAGE__ and $h->can($_); $h = $h->$_; } $h } my $h = bless {a=>bless{a=>bless{a=>bless{a=>1, b=>2}}}}; sub aaa() {$_[0]->{a}} sub bbb() {$_[0]->{b}} ok !defined($h->deref(qw(bbb bbb bbb bbb))); ok 1 == $h->deref(qw(aaa aaa aaa aaa)); ok 2 == $h->deref(qw(aaa aaa aaa bbb));

    Produces

    ok 1
    ok 2
    ok 3
    1..3
    
Re: undef is undef
by andye (Curate) on Aug 29, 2012 at 13:42 UTC
    this works, but it's not pretty:

    use strict; use warnings; #set up a pretend subref inside some other stuff my $bar = {foo => [ sub {print "fishcake"}]}; { no warnings "uninitialized"; # we're going to be a bit naughty in t +his block ($bar->{potato}->[0] or sub {return undef})->() } # swap this for # ($bar->{foo}->[0] or sub {return undef})->() # and see what happens

    if you use that then if I were you I'd comment it liberally, and possibly surround it with bio-hazard marker tape.

Re: undef is undef
by sundialsvc4 (Abbot) on Aug 29, 2012 at 14:32 UTC

    Even though it may sound very pedantic and old-fashioned, I aggressively write code that verifies that something which might legitimately be undef is or is not.   Then, I enclose blocks of code in exception-handling blocks which, if they go off, package the exception message in another, case-specific class of exception object and then throw that.   I don’t use “well, an exception has been thrown, so eat the exception and then do this,” because an exception could be thrown for any reason, not just the one I expect.

    I would politely argue that you should never write that “merely ‘works’ (sic).”   Because there is only one agent out there with a clear chance to determine whether the app is or is not working properly, and that agent is:   the application itself.

    The next argument is in favor of clarity above all other considerations.   Go ahead and write two, three, four statements instead of one “clever” one if the multiple-statements approach is “obvious at a glance” and/or more maintainable in the decades to come.   You probably have CPU-power to burn.   (Maybe you legitimately don’t in high-science, but in bread and butter business apps you certainly do ... and what you can’t afford is risk.   Especially the risk of undetected application failure.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://990406]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (5)
As of 2024-04-25 12:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found