MooseX::Params Usage Examples

MooseX::Params (my experiment in parameter processing) has undergone some changes and only the the attributes-based interface has been left now. Here are some examples from the synopsis of how it works. Most of them have been adapted from the Moose manual and the subroutines chapter of Gabor Szabo's Perl 6 tutorial.

In brief, parameters are declared via the Args attribute, and made available in the %_ hash. You can use Moose types for validation, and an ampersand before a type constraint enables coercion.

sub add :Args(Int first, Int second) {
  return $_{first} + $_{second};
}

say add(2, 3); # 5
say add(2);    # error

subtype 'HexNum', as 'Str', where { /[a-f0-9]/i };
coerce 'Int', from 'HexNum', via { hex $_ };

sub add2 :Args(&Int first, &Int second) {
  return $_{first} + $_{second};
}

say add2('A', 'B'); # 21

# you can mix named and positional
sub add3 :Args(a, :b) {
  return $_{a} + $_{b} * 2;
}

# say add3( 3, b => 2 ); # 7
# say add3(4, 9);        # error
# say add3(2);           # error
# say add3(2, 3, 4);     # error

Slurpy arguments consume the remainder of @_.

sub sum :Args(ArrayRef *values) {
  my $sum = 0;
  my @values = @{$_{values}};

  foreach my $value (@values) {
    $sum += $value;
  }
  return $sum;
}

say sum(2, 3, 4, 5); # 14

In this subroutine the parameter 'all' is optional. If not present it searches the text within a file and returns 1 if found, 0 if not; if present it searches the text and returns the number of lines in which the text is found.

sub search :Args(text, fh, all?) {
  my $cnt = 0;

  while (my $line = $_{fh}->getline) {
    if ( index($line, $_{text}) > -1 ) {
      return 1 if not $_{all};
      $cnt++;
    }
  }

  return $cnt;
}

Paramerers can have simple defaults, or full-blown lazy parameter builders:

sub shop :Args(:size = 'medium', :color = 'white') { ... }

sub shop2 :Args(
  :size   = _build_param_size,
  :color  = _build_param_color,
  :height = 170 )
{ ... }

sub _build_param_color {
  return (qw(red green blue))[ int( rand 3 ) ];
}

# you can access all other parameters within a builder
sub _build_param_size {
  return $_{height} > 200 ? 'large' : 'medium';
}

BuildArgs can be used to preprocess @_ before it is validated against the signature, and CheckArgs can be used to perform additional validation after the %_ hash has been populated.

# preprocess @_ with buildargs
sub process_template
  :Args(input, output, params)
  :BuildArgs(_buildargs_process_template)
{
  say "open $_{input}";
  say "replace " . Dumper $_{params};
  say "save $_{output}";
}

# if 'output' is not provided, get it from input filename
sub _buildargs_process_template {
  if (@_ == 2) {
    my ($input, $params) = @_;
    my $output = $input;
    substr($output, -4, 4, "html");
    return $input, $output, $params;
  } else {
    return @_;
  }
}

my %data = (
  fname => "Foo",
  lname => "Bar",
);

process_template("index.tmpl", \%data);
# open index.tmpl
# replace {"lname" => "Bar", "fname" => "Foo"}
# save index.html

process_template("from.tmpl", "to.html", \%data);
# open from.tmpl
# replace {"lname" => "Bar", "fname" => "Foo"}
# save to.html

sub process_person
  :Args(:first_name!, :last_name!, :country!, :ssn?)
  :CheckArgs # shortcut for :CheckArgs(_checkargs_${subname})
{ ... }

sub _checkargs_process_person {
  if ( $_{country} eq 'USA' ) {
    die 'All US residents must have an SSN' unless $_{ssn};
  }
}

Most of the time you will use MooseX::Params in classes. Note the shortcut syntax to declare an invocant.

package User;

use Moose;
use MooseX::Params;
use DateTime;

extends 'Person';

has 'password' => (
  is  => 'rw',
  isa => 'Str',
);

has 'last_login' => (
  is      => 'rw',
  isa     => 'DateTime',
);

sub login :Args(self: Str pw) {
  return 0 if $_{pw} ne $_{self}->password;

  $_{self}->last_login( DateTime->now() );

  return 1;
}

Read the full docs and get the latest version from CPAN now (serious bugs are to be expected).

Posted in Modules
blog comments powered by Disqus