Как правильно создать метод BUILDARGS с помощью MooseX::Declare?

У меня возникают трудности с правильным использованием MooseX::Declare при вызове BUILDARGS.

Я пытаюсь создать объект как интерфейс для файла. (В частности, мне нужен интерфейс для двоичного файла, который позволяет мне просмотреть следующие несколько байтов в файле, а затем отрезать их для дальнейшей обработки.)

Я хочу иметь возможность создать один из этих объектов, подобных этому

my $f = binary_file_buffer->new( $file_name );

а затем использовать его так

while( my $block_id = $f->peek( $id_offset, $id_length ) ) {
    $block_id = unpack_block_id( $block_id );
    $munge_block{ $block_id }->(
        $f->pop( $block_size[ $block_id ] )
    );
}

Мое определение/объявление класса binary_file_buffer выглядит следующим образом

use MooseX::Declare;
class binary_file_buffer {
    use FileHandle;
    use Carp;

    has _file      => ( is => 'ro', isa => 'FileHandle' );
    has _file_name => ( is => 'ro', isa => 'Str' );
    has _buff      => ( is => 'rw', isa => 'Str',  default => '' );

    method BUILDARGS ( Str $file_name ) {
      my $file = FileHandle->new( $file_name );
      carp "unable to open $file_name : $!" unless defined $file;
      $file->binmode;
      return (
        _file_name => $file_name,
        _file      => $file,
      );
    }

    # get the next n bytes from the buffer.
    method pop ( Int $len ) {
        # ... Make sure there is data in _buff
        return substr( $self->{_buff}, 0, $len, '' );
    }

    # Look around inside the buffer without changing the location for pop
    method peek ( Int $offset, Int $len ) {
        # ... Make sure there is data in _buff
        return substr( $self->{_buff}, $offset, $len );
    }
}

(Есть код загрузки буфера и управления, который я здесь не включил. Это довольно прямолинейно.)

Проблема в том, что я использую ключевое слово method в объявлении BUILDARGS. Таким образом, MooseX::Declare ожидает объект binary_file_buffer в качестве первого аргумента для BUILDARGS. Но BUILDARGS получает аргументы, передаваемые в new, поэтому первым аргументом является строка a 'binary_file_buffer', имя пакета. В результате он не проходит проверку типов и умирает при создании объекта с помощью new, как я сделал в первом фрагменте кода. (По крайней мере, так я понимаю происходящее.)

Сообщение об ошибке, которое я получаю:

Validation failed for 'MooseX::Types::Structured::Tuple[MooseX::Types::Structured::Tuple[Object,Str,Bool],MooseX::Types::Structured::Dict[]]' failed with value [ [ "binary_file_buffer", "drap_iono_t1.log", 0 ], {  } ], Internal Validation Error is: Validation failed for 'MooseX::Types::Structured::Tuple[Object,Str,Bool]' failed with value [ "binary_file_buffer", "drap_iono_t1.log", 0 ] at C:/bin/perl/site/lib/MooseX/Method/Signatures/Meta/Method.pm line 445
 MooseX::Method::Signatures::Meta::Method::validate('MooseX::Method::Signatures::Meta::Method=HASH(0x2a623b4)', 'ARRAY(0x2a62764)') called at C:/bin/perl/site/lib/MooseX/Method/Signatures/Meta/Method.pm line 145
 binary_file_buffer::BUILDARGS('binary_file_buffer', 'drap_iono_t1.log') called at generated method (unknown origin) line 5
 binary_file_buffer::new('binary_file_buffer', 'drap_iono_t1.log') called at logshred.pl line 13

Мне нравится сахар проверки типов, который ключевое слово method предоставляет для $file_name, но я не знаю, как его получить, поскольку BUILDARGS технически не является методом.

Есть ли в MooseX::Declare способ пропустить создание $self или что-то в этом роде?

Правильно ли я делаю MooseX::Declare? Или я что-то упускаю?


person meta4    schedule 03.09.2009    source источник


Ответы (3)


Я думаю, вам нужно что-то вроде method BUILDARGS (ClassName $class: Str $filename) { ... }, в котором вы явно определяете invocant как ClassName $class.

person perigrin    schedule 03.09.2009
comment
Бинго! Это делает именно то, что я хочу. Я пропустил 2 вещи в документах, которые разъяснил ваш ответ. Во-первых, я не видел подтипа ClassName для Str в документах Moose::Manual::Types. Я не ожидал встроенного подтипа строки ClassName. Во-вторых, я не видел документации MooseX::Method::Signatures, показывающей, как назвать вызов метода и объявить его тип. Я не знал, что синтаксис подписи метода, который использует MooseX::Declare, взят из пакета MooseX::Method::Signatures. Пока мне нравится MooseX::Declare. Я знал, что есть способ сделать то, что я хочу, по-лосячьи. Я просто не мог понять, как. Спасибо. - person meta4; 03.09.2009
comment
Использование ClassName вместо Object для метода/вокруг действительно должно быть написано где-то в руководстве MooseX::Declare... это тоже укусило - person anydot; 12.04.2011

Я думаю, вы хотите:

#!/use/bin/perl

use strict;
use warnings;

use MooseX::Declare;
class BinaryFile::Buffer {
    use FileHandle;
    use Carp;

    has file      => ( is => 'ro', isa => 'FileHandle');
    has file_name => ( is => 'ro', isa => 'Str');
    has _buff     => (
        is       => 'rw',
        isa      => 'Str',
        default  => '',
        init_arg => undef
    );

    sub BUILDARGS {
        my ($class, $file_name) = @_;
        my $file = FileHandle->new( $file_name ) or do {
            carp "unable to open ", $file_name, " : $!";
            return;
        };
        $file->binmode;
        return $class->SUPER::BUILDARGS(
                file_name => $file_name,
                file      => $file
        );
    }

    # get the next n bytes from the buffer.
    method pop(Int $len) {
        # ... Make sure there is data in _buff
        return substr( $self->buff, 0, $len, '' );
    }

    # Look around inside the buffer without changing the location for pop
    method peek(Int $offset, Int $len) {
        # ... Make sure there is data in _buff
        return substr( $self->buff, $offset, $len );
    }
}

my $f = BinaryFile::Buffer->new($0);

print $f->file_name, "\n";
person Chas. Owens    schedule 03.09.2009

также действительно аккуратный способ сделать это (просто расширение ответа передо мной):

use MooseX::MultiMethods;

multi method BUILDARGS (ClassName $class: Str $filename) {

#do whatever you want to do if only a strg is passed

}

таким образом, MooseX::MultiMethods позаботится о том, чтобы, если вы НЕ вызываете FileHandle->new($file_name),

но

FileHandle->new(
_filename => $file_name
);

(это нормальный синтаксис),

еще бы работало!

Кроме того, вы могли бы (что не так полезно для имен файлов, но в других случаях)

добавить

multi method ( ClassName $class, Int $some_number ){}

таким образом, new теперь может обрабатывать хэш-ссылки, целые числа и строки...

о возможности... ;)

person erazor    schedule 02.11.2009