view release on metacpan or search on metacpan
- To support the previous item, massive refactoring.
- Minor API changes that shouldn't affect anyone.
0.64 2003-04-27
- Callbacks are given 'args' and 'self' keys.
- Callbacks can be arrays of callbacks.
0.62 2003-04-20
- Fixed erroneous call to on_fail()
- Added 'verbose' debugging capability.
- Added 'constructor' option to create_class/import.
# ERROR HANDLING AND BAD PARSES
Often, I will speak of `undef` being returned, however that's not strictly
true.
When a simple single specification is given for a method, the method isn't
given a single parser directly. It's given a wrapper that will call `on_fail`
if the single parser returns `undef`. The single parser must return `undef`
so that a multiple parser can work nicely and actual errors can be thrown from
any of the callbacks.
Similarly, any multiple parsers will only call `on_fail` right at the end
when it's tried all it could.
`on_fail` (see [later](#on_fail)) is defined, by default, to throw an error.
Multiple parser specifications can also specify `on_fail` with a coderef as
an argument in the options block. This will take precedence over the
inheritable and overrideable method.
That said, don't throw real errors from callbacks in multiple parser
specifications unless you really want parsing to stop right there and not try
any other parsers.
In summary: calling a **method** will result in either a `DateTime` object
being returned or an error being thrown (unless you've overridden `on_fail`
or `create_method`, or you've specified a `on_fail` key to a multiple
parser specification).
Individual **parsers** (be they multiple parsers or single parsers) will return
either the `DateTime` object or `undef`.
If any specifications without _length_s are given and the particular
_length_ parser fails, then the non-_length_ parsers are tried.
This parameter is ignored unless the specification is part of a multiple
parser specification.
- label
**label** provides a name for the specification and is passed to some of the
callbacks about to mentioned.
- on\_match and on\_fail
**on\_match** and **on\_fail** are callbacks. Both routines will be called with
parameters of:
- input
**input** is the input to the parser (after any preprocessing callbacks).
- label
**label** is the label of the parser if there is one.
- self
**self** is the object on which the method has been invoked (which may just be
a class name). Naturally, you can then invoke your own methods on it do get
information you want.
anonymous, or whatever, then it's passed more or less straight through. The
code should return `undef` in event of failure (or any false value, but
`undef` is strongly preferred), or a true value in the event of success
(ideally a `DateTime` object or some object that has the same interface).
This all said, I generally wouldn't recommend using this feature unless you
have to.
## Callbacks
I mention a number of callbacks in this document.
Any time you see a callback being mentioned, you can, if you like, substitute
an arrayref of coderefs rather than having the straight coderef.
# MULTIPLE SPECIFICATIONS
These are very easily described as an array of single specifications.
Note that if the first element of the array is an arrayref, then you're
specifying options.
- on\_fail
**on\_fail** should be a reference to a subroutine that is called if the parser
fails. If this is not provided, the default action is to call
`DateTime::Format::Builder::on_fail`, or the `on_fail` method of the
subclass of DTFB that was used to create the parser.
# EXECUTION FLOW
Builder allows you to plug in a fair few callbacks, which can make following
how a parse failed (or succeeded unexpectedly) somewhat tricky.
## For Single Specifications
A single specification will do the following:
User calls parser:
my $dt = $class->parse_datetime($string);
writing the multi-length code (both one length with multiple parsers and
single parser with multiple lengths), blame for the Regex custom constructor
code, spotting a bug in Dispatch, and more much needed review.
Kellan Elliott-McCrea (KELLAN) for even more review, suggestions,
[DateTime::Format::W3CDTF](https://metacpan.org/pod/DateTime%3A%3AFormat%3A%3AW3CDTF) and the encouragement to rewrite these docs almost
100%!
Claus Färber (CFAERBER) for having me get around to fixing the
auto-constructor writing, providing the 'args'/'self' patch, and suggesting
the multi-callbacks.
Rick Measham (RICKM) for [DateTime::Format::Strptime](https://metacpan.org/pod/DateTime%3A%3AFormat%3A%3AStrptime) which Builder now
supports.
Matthew McGillis for pointing out that `on_fail` overriding should be
simpler.
Simon Cozens (SIMON) for saying it was cool.
# SEE ALSO
lib/DateTime/Format/Builder.pm view on Meta::CPAN
=head1 ERROR HANDLING AND BAD PARSES
Often, I will speak of C<undef> being returned, however that's not strictly
true.
When a simple single specification is given for a method, the method isn't
given a single parser directly. It's given a wrapper that will call C<on_fail>
if the single parser returns C<undef>. The single parser must return C<undef>
so that a multiple parser can work nicely and actual errors can be thrown from
any of the callbacks.
Similarly, any multiple parsers will only call C<on_fail> right at the end
when it's tried all it could.
C<on_fail> (see L<later|/on_fail>) is defined, by default, to throw an error.
Multiple parser specifications can also specify C<on_fail> with a coderef as
an argument in the options block. This will take precedence over the
inheritable and overrideable method.
That said, don't throw real errors from callbacks in multiple parser
specifications unless you really want parsing to stop right there and not try
any other parsers.
In summary: calling a B<method> will result in either a C<DateTime> object
being returned or an error being thrown (unless you've overridden C<on_fail>
or C<create_method>, or you've specified a C<on_fail> key to a multiple
parser specification).
Individual B<parsers> (be they multiple parsers or single parsers) will return
either the C<DateTime> object or C<undef>.
lib/DateTime/Format/Builder.pm view on Meta::CPAN
If any specifications without I<length>s are given and the particular
I<length> parser fails, then the non-I<length> parsers are tried.
This parameter is ignored unless the specification is part of a multiple
parser specification.
=item * label
B<label> provides a name for the specification and is passed to some of the
callbacks about to mentioned.
=item * on_match and on_fail
B<on_match> and B<on_fail> are callbacks. Both routines will be called with
parameters of:
=over 4
=item * input
B<input> is the input to the parser (after any preprocessing callbacks).
=item * label
B<label> is the label of the parser if there is one.
=item * self
B<self> is the object on which the method has been invoked (which may just be
a class name). Naturally, you can then invoke your own methods on it do get
information you want.
lib/DateTime/Format/Builder.pm view on Meta::CPAN
anonymous, or whatever, then it's passed more or less straight through. The
code should return C<undef> in event of failure (or any false value, but
C<undef> is strongly preferred), or a true value in the event of success
(ideally a C<DateTime> object or some object that has the same interface).
This all said, I generally wouldn't recommend using this feature unless you
have to.
=head2 Callbacks
I mention a number of callbacks in this document.
Any time you see a callback being mentioned, you can, if you like, substitute
an arrayref of coderefs rather than having the straight coderef.
=head1 MULTIPLE SPECIFICATIONS
These are very easily described as an array of single specifications.
Note that if the first element of the array is an arrayref, then you're
specifying options.
lib/DateTime/Format/Builder.pm view on Meta::CPAN
B<on_fail> should be a reference to a subroutine that is called if the parser
fails. If this is not provided, the default action is to call
C<DateTime::Format::Builder::on_fail>, or the C<on_fail> method of the
subclass of DTFB that was used to create the parser.
=back
=head1 EXECUTION FLOW
Builder allows you to plug in a fair few callbacks, which can make following
how a parse failed (or succeeded unexpectedly) somewhat tricky.
=head2 For Single Specifications
A single specification will do the following:
User calls parser:
my $dt = $class->parse_datetime($string);
lib/DateTime/Format/Builder.pm view on Meta::CPAN
writing the multi-length code (both one length with multiple parsers and
single parser with multiple lengths), blame for the Regex custom constructor
code, spotting a bug in Dispatch, and more much needed review.
Kellan Elliott-McCrea (KELLAN) for even more review, suggestions,
L<DateTime::Format::W3CDTF> and the encouragement to rewrite these docs almost
100%!
Claus Färber (CFAERBER) for having me get around to fixing the
auto-constructor writing, providing the 'args'/'self' patch, and suggesting
the multi-callbacks.
Rick Measham (RICKM) for L<DateTime::Format::Strptime> which Builder now
supports.
Matthew McGillis for pointing out that C<on_fail> overriding should be
simpler.
Simon Cozens (SIMON) for saying it was cool.
=head1 SEE ALSO
lib/DateTime/Format/Builder/Parser.pm view on Meta::CPAN
$self->{parser} = $parser;
$self;
}
sub set_fail {
my ( $self, $fail ) = @_;
$self->{on_fail} = $fail;
$self;
}
my @callbacks = qw( on_match on_fail postprocess preprocess );
{
my %params = (
common => {
length => {
type => SCALAR | ARRAYREF,
optional => 1,
callbacks => {
'is an int' => sub { ref $_[0] ? 1 : $_[0] !~ /\D/ },
'not empty' => sub { ref $_[0] ? @{ $_[0] } >= 1 : 1 },
}
},
# Stuff used by callbacks
label => { type => SCALAR, optional => 1 },
(
map { $_ => { type => CODEREF | ARRAYREF, optional => 1 } }
@callbacks
),
},
);
sub params {
my $self = shift;
my $caller = ref $self || $self;
return { map {%$_} @params{ $caller, 'common' } };
}
lib/DateTime/Format/Builder/Parser.pm view on Meta::CPAN
}
sub create_single_parser {
my $class = shift;
return $_[0] if ref $_[0] eq 'CODE'; # already code
@_ = %{ $_[0] } if ref $_[0] eq 'HASH'; # turn hashref into hash
# ordinary boring sort
my %args = validate( @_, params_all() );
# Determine variables for ease of reference.
for (@callbacks) {
$args{$_} = $class->merge_callbacks( $args{$_} ) if $args{$_};
}
# Determine parser class
my $from;
for ( keys %args ) {
$from = whose_params($_);
next if ( not defined $from ) or ( $from eq 'common' );
last;
}
croak "Could not identify a parsing module to use." unless $from;
# Find and call parser creation method
my $method = $from->can("create_parser")
or croak
"Can't create a $_ parser (no appropriate create_parser method)";
my @args = %args;
%args = validate( @args, $from->params );
$from->$method(%args);
}
sub merge_callbacks {
my $self = shift;
return unless @_; # No arguments
return unless $_[0]; # Irrelevant argument
my @callbacks = @_;
if ( @_ == 1 ) {
return $_[0] if ref $_[0] eq 'CODE';
@callbacks = @{ $_[0] } if ref $_[0] eq 'ARRAY';
}
return unless @callbacks;
for (@callbacks) {
croak "All callbacks must be coderefs!" unless ref $_ eq 'CODE';
}
return sub {
my $rv;
my %args = @_;
for my $cb (@callbacks) {
$rv = $cb->(%args);
return $rv unless $rv;
# Ugh. Symbiotic. All but postprocessor return the date.
$args{input} = $rv unless $args{parsed};
}
$rv;
};
}
sub create_multiple_parsers {
my $class = shift;
my ( $options, @specs ) = @_;
my $obj = $class->new;
# Organise the specs, and transform them into parsers.
my ( $lengths, $others ) = $class->sort_parsers( $options, \@specs );
# Merge callbacks if any.
for ('preprocess') {
$options->{$_} = $class->merge_callbacks( $options->{$_} )
if $options->{$_};
}
# Custom fail method?
$obj->set_fail( $options->{on_fail} ) if exists $options->{on_fail};
# Who's our maker?
$obj->set_maker( $options->{maker} ) if exists $options->{maker};
# We don't want to save the whole options hash as a closure, since
# that can cause a circular reference when $options->{maker} is
# set.
my $preprocess = $options->{preprocess};
# These are the innards of a multi-parser.
my $parser = sub {
my ( $self, $date, @args ) = @_;
return unless defined $date;
# Parameters common to the callbacks. Pre-prepared.
my %param = (
self => $self,
( @args ? ( args => \@args ) : () ),
);
my %p;
# Preprocess and potentially fill %p
if ($preprocess) {
$date = $preprocess->( input => $date, parsed => \%p, %param );
lib/DateTime/Format/Builder/Parser.pm view on Meta::CPAN
This takes a single specification and returns a coderef that is a parser that
suits that specification. This is the end of the line for all the parser
creation methods. It delegates no further.
If a coderef is specified, then that coderef is immediately returned (it is
assumed to be appropriate).
The single specification (if not a coderef) can be either a hashref or a
hash. The keys and values must be as per the specification.
It is here that any arrays of callbacks are unified. It is also here that any
parser implementations are used. With the spec that's given, the keys are
looked at and whichever module is the first to have a unique key in the spec
is the one to whom the spec is given.
B<Note>: please declare a C<valid_params> argument with an uppercase
letter. For example, if you're writing
C<DateTime::Format::Builder::Parser::Fnord>, declare a parameter called
C<Fnord>. Similarly, C<DTFBP::Strptime> should have C<Strptime> and
C<DTFBP::Regex> should have C<Regex>. These latter two don't for backwards
compatibility reasons.
The returned parser will return either a C<DateTime> object or C<undef>.
=head3 merge_callbacks
Produce either undef or a single coderef from either undef, an empty array, a
single coderef or an array of coderefs
=head2 create_multiple_parsers
Given the options block (as made from C<create_parser>) and a list of single
parser specifications, this returns a coderef that returns either the
resultant C<DateTime> object or C<undef>.
lib/DateTime/Format/Builder/Parser.pm view on Meta::CPAN
=head2 Declaring specification arguments
Call C<<DateTime::Format::Builder::Parser->valid_params>> with
C<Params::Validate> style arguments. For example:
DateTime::Format::Builder::Parser->valid_params(
params => { type => ARRAYREF },
Regex => {
type => SCALARREF,
callbacks => {
'is a regex' => sub { ref(shift) eq 'Regexp' }
}
}
);
Start one of the key names with a capital letter. Ideally that key should
match the I<XXX> from earlier. This will be used to help identify which module
a parser specification should be given to.
The key names I<on_match>, I<on_fail>, I<postprocess>, I<preprocess>, I<label>
lib/DateTime/Format/Builder/Parser.pm view on Meta::CPAN
Its arguments are as for a normal method (i.e. class as first argument). The
other arguments are the result from a call to C<Params::Validate> according to
your specification (the C<valid_params> earlier), i.e. a hash of argument name
and value.
The return value should be a coderef that takes a date string as its first
argument and returns either a C<DateTime> object or C<undef>.
=head2 Callbacks
It is preferred that you support some callbacks to your parsers. In
particular, C<preprocess>, C<on_match>, C<on_fail> and C<postprocess>. See the
L<main Builder|DateTime::Format::Builder> docs for the appropriate placing of
calls to the callbacks.
=head1 SEE ALSO
C<datetime@perl.org> mailing list.
http://datetime.perl.org/
L<perl>, L<DateTime>, L<DateTime::Format::Builder>.
L<Params::Validate>.
lib/DateTime/Format/Builder/Parser/Quick.pm view on Meta::CPAN
our %dispatch_data;
use Params::Validate qw( SCALAR OBJECT CODEREF validate );
use parent qw( DateTime::Format::Builder::Parser );
__PACKAGE__->valid_params(
Quick => {
type => SCALAR | OBJECT,
callbacks => {
good_classname => sub {
( ref $_[0] ) or ( $_[0] =~ /^\w+[:'\w+]*\w+/ );
},
}
},
method => {
optional => 1,
type => SCALAR | CODEREF,
},
);
lib/DateTime/Format/Builder/Parser/Regex.pm view on Meta::CPAN
use parent 'DateTime::Format::Builder::Parser::generic';
__PACKAGE__->valid_params(
# How to match
params => {
type => ARRAYREF, # mapping $1,$2,... to new args
},
regex => {
type => SCALARREF,
callbacks => {
'is a regex' => sub { ref(shift) eq 'Regexp' }
}
},
# How to create
extra => {
type => HASHREF,
optional => 1,
},
constructor => {
type => CODEREF | ARRAYREF,
optional => 1,
callbacks => {
'array has 2 elements' => sub {
ref( $_[0] ) eq 'ARRAY' ? ( @{ $_[0] } == 2 ) : 1;
}
}
},
);
sub do_match {
my $self = shift;
my $date = shift;
lib/DateTime/Format/Builder/Parser/generic.pm view on Meta::CPAN
=head3 new
Standard constructor. Returns a blessed hash; any arguments are placed in the
hash. This is useful for storing information between methods.
=head3 generic_parser
This is a method provided solely for the benefit of C<Parser>
implementations. It semi-neatly abstracts a lot of the work involved.
Basically, it takes parameters matching the assorted callbacks from the parser
declarations and makes a coderef out of it all.
Currently recognized callbacks are:
=over 4
=item * on_match
=item * on_fail
=item * preprocess
=item * postprocess
t/mergecb.t view on Meta::CPAN
use Test::More;
use DateTime::Format::Builder::Parser;
{
my $new_sub = sub {
my $x = shift;
sub { $_[1] . $x }
};
my @cbs = ( map { $new_sub->($_) } qw( a b c d e f g ) );
my $cb = DateTime::Format::Builder::Parser->merge_callbacks(@cbs);
is( $cb->( input => "x" ) => "xabcdefg", "Callback chaining works." );
my $cbr = DateTime::Format::Builder::Parser->merge_callbacks( \@cbs );
is(
$cbr->( input => "x" ) => "xabcdefg",
"Callback chaining works on ref."
);
}
{
my $inout = sub { $_[0] . "foo" };
my $cb = DateTime::Format::Builder::Parser->merge_callbacks($inout);
is( $cb->("foo") => "foofoo", "Single callback works." );
}
{
my $empty = DateTime::Format::Builder::Parser->merge_callbacks(undef);
ok( !defined $empty, "Given undef, do bugger all." );
$empty = DateTime::Format::Builder::Parser->merge_callbacks();
ok( !defined $empty, "Given nothing, do bugger all." );
$empty = DateTime::Format::Builder::Parser->merge_callbacks( [] );
ok( !defined $empty, "Given empty arrayref, do bugger all." );
}
{
my $error = eval {
DateTime::Format::Builder::Parser->merge_callbacks( { foo => 4 } );
};
ok( $@, "Correctly faulted on bad arguments." );
}
done_testing();