Iterator tag framework compile method - Hiranyaloka/Documentation GitHub Wiki

The following (see here for a syntax highlighted version) is rough example code showing a possible implementation of iterating block template tag compilation function discussed on Proposal:Iterator template tag consistency

This has not been tested and does not yet contain plugin callback hookpoints.

    sub block_tag_iterator {
        my ($ctx, $args) = @_;

        # Initialize the iterator
        # Accepted values are a code reference or an array reference
        my $iter;
        if (! defined $args->{iterator}) {
            return $ctx->error('No iterator defined');
        }
        elsif (! ref $args->{iterator}) {
            return $ctx->error('Iterator must be an ARRAY or CODE reference');
        }
        elsif ('Data::ObjectDriver::Iterator' eq ref($args->{iterator})) {
            $iter = $args->{iterator};
        }
        elsif ('CODE' eq ref($args->{iterator})) {
            $iter = $args->{iterator};
        }
        elsif ('ARRAY' eq ref($args->{iterator})) {
            require Data::ObjectDriver;
            $iter = Data::ObjectDriver->list_or_iterator($args->{iterator});        
        }

        # Check for compilation hookpoints
        # Return error if defined and not code refs as something is wrong
        foreach my $hook (qw(prerun postrun skip)) {
            next if ! defined $args->{$hook} or 'CODE' eq ref($args->{$hook});
            return $ctx->error(
                sprintf 'Non-code reference in "%s" hookpoint "%s"',
                    $ctx->stash('tag'), $hook);
        }

        # Initialize the loop variables
        my $res     = '';                            # Cumulative output
        my $count   = 0;                             # Loop counter
        my $attr    = $args->{attributes} || {};     # Tag attributes
        my $glue    = $attr->{glue};                 # Glue for output
        my $vars    = $ctx->{__stash}{vars} ||= {};  # Template variables
        my $builder = $ctx->stash('builder');        # Builder objects
        my $tokens  = $ctx->stash('tokens');         # Current template tokens
        my $next    = $iter->();                     # Initialize first object

        # Start iterating over each object
        while ($next) {

            # Populate current and next object variables
            my $obj = $next;        
            $next = $iter->();

            # Run tag-specific skip test for conditional object processing
            next if $args->{skip}
                and $args->{skip}->($ctx, $args, $obj, $next);
        
            # Set template loop meta variables
            local $vars->{__counter__} = ++$count;
            local $vars->{__first__}   = $count == 1;
            local $vars->{__last__}    = !$next;  # ...why we needed next
            local $vars->{__odd__}     = ($count % 2) == 1;
            local $vars->{__even__}    = ($count % 2) == 0;

            # Run tag-specific pre-processing code
            # Return value is ignored, return on context error
            $ctx = $args->{prerun}->($ctx, $args, $obj, $next)
                if $args->{prerun};
            return if $ctx->errstr;

            # Build the code from the available context, passing in
            # conditional tests for subtags
            my $out =
                $builder->build($ctx, $tokens, $args->{condition});

            # Undefined return value indicates an error
            defined $out or return $ctx->error($builder->errstr);
    
            # Run tag-specific post-processing code
            # Output for current object is passed as a scalarref for changes
            # Return value is ignored, retrun on context error
            $ctx = $args->{postrun}->($ctx, $args, $obj, $next, \$out)
                if $args->{postrun};
            return if $ctx->errstr;

            # Add glue and output to cumulative results
            $res .= $glue if defined $glue && length($res) && length($out);
            $res .= $out if defined $out;
        }

         if (! $count) {
            return _hdlr_pass_tokens_else(
                $ctx, $attr, $args->{condition});
        }
        $res;
    }
⚠️ **GitHub.com Fallback** ⚠️