NAME
Data::Filter::Abstract::Util - DSL for generating Perl filter expressions from data structures
SYNOPSIS
use Data::Filter::Abstract::Util qw(:all);
my $expr = simple_sub(foo => "bar");
# => '($_->{foo} eq "bar")'
my $code = eval "sub { $expr }";
$code->({ foo => "bar" }); # true
DESCRIPTION
This module implements a small domain-specific language (DSL) for describing boolean filters as Perl data structures. The DSL is compiled into Perl expressions (as strings) which operate on $_, assumed to be a hash reference.
The resulting expressions are intended to be embedded into sub { ... } blocks and evaluated.
The test suite defines the DSL behavior and serves as its authoritative specification.
GENERAL SEMANTICS
All generated expressions assume
$_is a hash reference.Field access is performed as
$_-{field}>.Hashes represent logical AND.
Arrays represent logical OR, unless explicitly overridden by logical operators.
Generated expressions are syntactically valid Perl code suitable for
eval.
BASIC FILTER FORMS
Scalar equality
simple_sub(foo => "bar")
Generates a string equality comparison:
($_->{foo} eq "bar")
If the value looks like a number, numeric comparison is used:
simple_sub(foo => 1)
# ($_->{foo} == 1)
Regular expressions
simple_sub(foo => qr/12/)
simple_sub(foo => qr/[a-z]/i)
Generates regex match expressions:
($_->{foo} =~ qr/12/)
($_->{foo} =~ qr/[a-z]/i)
Hash form (implicit AND)
simple_sub({ bar => 1, baz => "wq", boo => qr/12/ })
Equivalent to:
($_->{bar} == 1)
&& ($_->{baz} eq "wq")
&& ($_->{boo} =~ qr/12/)
simple_hash behaves identically.
ARRAY FORMS
Simple OR array
simple_sub(foo => [ 1, "wq", qr/12/ ])
Generates a logical OR over the values:
($_->{foo} == 1)
|| ($_->{foo} eq "wq")
|| ($_->{foo} =~ qr/12/)
Arrays of operator expressions
simple_array(foo => [ { '==', 2 }, { '>', 5 } ])
Generates:
($_->{foo} == 2) || ($_->{foo} > 5)
LOGICAL ARRAYS
Arrays may begin with a logical operator to control how elements are combined.
AND logic
simple_sub(foo => [ -and => { '==', 2 }, { '>=', 5 } ])
Generates:
($_->{foo} == 2) && ($_->{foo} >= 5)
OR logic
simple_sub(foo => [ -or => { '==', 2 }, { '>=', 5 } ])
Generates:
($_->{foo} == 2) || ($_->{foo} >= 5)
OPERATOR HASHES (FUNCTION HASHES)
A field value may be a hash mapping operators to values.
simple_sub(foo => { '>' => 12, '<' => 23 })
Generates:
($_->{foo} < 23) && ($_->{foo} > 12)
The order of operators is not significant.
Supported operators (as tested)
String operators
eq ne lt le gt geNumeric operators
== != < <= > >=Regex operators
=~ !~
OPERATORS WITH ARRAY VALUES
Equality with array
simple_sub(status => { eq => [ 'assigned', 'in-progress', 'pending' ] })
Generates an OR expression:
($_->{status} eq "assigned")
|| ($_->{status} eq "in-progress")
|| ($_->{status} eq "pending")
Equivalent logic may also be expressed via a logical array:
simple_sub(status => [
-or =>
{ eq => 'assigned' },
{ eq => 'in-progress' },
{ eq => 'pending' },
])
Regex operators with arrays
simple_sub(foo => { '=~' => [ qr/12/, qr/23/i ] })
Generates:
($_->{foo} =~ qr/12/)
|| ($_->{foo} =~ qr/23/i)
Non-regex values are coerced to regexes:
simple_sub(foo => { '=~' => [ "12", qr/23/i ] })
UNDEF HANDLING
simple_sub(user => undef)
Generates:
(! defined $_->{user})
Operator hashes may also compare against undef, though some combinations are known to be problematic (see tests marked as failing).
FIELD-TO-FIELD COMPARISON (SCALAR REF)
If the value is a scalar reference, it is interpreted as another field name.
simple_sub(foo => \"bar")
Generates a dynamic comparison:
( (looks_like_number($_->{foo}))
? ($_->{foo} == $_->{bar})
: ($_->{foo} eq $_->{bar})
)
This form may also appear inside logical arrays.
CODE REFERENCES
A code reference may be supplied as a filter.
simple_sub(sub { $_->{foo}->{bar} > 5 })
or
simple_sub(foo => sub { $_->{foo}->{bar} > 5 })
The code reference is deparsed and embedded verbatim:
(sub { use strict; $_->{'foo'}{'bar'} > 5; })
The field name is ignored when a code reference is supplied.
MIXED COMPLEX EXPRESSIONS
Arrays may contain mixed value types:
simple_sub(foo => [
1,
"wq",
qr/12/,
sub { shift()->{foo}->{bar} > 5 }
])
Generates a logical OR over all components.