The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

App::Rssfilter::Rule - match and filter RSS feeds

VERSION

version 0.07

SYNOPSIS

    use App::RssFilter::Rule;

    use Mojo::DOM;
    my $rss = Mojo::DOM->new( 'an RSS document' );

    my $delete_duplicates_rule = App::Rssfilter::Rule->new( Duplicate => 'DeleteItem' );

    # shorthand for
    $delete_duplicates_rule = App::Rssfilter::Rule->new(
        condition => 'App::Rssfilter::Match::Duplicate',
        action    => 'App::Rssfilter::Filter::DeleteItem',
    );

    # apply rule to RSS document
    $delete_duplicates_rule->constrain( $rss );

    # write modules with match and filter subs

    package MyMatcher::LevelOfInterest;
    
    sub new {
        my ( $class, @bracketed_args) = @_;
        if ( grep { $_ eq 'BORING' } @bracketed_args ) {
            # turn on boredom detection circuits
            ...
        }
        ...
    }
    
    sub match {
        my ( $self, $mojo_dom_rss_item ) = @_;
        ...
    }

    package MyFilter::MakeMoreInteresting;

    sub filter {
        my ( $reason_for_match,
             $matched_mojo_dom_rss_item,
             @bracketed_args ) = @_;
        ...
    }

    package main;

    my $boring_made_interesting_rule = App::Rssfilter::Rule->new( 
        'MyMatcher::LevelOfInterest[BORING]'
            => 'MyFilter::MakeMoreInteresting[glitter,lasers]'
    );
    $boring_made_interesting_rule->constrain( $rss );

    my $interesting_with_decoration_rule = App::Rssfilter::Rule->new( 
        condition      => MyMatcher::LevelOfInterest->new('OUT_OF_SIGHT'),
        condition_name => 'ReallyInteresting', # instead of plain 'MyMatcher::LevelOfInterest'
        action         => 'MyFilter::MakeMoreInteresting[ascii_art]',
    );
    $interesting_with_decoration_rule->constrain( $rss );

    # or use anonymous subs
    my $space_the_final_frontier_rule = App::Rssfilter:Rule->new(
        condition => sub {
            my ( $item_to_match ) = @_;
            return $item_to_match->title->text =~ / \b space \b /ixms;
        },
        action => sub {
            my ( $reason_for_match, $matched_item ) = @_;
            my @to_check = ( $matched_item->tree );
            my %seen;
            while( my $elem = pop @to_check ) {
                next if 'ARRAY' ne ref $elem or $seen{ $elem }++;
                if( $elem->[0] eq 'text' ) {
                    $elem->[1] =~ s/ \b space \b /\& (the final frontier)/xmsig;
                }
                else
                {
                    push @to_check, @{ $elem };
                }
            }
        },
    );
    $space_the_final_frontier_rule->constrain( $rss );

    ### or with an App::Rssfilter feed or group

    use App::RssFilter::Feed;
    my $feed = App::RssFilter::Feed->new( 'examples' => 'http://example.org/e.g.rss' );
    $feed->add_rule( 'My::Matcher' => 'My::Filter' );
    # same as
    $feed->add_rule( App::Rssfilter::Rule->new( 'My::Matcher' => 'My::Filter' ) );
    $feed->update;

DESCRIPTION

This module will test all item elements in a Mojo::DOM object against a condition, and apply an action on items where the condition is true.

It consumes the App::Rssfilter::Logger role.

ATTRIBUTES

logger

This is a object used for logging; it defaults to a Log::Any object. It is provided by the App::Rssfilter::Logger role.

condition

This is the module, object, or coderef to use to match item elements for filtering. Modules are passed as strings, and must contain a match sub. Object must have a match method.

_match

This is a coderef created from this rule's condition which will be used by "match" to check RSS items. It is automatically coerced from the condition attribute and cannot be passed to the constructor.

If this rule's condition is an object, _match will store a wrapper which calls the match method of the object. If this rule's condition is a subref, _match will store the same subref.

If this rule's condition is a string, it is treated as a namespace. If the string is not a fully-specified namespace, it will be changed to App::Rssfilter::Match::<string>; if you really want to use &TopLevelNamespace::match, specify condition as '::TopLevelNamespace' (or directly as \&TopLevelNameSpace::match). Additional arguments can be passed to the matcher by appending then to the string, separated by commas, surrounded by square brackets.

_match will then be set to a wrapper:

  • If <namespace>::new exists, _match will be set as if condition had originally been the object returned from calling <namespace>::new( @additional_arguments ).

  • Otherwise, _match will store a wrapper which calls <namespace>::match( $rss_item, @additional_arguments ).

condition_name

This is a nice name for the condition, which will be used as the reason for the match given to the action. Defaults to the class of the condition, or its value if it is a simple scalar, or unnamed RSS matcher otherwise.

action

This is the module, object, or coderef to use to filter item elements matched by this rule's condition. Modules are passed as strings, and must contain a filter sub. Object must have a filter method.

_filter

This is a coderef created from this rule's action which will be used by "filter" to check RSS items. It is automatically coerced from the action attribute and cannot be passed to the constructor.

If this rule's action is an object, _filter will store a wrapper which calls the filter method of the object. If this rule's action is a subref, _filter will store the same subref.

If the rule's action is a string, it is treated as a namespace. If the string is not a fully-specified namespace, it will be changed to App::Rssfilter::filter::<string>; if you really want to use &TopLevelNamespace::filter, specify action as '::TopLevelNamespace' (or directly as \&TopLevelNameSpace::filter). Additional arguments can be passed to the filter by appending then to the string, separated by commas, surrounded by square brackets.

The filter will then be set to a wrapper:

  • If <namespace>::new exists, _filter will be set as if action had originally been the object returned from calling <namespace>::new( @additional_arguments ).

  • Otherwise, _filter will store a wrapper which calls <namespace>::filter( $rss_item, @additional_arguments ).

action_name

This is a nice name for the action. Defaults to the class of the action, or its value if it is a simple scalar, or unnamed RSS filter otherwise.

METHODS

match

    my $did_match = $self->match( $item_element_from_Mojo_DOM );

Returns the result of testing this rule's condition against $item.

filter

    $self->filter( $item_element_from_Mojo_DOM );

Applies this rule's action to $item.

constrain

    my $count_of_filtered_items = $rule->constrain( $Mojo_DOM );

Gathers all child item elements of $Mojo_DOM for which the condition is true, and applies the action to each. Returns the number of items that were matched (and filtered).

SEE ALSO

AUTHOR

Daniel Holz <dgholz@gmail.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Daniel Holz.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.