Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?

Re^2: How best to validate the keys of hashref arguments?

by cbeckley (Curate)
on Mar 16, 2017 at 16:54 UTC ( #1184901=note: print w/replies, xml ) Need Help??

in reply to Re: How best to validate the keys of hashref arguments?
in thread How best to validate the keys of hashref arguments?

Wow, haukex, thank you. There's a lot of info in there. Restricted hashes from Hash::Util works beautifully and is exactly what I was looking for.

For the curious, I added a sub to my ssh module:

sub ops_new_cmd { my ($init_hash) = @_; my $new_cmd = {}; lock_ref_keys($new_cmd, qw(name user host key command status ssh_re +tcode ssh_retmsg output std_err cmd_ret_code cmd_ret_msg)); for my $k (keys %$init_hash) { $new_cmd->{$k} = $init_hash->{$k}; } return $new_cmd }

And then invoking it, from my initial example:

my @commands = ( ops_new_cmd({ name => 'command_name1', user => 'user1', host => '', command => 'rsync_command yadda yadda' }), ops_new_cmd({ name => 'command_name2', user => 'user2', host => '', command => 'rsync_command yadda yadda' }) );

You are correct, however, I am going to need to add some validation to the values at some point. I'm leaning toward using OO. Thank you for the examples there as well.


Replies are listed 'Best First'.
Re^3: How best to validate the keys of hashref arguments?
by cbeckley (Curate) on Mar 16, 2017 at 17:48 UTC

    Ah, forgot to ask, in the previous node, the line of code:

    for my $k (keys %$init_hash) { $new_cmd->{$k} = $init_hash->{$k}; }

    Is there a more idiomatic way to say that?


      for my $k (keys %$init_hash) { $new_cmd->{$k} = $init_hash->{$k}; }
      Is there a more idiomatic way to say that?

      Several, but it seems to me you're just copying over every value from %$init_hash into %$new_cmd, so it seems easiest to write:

      my %new_cmd = %$init_hash;

      This creates the same shallow copy of the hash that your current code is creating. You can lock_keys afterwards and incorrect keys will still cause corresponding errors. In the interest of TIMTOWTDI, here's a couple other solutions, most of which would be better applicable if you only wanted to copy over a subset of the keys. Note that the latter three clobber the entire contents of the hash, which should be fine in your case:

      sub ops_new_cmd { my ($init_hash) = @_; my %new_cmd; # -OR- #my %new_cmd = %$init_hash; lock_keys(%new_cmd, qw/name user host command/); my @keyset = keys %$init_hash; #for my $k (@keyset) { $new_cmd{$k} = $init_hash->{$k}; } # -OR- #$new_cmd{$_} = $init_hash->{$_} for @keyset; # -OR- %new_cmd = map { $_=>$init_hash->{$_} } @keyset; # -OR- #use 5.020; # For Key/Value Slices #%new_cmd = %$init_hash{ @keyset }; # -OR- #use experimental 'postderef'; # For Postfix Deref + Key/Value Sli +ces #%new_cmd = $init_hash->%{ @keyset }; return \%new_cmd }

      As for locked hashes, there was some discussion on P5P recently about them, but even if something were to happen to this feature, tied hashes would be an easy replacement. For example, a quick search on CPAN shows Tie::Hash::FixedKeys. On the other hand, if you start taking locked hashes that seriously, it's probably better to start moving to OO.

      Update in response to your reply, since I should have included it here in the first place: Personally what I currently use locked hashes for is mostly typo prevention, which is helpful during development, but could also be removed later without really affecting the code.

        Ah, well that last bit certainly nudges me a little harder towards OO, which is where I was leaning anyway, eventually ...

        In the short term Hash::Util::lock_ref_keys keeps me from looking for typos at run time, which was what I wanted ... initially ...

        Thanks again for all your input. Incredibly helpful, and enlightening, and oh-my-god-do-I-have-a-lot-to-learn!

      Like this perhaps ?

      #!perl use strict; use Hash::Util 'lock_keys'; use Data::Dumper ; # valid my $cmd = ops_new_cmd({ name=>1, user=>2, host=>3}); print Dumper $cmd; # not valid $cmd = ops_new_cmd({ name=>1, user=>2, hostt=>3}); sub ops_new_cmd { my ($init_hash) = @_; my @valid = qw( name user host key command status ssh_retcode ssh_retmsg output std_err cmd_ret_code cmd_ret_msg); + lock_keys(my %new_cmd,@valid); %new_cmd = %$init_hash; return \%new_cmd; }

        Yes it does, so does this:

        %$new_cmd = %$init_hash;

        Of course both assume I'm never gonna want to nest other hashrefs in my commands and initializers. I'm not, right? No. Never. Of course not ....

        Right. Ok, I'm going to use Clone.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1184901]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2020-05-28 22:13 GMT
Find Nodes?
    Voting Booth?
    If programming languages were movie genres, Perl would be:

    Results (166 votes). Check out past polls.