File Coverage

blib/lib/String/FlexMatch.pm
Criterion Covered Total %
statement 39 54 72.2
branch 19 30 63.3
condition 4 8 50.0
subroutine 15 27 55.5
pod 13 13 100.0
total 90 132 68.1


line stmt bran cond sub pod time code
1 2     2   35 use 5.008;
  2         7  
  2         77  
2 2     2   11 use strict;
  2         3  
  2         55  
3 2     2   10 use warnings;
  2         4  
  2         90  
4              
5             package String::FlexMatch;
6             our $VERSION = '1.100820';
7             # ABSTRACT: Flexible ways to match a string
8 2     2   1909 use parent 'Class::Accessor::Complex';
  2         584  
  2         10  
9             __PACKAGE__->mk_new;
10              
11             # Back in Test::More 0.45 the sane view was taken that if an object overrides
12             # stringification, it probably does so for a reason, and that stringification
13             # defines how the object wants to be compared. Newer versions of Test::More
14             # simply say that if you have a string and a reference, they can't possibly be
15             # the same, effectively overriding overload. This is completely fucked up, and
16             # we override it here again.
17             #
18             # You might say that's an evil hack and I might say I don't care. If you use
19             # String::FlexMatch you subscribe to my point of view.
20             #require Test::Builder;
21             #no warnings 'redefine';
22             #*Test::Builder::_unoverload = sub {};
23             use overload
24 2         58 '""' => \&as_string,
25             'eq' => \&is_eq,
26             'ne' => \&is_ne,
27 2     2   55414 '==' => \&is_eq;
  2         8  
28 0     0 1 0 sub init { } # so potential subclasses can override
29              
30             sub string {
31 17     17 1 19 my $self = shift;
32 17 50       88 @_ ? $self->{string} = shift : $self->{string};
33             }
34              
35             sub force_regex {
36 25 100   25 1 62 return unless defined $_[1];
37 20 50       192 ref $_[1] eq 'Regexp' ? $_[1] : qr/$_[1]/;
38             }
39              
40             sub regex {
41 25     25 1 28 my $self = shift;
42             @_
43 25 50       82 ? $self->{regex} = $self->force_regex(+shift)
44             : $self->force_regex($self->{regex});
45             }
46              
47             sub force_code {
48 10 50   10 1 19 return unless defined $_[1];
49 10 50       587 ref $_[1] eq 'CODE' ? $_[1] : eval $_[1];
50             }
51              
52             sub code {
53 10     10 1 13 my $self = shift;
54             @_
55 10 50       29 ? $self->{code} = $self->force_code(+shift)
56             : $self->force_code($self->{code});
57             }
58 0     0 1 0 sub as_string { $_[0]->choice_attr }
59              
60             sub is_eq {
61 16     16 1 25 my ($lhs, $rhs) = @_;
62              
63             # only 'undef' matches 'undef'; if one side is undef and the other is not,
64             # there's no match
65 16 50       34 return !defined $rhs unless defined $lhs;
66 16 50       31 return !defined $lhs unless defined $rhs;
67 16 50 33     146 my $lhs_val =
68             ref($lhs) && $lhs->isa('String::FlexMatch') ? $lhs->choice_attr : "$lhs";
69 16 50 33     97 my $rhs_val =
70             ref($rhs) && $rhs->isa('String::FlexMatch') ? $rhs->choice_attr : "$rhs";
71 16 100       28 my $key = sprintf "%s_%s", map { ref || 'STRING' } $lhs_val, $rhs_val;
  32         157  
72             our $match ||= {
73 1     1   13 STRING_STRING => sub { $_[0] eq $_[1] },
74 0     0   0 STRING_Regexp => sub { $_[0] =~ $_[1] },
75 0     0   0 STRING_CODE => sub { $_[1]->($_[0]) },
76 10     10   113 Regexp_STRING => sub { $_[1] =~ $_[0] },
77 0     0   0 Regexp_Regexp => sub { die "can't compare two regexes" },
78 0     0   0 Regexp_CODE => sub { die "can't compare a regex to a string" },
79 5     5   120 CODE_STRING => sub { $_[0]->($_[1]) },
80 0     0   0 CODE_Regexp => sub { die "can't compare a coderef to a regex" },
81 0     0   0 CODE_CODE => sub { die "can't compare two coderefs" },
82 16   100     98 };
83 16         37 $match->{$key}->($lhs_val, $rhs_val);
84             }
85 0     0 1 0 sub is_ne { !is_eq(@_) }
86              
87             sub choice_attr {
88 16     16 1 20 my $self = shift;
89 16 50       36 defined $self->string ? $self->string
    100          
    100          
90             : defined $self->regex ? $self->regex
91             : defined $self->code ? $self->code
92             : undef;
93             }
94              
95             # If this module is used with YAML::Active, we want it to dump as a
96             # String::Flex::NoOverload object. If this sub wasn't there, YAML would
97             # stringify the String::FlexMatch object, which would produce a normal string
98             # (cf. as_string() - something like '(?-xism:blah)'. However, we wouldn't be
99             # able to re-Load this dump via YAML::Active again, since the string, when
100             # loaded, would just stay a normal string and not turn into a
101             # String::FlexMatch object again.
102             #
103             # To remedy this, we provide this sub to tell YAML::Active how we want a
104             # String::FlexMatch object dumped: as a String::FlexMatch::NoOverload object,
105             # which can then be given to YAML to dump - it will produce something like
106             #
107             # !perl/String::FlexMatch::NoOverload regex: ...
108             #
109             # The last piece of the puzzle is to make String::FlexMatch::NoOverload
110             # inherit from String::FlexMatch. That way, when re-Loading the above YAML,
111             # the expected behaviour of the flex string still works.
112 0     0 1   sub prepare_dump { @String::FlexMatch::NoOverload::ISA = () }
113 0     0 1   sub finish_dump { @String::FlexMatch::NoOverload::ISA = 'String::FlexMatch' }
114              
115             sub yaml_dump {
116 0     0 1   my $self = shift;
117 0           my $dump_self;
118 0           %$dump_self = %$self;
119 0           bless $dump_self, 'String::FlexMatch::NoOverload';
120             }
121             @String::FlexMatch::NoOverload::ISA = 'String::FlexMatch';
122             1;
123              
124              
125             __END__