Data-Annotation

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

    Check if there is a chain definition whose name is $chain_nameo

  inflate_chains

       $da->inflate_chains;

    Make sure all input chain definitions are inflated, i.e. turned into
    "Data::Annotation::Chain" objects. This makes sure the definitions
    parse correctly.

  overlay_cloak

       my $overlay = $da->overlay_cloak($data, %opts);

    Shorthand to the constructor for "Data::Annotation::Overlay", passing
    $data as the under option and then the rest of %opts:

BUGS AND LIMITATIONS

    Minimul perl version 5.24.

    Report bugs through Codeberg (patches welcome) at
    https://codeberg.org/polettix/Data-Annotation.

lib/Data/Annotation.pm  view on Meta::CPAN

   return defined($name) && exists($self->chains->{$name});
}

sub chains_list ($self) { sort { $a cmp $b } keys($self->chains->%*) }

sub inflate_chains ($self) {
   $self->_chain_for($_) for $self->chains_list;
   return $self;
}

sub overlay_cloak ($self, $data, %opts) {
   return Data::Annotation::Overlay->new(under => $data, %opts);
}

sub evaluate ($self, $chain, $data) {
   $chain = $self->default_chain unless $self->has_chain_for($chain);

   # cloak the input $data with an Overlay, unless it's already an
   # overlay in which case it's used directly
   $data = $self->overlay_cloak($data,
      value_if_missing => '',
      value_if_undef   => '',
   ) unless blessed($data) && $data->isa('Data::Annotation::Overlay');

   my @call_sequence;

   my $wrapped = sub ($name) {
      my @stack;
      push @stack, { name => $name, state => {} }
         if $self->has_chain_for($name);

lib/Data/Annotation.pod  view on Meta::CPAN

Check if there is a chain definition whose name is C<< $chain_name >>o

=head3 B<< inflate_chains >>

   $da->inflate_chains;

Make sure all input chain definitions are I<inflated>, i.e. turned into
L</Data::Annotation::Chain> objects. This makes sure the definitions
parse correctly.

=head3 B<< overlay_cloak >>

   my $overlay = $da->overlay_cloak($data, %opts);

Shorthand to the constructor for L</Data::Annotation::Overlay>, passing
C<$data> as the C<under> option and then the rest of C<%opts>:

=head1 BUGS AND LIMITATIONS

Minimul perl version 5.24.

Report bugs through Codeberg (patches welcome) at
L<https://codeberg.org/polettix/Data-Annotation>.

lib/Data/Annotation/Chain.pm  view on Meta::CPAN

use Scalar::Util qw< blessed >;
use Data::Annotation::Rule;
use namespace::clean;

has description => (is => 'ro', default => '');
has default_retval => (is => 'ro', init_arg => 'default', predicate => 1);
has parse_context => (is => 'ro', default => sub { return {} },
   init_arg => 'condition-parse-context');
has rules => (is => 'ro', default => sub { [] });

sub evaluate ($self, $state, $overlay) {
   my $rules = $self->rules;
   my $ri = \($state->{idx} //= 0);
   while ($$ri <= $rules->$#*) {
      $rules->[$$ri] = Data::Annotation::Rule->new(
         'condition-parse-context' => $self->parse_context,
         $rules->[$$ri]->%*,
      ) unless blessed($rules->[$$ri]);
      my $rule = $rules->[$$ri++];
      if (defined(my $outcome = $rule->evaluate($overlay))) {
         my $name = $rule->has_name ? $rule->name : "#@{[ $$ri - 1 ]}";
         return ($outcome, $name);
      }
   }
   return $self->has_default_retval ? ($self->default_retval, undef) : ();
}

1;

lib/Data/Annotation/Chain.pod  view on Meta::CPAN

=head3 B<< rules >>

   my $aref = $chain->rules;

Get the list of rules configured for the chain. Thi

=head2 Methods

=head2 B<< evaluate >>

   my $result = $chain->evaluate($state, $overlay);

Evaluate the annotation for data wrapped in some C<$overlay>, leveraging
state in C<$state>.

=head1 ANYTHING ELSE (INCLUDING AUTHOR, COPYRIGHT AND LICENSE)

See documentation for L<Data::Annotation>.

=cut

lib/Data/Annotation/Expression.pm  view on Meta::CPAN

   $definition = $normalizer->($parse_ctx, $definition)
      if defined($normalizer);

   my $type = $definition->{type};
   my $parser = __PACKAGE__->can("generate_function_$type")
      or die "no parser for function type '$type'\n";
   return $parser->($parse_ctx, $definition);
}

sub generate_function_data ($parse_ctx, $definition) {
   return sub ($overlay) { return $definition->{value} };
}

sub generate_function_context ($parse_ctx, $definition) {
   my $path = $definition->{path} // '';
   my ($entry, @crumbs) = crumble($path)->@*;

   # the runtime context is a Data::Annotation::Overlay instance. For
   # 'run' we plug directly into it, otherwise we use its access options
   # for other data, but without the overlay/caching
   return sub ($overlay) { $overlay->get(\@crumbs) } if $entry eq 'run';
   my $other = { definition => $definition, parse => $parse_ctx };
   return sub ($overlay) {
      return $overlay->get_external([$entry, @crumbs], $other);
   };
}

sub generate_function_sub ($parse_ctx, $definition) {
   my ($name, $package) = $definition->@{qw< name package >};
   my $function = resolve_function($parse_ctx, $name, $package);
   my @args = map { generate_function($parse_ctx, $_) }
      ($definition->{args} // [])->@*;
   return sub ($overlay) { $function->(map { $_->($overlay) } @args) };
}

sub resolve_function ($parse_ctx, $name, $package) {
   die "undefined sub name\n" unless defined($name);
   die "empty sub name\n"     unless length($name);

   my $suffix = $package //= '';
   my $is_absolute = $suffix =~ s{\A /}{}mxs;
   my $relative_prefixes = $parse_ctx->{'locator-relative-prefixes'};
   my @prefixes = $is_absolute ? ('') : (($relative_prefixes // [])->@*);

lib/Data/Annotation/Overlay.pm  view on Meta::CPAN

has value_if_undef   => (is => 'ro', predicate => 1);
has cache_existing   => (is => 'ro', default => 1);

sub delete ($self, $path) { $self->set($path, MISSING) }

sub get ($self, $path) {
   ouch 400, 'cannot get an undefined path' unless defined($path);
   my $crumbs = crumble($path);
   my $kpath  = kpath($crumbs);

   # retrieve item, first look in the overlay, then go down
   my $retval;
   my $over = $self->over;
   my $under = $self->under;
   my $under_class = blessed($under);
   if (exists($over->{$kpath})) {
      $retval = $over->{$kpath};
   }
   elsif (blessed($under) && $under->isa(__PACKAGE__)) {
      $retval = $under->get($path); # get from previous layer in stack
   }

lib/Data/Annotation/Overlay.pod  view on Meta::CPAN

=head1 NAME

Data::Annotation::Overlay


=head1 SYNOPSIS

   use Data::Annotation::Overlay;

   my %data = (...);
   my $overlay = Data::Annotation::Overlay->new(under => \%data);


=head1 DESCRIPTION

Wrapper around a data structure, useful to record "changes" to the data
structure without making actual modifications to it. Loosely inspired by
the I<overalay> file systems.

=head1 INTERFACE

=head2 Constructor

   my $rule = Data::Annotation::Overlay->(%opts);

Make a new instance for an overlay. Accepted options are all
L</Accessors>.

Input parameter C<under> is the only required key, representing the data
structure that the overlay wraps around.

All I<paths> used in this module must be compatible with
L</Data::Annotation::Traverse::crumble> and will be normalized with
L</Data::Annotation::Traverse::kpath>.

=head2 Accessors

=head3 B<< cache_existing >>

All values retrieved with L</get> will be cached for quicker access
later on. Set by default. Disable if there are intermediate function
calls that need to be executed at every access.

=head3 B<< method_over_key >>

Prefer the method call over key access when there is an intermediate
blessed object while traversing.

=head3 B<< over >>

Reference to the I<overlay> data structure (a hash reference).

=head3 B<< strict_blessed >>

Only consider method calls on blessed objects while traversing. False by
default.

=head3 B<< traverse_methods >>

Consider using methods when traversing an object. True by default.

lib/Data/Annotation/Overlay.pod  view on Meta::CPAN

   my $value = $ov->get_external($path, $data);

Shorthand to apply the same traversing algorithm (with the same options)
in traversing C<$data> with C<$path>. In particular, it also uses
L</return_value_for> to cope with missing/undefined elements.

=head3 B<< merged >>

   my $thin_ov = $stacked_ov->merged;

Generate a new overlay from a stack of multiple wrapped overlays. The
L</under> of this new overlay is the same as the one for the bottom
overlay of the whole stack, as well as traversal options; L</over> is
generated anew and independent of what's in the stacked overlay;
L</value_if_missing> and L</value_if_undef> are taken from the top
overlay, and L<cache_existing> is taken from any layer.

All in all, it aims at being a close representation of the behaviour as
seen from the top layer of the C<$stacked_ov>.

=head3 B<< return_value_for >>

   my $value = $ov->return_value_for($retval);

Adapt the return value according to the configuration.

lib/Data/Annotation/Rule.pm  view on Meta::CPAN

has condition => (is => 'ro', default => 1);
has _condition => (is => 'lazy');

sub _build__condition ($self) {
   my $condition = $self->condition;
   return evaluator_factory($condition, $self->parse_context // {})
      if ref($condition) eq 'HASH';
   return sub { $condition };
}

sub evaluate ($self, $overlay) {
   return unless $self->_condition->($overlay);
   if ($self->has_record) {
      my $record = $self->record;
      if (exists($record->{delete})) {
         for my $skey ($record->{delete}->@*) {
            my $key = $skey =~ s{\A \.?}{}rmxs;
            $overlay->delete($key);
         }
      }
      if (exists($record->{set})) {
         my $set = $record->{set};
         for my $skey (keys($set->%*)) {
            my $key = $skey =~ s{\A \.?}{}rmxs;
            $overlay->set($key, $set->{$skey});
         }
      }
   }
   return $self->retval if $self->has_retval;
   return;
}

1;

lib/Data/Annotation/Rule.pod  view on Meta::CPAN

   my $ctx = $rule->parse_context;

Whatever was passed as argument C<condition-parse-context>, which can
help set the stage for condition parsing. This should not be generally
needed, but still. See L<Data::Annotation::Expression> for more details.

=head3 B<< record >>

   my $defs = $rule->record;

A data structure to drive changes in the C<$overlay> passed to
L</evaluate> in case the condition applies. This allows recording some
data that can be later used by other chains/rules during following
evaluations.

Accepts a hash reference with the following keys:

=over

=item *

C<delete>: an array reference of I<paths> suitable for
L</Data::Annotation::Traverse::traverse_plain>, these paths will be
marked as deleted in the overlay.

=item *

C<set>: a hash reference of I<paths>/values to be set in the overlay.

=back

All paths are supposed to be relative to an initial key C<run>, which is
mapped onto the C<$overlay> in L</execute>.

As an example, let's consider the following chains definitions:

   # ...
   EHLO:
     default: reject
     rules:
       - condition:
           and:
             - eq: [ '.peer_ip', '=127.0.0.1' ]

lib/Data/Annotation/Rule.pod  view on Meta::CPAN

The result (annotation) value returned by L</evaluate> when the
condition of the rule applies.

Initialized by key C<return> in the constructor.


=head2 Methods

=head3 B<< evaluate >>

   my $result = $rule->evaluate($overlay);

Evaluate the annotation for data wrapped in some C<$overlay>.


=head1 ANYTHING ELSE (INCLUDING AUTHOR, COPYRIGHT AND LICENSE)

See documentation for Data::Annotation.


=cut

lib/Data/Annotation/Util.pm  view on Meta::CPAN

package Data::Annotation::Util;
use v5.24;
use experimental qw< signatures >;
use Data::Annotation::Overlay;
use Exporter qw< import >;
our @EXPORT_OK = qw< o overlay >;

sub o { Data::Annotation::Overlay->new(under => @_) }

*{overlay} = \&o;

1;

lib/Data/Annotation/Util.pod  view on Meta::CPAN


=encoding utf8

=head1 NAME

Data::Annotation::Util


=head1 SYNOPSIS

   use Data::Annotation::Util qw< o overlay >;

   my $ov = o(\%data);  # same as overlay(\%data);

=head1 DESCRIPTION

Utility functions.


=head1 INTERFACE

=head2 C<< o >>

Same as L</overlay>.

=head2 C<< overlay >>

   my $o = overlay(\%data_structure);

Instantiate a L<Data::Annotation::Overlay> taking the provided
C<%data_structure> for the underlay. It's also possible to pass additional
parameters that will be passed on to the constructor.

=head1 ANYTHING ELSE (INCLUDING AUTHOR, COPYRIGHT AND LICENSE)

See documentation for L<Data::Annotation>.

=cut



( run in 0.628 second using v1.01-cache-2.11-cpan-49f99fa48dc )