A piggy bank of commands, fixes, succinct reviews, some mini articles and technical opinions from a (mostly) Perl developer.

Jump to

Quick reference

Showing posts with label moose. Show all posts
Showing posts with label moose. Show all posts

Moose questions

These points are not covered in the Moose documentation:

Question: If you set an attribute in the constructor, does it override the builder?
Answer: Yes, the builder is not run.
Code:
perl -MMoose -le'package Quxx; use Moose; has "qux" => ( is => "ro", builder => "_build_qux" ); sub _build_qux { die "duck" }; package main; my $q = Quxx->new( qux => "cat" ); print "qux = #".$q->qux."#"'
Output:
qux = #cat#

Question: How do you compose a writer subroutine? (aka setter / mutator method).
Answer: You don't need to, Moose creates it for you. Just define it in the attribute and then call it.
Code:
perl -MMoose -le'package Foo; use Moose; has "foo" => ( is => "rw", writer => "_set_foo" ); sub set_foo { "bar" }; sub bar { my $self = shift; $self->_set_foo(2); }; package main; my $f = Foo->new; $f->bar(4); print "foo = #".$f->foo."#"'
Output:
foo = #2#

Manual automatic accessors in Perl

If you don't have Moose, Mo* or Class::Accessor:

package Foo;

=head1 SYNOPSIS

    my $f = Foo->new(
        bar => 'rab',
        qux => 'xuq',
    );

=cut

use constant ACCESSOR_MAP => {
    'get_bar' => 'bar',
    'get_qux' => 'qux',
};

use constant ACCESSOR_ITEM => qw(ping ting zing);

sub new {
    my ( $c, %args ) = @_;
    my $class = ref $c || $c;
    bless {%args}, $class;
}

sub __mk_accessor {
    my ( $c, $name, $key ) = @_;
    $key ||= $name;
    my $pkg = ref $c || $c;
    my $sym = join( '::', $pkg, $name );
    no strict 'refs';
    *{$sym} = sub {
        my ($self) = @_;
        $self->{$key};
    };
}

# set up ping(), ting(), zing()
__PACKAGE__->__mk_accessor($_) foreach __PACKAGE__->ACCESSOR_ITEM;

# set up get_bar(), get_qux()
for my $k ( keys %{ __PACKAGE__->ACCESSOR_MAP } ) {
    my $v = __PACKAGE__->ACCESSOR_MAP->{$k};
    __PACKAGE__->__mk_accessor( $k, $v );
}

# not tested

Perl MooseX::Params::Validate gotcha

This error:

Parameter #1 ("1") to Some::Module::a_method did not pass the \'checking type constraint for Different::Module\' callback\n at /some/path/to/some/file/or/other.pm line 42

(but the parameters passed in are actually all correct)

...it could mean you forgot to shift off $self. pos_validated_list doesn't detect $self like validated_list and validated_hash do.

Parameter checking Perl


use MooseX::Params::Validate;
use MooseX::Types::Moose qw{ ArrayRef Int };
use MooseX::Types::Structured qw{ Dict };
use MooseX::Types::Common::Numeric qw{ PositiveInt PositiveOrZeroInt };
use MooseX::Types::Common::String qw{ NonEmptyStr };

sub some_method {
    my ($self, $channel_id, $some_array_of_structs) = validated_list(
        \@_,
        channel_id   => { isa => PositiveInt },
        channel_name => { isa => NonEmptyStr },
        some_array_of_structs => {
            isa => ArrayRef[ Dict[
                some_id    => PositiveInt,
                quantity   => PositiveOrZeroInt,
                cruciality => Int
            ] ]
        }
    );
    # ...
}

Trace the execution of subroutines under mod_perl

1) First try one of these to trace every line (doesn't actually capture subroutine names):

Debug::TraceDebug::LTraceDevel::Trace, or others.

2) Then if you want to filter the list, try:

perl PERL5DB='sub DB::DB {my @c=caller;return if $c[1] =~ m|/opt/foo/bar| || $c[1] =~ m|/qux/| || $c[1] =~ m|Useless|; print STDERR qq|@c[1,2] ${"::_<$c[1]"}[$c[2]]|}' perl -d path/to/test/file.t

(thanks Brian)

3) Here's one I came up with using Moose:

use Moose;
use Scalar::Util;
use Data::Dumper;
for my $func qw(list all the subroutine names here) {
    around $func => sub {
        my $orig = shift;
        my $self = shift;
        warn "Running __PACKAGE__::${func} with:\n";
        my $i = 0;
        foreach my $param (@_) {
            $i++;
            # don't display class it$self
            next if blessed $param and $param->isa(__PACKAGE__);
            if (blessed $param) {
                # try not to dump out huge objects
                warn "\t$i = $param\n";
            } else {
                warn "\t$i = ".Dumper($param);
            }
        }
        # call the original sub
        $self->$orig(@_);
    }
}

4) Simple way to log all parameters:

my $r=\@_; $logger->debug('entering ', sub { $logger->dump(args => $r) });

5) Simple way to log method name only, without potentially verbose parameters:

$logger->info("entering ".( caller(0) )[3]);

6) Log4perl configuration

Use %M to log the method name, then just log anything, e.g. "entering".