File Coverage

lib/Data/Hopen/Util/NameSet.pm
Criterion Covered Total %
statement 42 42 100.0
branch 26 26 100.0
condition n/a
subroutine 10 10 100.0
pod 6 6 100.0
total 84 84 100.0


line stmt bran cond sub pod time code
1             # Data::Hopen::Util::NameSet - set of strings and regexps
2             package Data::Hopen::Util::NameSet;
3 23     23   5244 use strict;
  23         46  
  23         2722  
4 23     23   1659 use Data::Hopen::Base;
  23         1990  
  23         158  
5              
6             our $VERSION = '0.000021';
7              
8             # Docs {{{1
9              
10             =head1 NAME
11              
12             Data::Hopen::Util::NameSet - set of names (strings or regexps)
13              
14             =head1 SYNOPSIS
15              
16             NameSet stores strings and regexps, and can quickly tell you whether
17             a given string matches one of the stored strings or regexps.
18              
19             =cut
20              
21             # }}}1
22              
23             =head1 FUNCTIONS
24              
25             =head2 new
26              
27             Create a new instance. Usage: C<< Data::Hopen::Util::Nameset->new(...) >>.
28             The parameters are as L.
29              
30             =cut
31              
32             sub new {
33 53 100   53 1 212702 my $class = shift or croak 'Call as ' . __PACKAGE__ . '->new(...)';
34 52         226 my $self = bless { _strings => [], _regexps => [], _RE => undef }, $class;
35 52 100       242 $self->add(@_) if @_;
36 51         147 return $self;
37             } #new()
38              
39             =head2 add
40              
41             Add one or more strings or regexps to the NameSet. Usage:
42              
43             $instance->add(x1, x2, ...)
44              
45             where each C can be a scalar, regexp, arrayref (processed recursively)
46             or hashref (the keys are added and the values are ignored).
47              
48             =cut
49              
50             sub add {
51 40 100   40 1 2111 my $self = shift or croak 'Need an instance';
52 39 100       192 return unless @_;
53 38         138 $self->{_RE} = undef; # dirty the instance
54              
55 38         127 foreach my $arg (@_) {
56 61 100       250 if(!ref $arg) {
    100          
    100          
    100          
57 24         39 push @{$self->{_strings}}, "$arg";
  24         100  
58             } elsif(ref $arg eq 'Regexp') {
59 29         72 push @{$self->{_regexps}}, $arg;
  29         122  
60             } elsif(ref $arg eq 'ARRAY') {
61 4         18 $self->add(@$arg);
62             } elsif(ref $arg eq 'HASH') {
63 3         14 $self->add(keys %$arg);
64             } else {
65 23     23   14049 use Data::Dumper;
  23         51  
  23         12458  
66 1         8 croak "I don't know how to handle this: " . Dumper($arg)
67             }
68             }
69             } #add()
70              
71             =head2 contains
72              
73             Return truthy if the NameSet contains the argument. Usage:
74             C<< $set->contains('foo') >>.
75              
76             =cut
77              
78             sub contains {
79 74 100   74 1 27228 my $self = shift or croak 'Need an instance';
80 73 100       249 $self->{_RE} = $self->_build unless $self->{_RE}; # Clean
81             #say STDERR $self->{_RE};
82 73         1084 return shift =~ $self->{_RE};
83             } #contains()
84              
85             =head2 strings
86              
87             Accessor for the strings in the set. Returns an arrayref.
88              
89             =cut
90              
91 2     2 1 532 sub strings { (shift)->{_strings} }
92              
93             =head2 regexps
94              
95             Accessor for the regexps in the set. Returns an arrayref.
96              
97             =cut
98              
99 2     2 1 16 sub regexps { (shift)->{_regexps} }
100              
101             =head2 complex
102              
103             Returns truthy if the nameset has any regular expressions.
104              
105             =cut
106              
107 4     4 1 22 sub complex { @{(shift)->{_regexps}} > 0 }
  4         26  
108              
109             =head2 _build
110              
111             (Internal) Build a regex from all the strings and regexps in the set.
112             Returns the new regexp --- does not mutate $self.
113              
114             In the current implementation, strings are matched case-sensitively.
115             Regexps are matched with whatever flags they were compiled with.
116              
117             =cut
118              
119             sub _build {
120 8 100   8   1019 my $self = shift or croak 'Need an instance';
121              
122 7         13 my @quoted_strs;
123 7 100       10 if(@{$self->{_strings}}) {
  7         23  
124             push @quoted_strs,
125 3         11 join '|', map { quotemeta } @{$self->{_strings}};
  18         52  
  3         10  
126             # TODO should I be using qr/\Q$_\E/ instead, since quotemeta
127             # isn't quite right on 5.14? Or should I be using 5.16+?
128             }
129              
130 7         15 my $pattern = join '|', @{$self->{_regexps}}, @quoted_strs;
  7         23  
131             # Each regexp stringifies with surrounding parens, so we
132             # don't need to add any.
133              
134 7 100       249 return $pattern ? qr/\A(?:$pattern)\z/ : qr/(*FAIL)/;
135             # If $pattern is empty, the nameset is empty (`(*FAIL)`). Without the
136             # ?:, qr// would match anything, when we want to match nothing.
137             } #_build()
138              
139             1;
140             __END__